add within/without messages

This commit is contained in:
David Rose 2001-10-24 20:39:42 +00:00
parent 0ff966d799
commit 7c00e5b6ce
10 changed files with 628 additions and 127 deletions

View File

@ -237,6 +237,30 @@ get_exit_prefix() {
return "exit-";
}
////////////////////////////////////////////////////////////////////
// Function: PGItem::get_within_prefix
// Access: Published, Static
// Description: Returns the prefix that is used to define the within
// event for all PGItems. The within event is the
// concatenation of this string followed by get_id().
////////////////////////////////////////////////////////////////////
INLINE string PGItem::
get_within_prefix() {
return "within-";
}
////////////////////////////////////////////////////////////////////
// Function: PGItem::get_without_prefix
// Access: Published, Static
// Description: Returns the prefix that is used to define the without
// event for all PGItems. The without event is the
// concatenation of this string followed by get_id().
////////////////////////////////////////////////////////////////////
INLINE string PGItem::
get_without_prefix() {
return "without-";
}
////////////////////////////////////////////////////////////////////
// Function: PGItem::get_focus_in_prefix
// Access: Published, Static
@ -297,7 +321,8 @@ get_release_prefix() {
// Function: PGItem::get_enter_event
// Access: Published
// Description: Returns the event name that will be thrown when the
// item is active and the mouse enters its frame.
// item is active and the mouse enters its frame, but
// not any nested frames.
////////////////////////////////////////////////////////////////////
INLINE string PGItem::
get_enter_event() const {
@ -308,13 +333,43 @@ get_enter_event() const {
// Function: PGItem::get_exit_event
// Access: Published
// Description: Returns the event name that will be thrown when the
// item is active and the mouse exits its frame.
// item is active and the mouse exits its frame, or
// enters a nested frame.
////////////////////////////////////////////////////////////////////
INLINE string PGItem::
get_exit_event() const {
return get_exit_prefix() + get_id();
}
////////////////////////////////////////////////////////////////////
// Function: PGItem::get_within_event
// Access: Published
// Description: Returns the event name that will be thrown when the
// item is active and the mouse moves within the
// boundaries of the frame. This is different from the
// enter_event in that the mouse is considered within
// the frame even if it is also within a nested frame.
////////////////////////////////////////////////////////////////////
INLINE string PGItem::
get_within_event() const {
return get_within_prefix() + get_id();
}
////////////////////////////////////////////////////////////////////
// Function: PGItem::get_without_event
// Access: Published
// Description: Returns the event name that will be thrown when the
// item is active and the mouse moves completely outside
// the boundaries of the frame. This is different from
// the exit_event in that the mouse is considered
// within the frame even if it is also within a nested
// frame.
////////////////////////////////////////////////////////////////////
INLINE string PGItem::
get_without_event() const {
return get_without_prefix() + get_id();
}
////////////////////////////////////////////////////////////////////
// Function: PGItem::get_focus_in_event
// Access: Published

View File

@ -226,7 +226,10 @@ draw_item(PGTop *top, GraphicsStateGuardian *gsg,
// Function: PGItem::enter
// Access: Public, Virtual
// Description: This is a callback hook function, called whenever the
// mouse enters the region.
// mouse enters the region. The mouse is only
// considered to be "entered" in one region at a time;
// in the case of nested regions, it exits the outer
// region before entering the inner one.
////////////////////////////////////////////////////////////////////
void PGItem::
enter(const MouseWatcherParameter &param) {
@ -240,7 +243,10 @@ enter(const MouseWatcherParameter &param) {
// Function: PGItem::exit
// Access: Public, Virtual
// Description: This is a callback hook function, called whenever the
// mouse exits the region.
// mouse exits the region. The mouse is only considered
// to be "entered" in one region at a time; in the case
// of nested regions, it exits the outer region before
// entering the inner one.
////////////////////////////////////////////////////////////////////
void PGItem::
exit(const MouseWatcherParameter &param) {
@ -250,6 +256,39 @@ exit(const MouseWatcherParameter &param) {
throw_event(event, EventParameter(ep));
}
////////////////////////////////////////////////////////////////////
// Function: PGItem::within
// Access: Public, Virtual
// Description: This is a callback hook function, called whenever the
// mouse moves within the boundaries of the region, even
// if it is also within the boundaries of a nested
// region. This is different from "enter", which is
// only called whenever the mouse is within only that
// region.
////////////////////////////////////////////////////////////////////
void PGItem::
within(const MouseWatcherParameter &param) {
PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param);
string event = get_within_event();
play_sound(event);
throw_event(event, EventParameter(ep));
}
////////////////////////////////////////////////////////////////////
// Function: PGItem::without
// Access: Public, Virtual
// Description: This is a callback hook function, called whenever the
// mouse moves completely outside the boundaries of the
// region. See within().
////////////////////////////////////////////////////////////////////
void PGItem::
without(const MouseWatcherParameter &param) {
PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param);
string event = get_without_event();
play_sound(event);
throw_event(event, EventParameter(ep));
}
////////////////////////////////////////////////////////////////////
// Function: PGItem::focus_in
// Access: Public, Virtual

View File

@ -75,6 +75,8 @@ public:
virtual void enter(const MouseWatcherParameter &param);
virtual void exit(const MouseWatcherParameter &param);
virtual void within(const MouseWatcherParameter &param);
virtual void without(const MouseWatcherParameter &param);
virtual void focus_in();
virtual void focus_out();
virtual void press(const MouseWatcherParameter &param, bool background);
@ -119,6 +121,8 @@ PUBLISHED:
INLINE static string get_enter_prefix();
INLINE static string get_exit_prefix();
INLINE static string get_within_prefix();
INLINE static string get_without_prefix();
INLINE static string get_focus_in_prefix();
INLINE static string get_focus_out_prefix();
INLINE static string get_press_prefix();
@ -126,6 +130,8 @@ PUBLISHED:
INLINE string get_enter_event() const;
INLINE string get_exit_event() const;
INLINE string get_within_event() const;
INLINE string get_without_event() const;
INLINE string get_focus_in_event() const;
INLINE string get_focus_out_event() const;
INLINE string get_press_event(const ButtonHandle &button) const;

View File

@ -52,7 +52,10 @@ PGMouseWatcherRegion::
// Function: PGMouseWatcherRegion::enter
// Access: Public, Virtual
// Description: This is a callback hook function, called whenever the
// mouse enters the region.
// mouse enters the region. The mouse is only
// considered to be "entered" in one region at a time;
// in the case of nested regions, it exits the outer
// region before entering the inner one.
////////////////////////////////////////////////////////////////////
void PGMouseWatcherRegion::
enter(const MouseWatcherParameter &param) {
@ -65,7 +68,10 @@ enter(const MouseWatcherParameter &param) {
// Function: PGMouseWatcherRegion::exit
// Access: Public, Virtual
// Description: This is a callback hook function, called whenever the
// mouse exits the region.
// mouse exits the region. The mouse is only considered
// to be "entered" in one region at a time; in the case
// of nested regions, it exits the outer region before
// entering the inner one.
////////////////////////////////////////////////////////////////////
void PGMouseWatcherRegion::
exit(const MouseWatcherParameter &param) {
@ -74,6 +80,37 @@ exit(const MouseWatcherParameter &param) {
}
}
////////////////////////////////////////////////////////////////////
// Function: PGMouseWatcherRegion::within
// Access: Public, Virtual
// Description: This is a callback hook function, called whenever the
// mouse moves within the boundaries of the region, even
// if it is also within the boundaries of a nested
// region. This is different from "enter", which is
// only called whenever the mouse is within only that
// region.
////////////////////////////////////////////////////////////////////
void PGMouseWatcherRegion::
within(const MouseWatcherParameter &param) {
if (_item != (PGItem *)NULL) {
_item->within(param);
}
}
////////////////////////////////////////////////////////////////////
// Function: PGMouseWatcherRegion::without
// Access: Public, Virtual
// Description: This is a callback hook function, called whenever the
// mouse moves completely outside the boundaries of the
// region. See within().
////////////////////////////////////////////////////////////////////
void PGMouseWatcherRegion::
without(const MouseWatcherParameter &param) {
if (_item != (PGItem *)NULL) {
_item->without(param);
}
}
////////////////////////////////////////////////////////////////////
// Function: PGMouseWatcherRegion::press
// Access: Public, Virtual

View File

@ -39,6 +39,8 @@ public:
virtual void enter(const MouseWatcherParameter &param);
virtual void exit(const MouseWatcherParameter &param);
virtual void within(const MouseWatcherParameter &param);
virtual void without(const MouseWatcherParameter &param);
virtual void press(const MouseWatcherParameter &param);
virtual void release(const MouseWatcherParameter &param);

View File

@ -124,7 +124,7 @@ is_over_region(const LPoint2f &pos) const {
////////////////////////////////////////////////////////////////////
INLINE MouseWatcherRegion *MouseWatcher::
get_over_region() const {
return _current_region;
return _preferred_region;
}
////////////////////////////////////////////////////////////////////
@ -197,8 +197,11 @@ get_button_up_pattern() const {
// Function: MouseWatcher::set_enter_pattern
// Access: Published
// Description: Sets the pattern string that indicates how the event
// names are generated when the mouse wanders over a
// region. See set_button_down_pattern().
// names are generated when the mouse enters a region.
// This is different from within_pattern, in that a
// mouse is only "entered" in the topmost region at a
// given time, while it might be "within" multiple
// nested regions.
////////////////////////////////////////////////////////////////////
INLINE void MouseWatcher::
set_enter_pattern(const string &pattern) {
@ -209,8 +212,10 @@ set_enter_pattern(const string &pattern) {
// Function: MouseWatcher::get_enter_pattern
// Access: Published
// Description: Returns the string that indicates how event names are
// generated when the mouse wanders over a region. See
// set_button_down_pattern().
// generated when the mouse enters a region. This is
// different from within_pattern, in that a mouse is
// only "entered" in the topmost region at a given time,
// while it might be "within" multiple nested regions.
////////////////////////////////////////////////////////////////////
INLINE const string &MouseWatcher::
get_enter_pattern() const {
@ -222,7 +227,10 @@ get_enter_pattern() const {
// Access: Published
// Description: Sets the pattern string that indicates how the event
// names are generated when the mouse leaves a region.
// See set_button_down_pattern().
// This is different from without_pattern, in that a
// mouse is only "entered" in the topmost region at a
// given time, while it might be "within" multiple
// nested regions.
////////////////////////////////////////////////////////////////////
INLINE void MouseWatcher::
set_leave_pattern(const string &pattern) {
@ -233,14 +241,75 @@ set_leave_pattern(const string &pattern) {
// Function: MouseWatcher::get_leave_pattern
// Access: Published
// Description: Returns the string that indicates how event names are
// generated when the mouse leaves a region. See
// set_button_down_pattern().
// generated when the mouse leaves a region. This is
// different from without_pattern, in that a mouse is
// only "entered" in the topmost region at a given time,
// while it might be "within" multiple nested regions.
////////////////////////////////////////////////////////////////////
INLINE const string &MouseWatcher::
get_leave_pattern() const {
return _leave_pattern;
}
////////////////////////////////////////////////////////////////////
// Function: MouseWatcher::set_within_pattern
// Access: Published
// Description: Sets the pattern string that indicates how the event
// names are generated when the mouse wanders over a
// region. This is different from enter_pattern, in
// that a mouse is only "entered" in the topmost region
// at a given time, while it might be "within" multiple
// nested regions.
////////////////////////////////////////////////////////////////////
INLINE void MouseWatcher::
set_within_pattern(const string &pattern) {
_within_pattern = pattern;
}
////////////////////////////////////////////////////////////////////
// Function: MouseWatcher::get_within_pattern
// Access: Published
// Description: Returns the string that indicates how event names are
// generated when the mouse wanders over a region. This
// is different from enter_pattern, in that a mouse is
// only "entered" in the topmost region at a given time,
// while it might be "within" multiple nested regions.
////////////////////////////////////////////////////////////////////
INLINE const string &MouseWatcher::
get_within_pattern() const {
return _within_pattern;
}
////////////////////////////////////////////////////////////////////
// Function: MouseWatcher::set_without_pattern
// Access: Published
// Description: Sets the pattern string that indicates how the event
// names are generated when the mouse wanders out of a
// region. This is different from leave_pattern, in
// that a mouse is only "entered" in the topmost region
// at a given time, while it might be "within" multiple
// nested regions.
////////////////////////////////////////////////////////////////////
INLINE void MouseWatcher::
set_without_pattern(const string &pattern) {
_without_pattern = pattern;
}
////////////////////////////////////////////////////////////////////
// Function: MouseWatcher::get_without_pattern
// Access: Published
// Description: Returns the string that indicates how event names are
// generated when the mouse wanders out of a region.
// This is different from leave_pattern, in that a mouse
// is only "entered" in the topmost region at a given
// time, while it might be "within" multiple nested
// regions.
////////////////////////////////////////////////////////////////////
INLINE const string &MouseWatcher::
get_without_pattern() const {
return _without_pattern;
}
////////////////////////////////////////////////////////////////////
// Function: MouseWatcher::set_geometry
// Access: Published

View File

@ -29,6 +29,8 @@
#include "pruneTransition.h"
#include "transformTransition.h"
#include <algorithm>
TypeHandle MouseWatcher::_type_handle;
TypeHandle MouseWatcher::_xyz_type;
@ -44,8 +46,8 @@ MouseWatcher::
MouseWatcher(const string &name) : DataNode(name) {
_has_mouse = false;
_suppress_flags = 0;
_current_region = (MouseWatcherRegion *)NULL;
_button_down_region = (MouseWatcherRegion *)NULL;
_preferred_region = (MouseWatcherRegion *)NULL;
_preferred_button_down_region = (MouseWatcherRegion *)NULL;
_button_down = false;
_eh = (EventHandler*)0L;
}
@ -68,12 +70,14 @@ MouseWatcher::
////////////////////////////////////////////////////////////////////
bool MouseWatcher::
remove_region(MouseWatcherRegion *region) {
if (region == _current_region) {
_current_region = (MouseWatcherRegion *)NULL;
remove_region_from(_current_regions, region);
if (region == _preferred_region) {
_preferred_region = (MouseWatcherRegion *)NULL;
}
if (region == _button_down_region) {
_button_down_region = (MouseWatcherRegion *)NULL;
if (region == _preferred_button_down_region) {
_preferred_button_down_region = (MouseWatcherRegion *)NULL;
}
return MouseWatcherGroup::remove_region(region);
}
@ -88,47 +92,9 @@ remove_region(MouseWatcherRegion *region) {
////////////////////////////////////////////////////////////////////
MouseWatcherRegion *MouseWatcher::
get_over_region(const LPoint2f &pos) const {
MouseWatcherRegion *over_region = (MouseWatcherRegion *)NULL;
Regions::const_iterator ri;
for (ri = _regions.begin(); ri != _regions.end(); ++ri) {
MouseWatcherRegion *region = (*ri);
const LVecBase4f &frame = region->get_frame();
if (region->get_active() &&
pos[0] >= frame[0] && pos[0] <= frame[1] &&
pos[1] >= frame[2] && pos[1] <= frame[3]) {
// We're over this region. Is it preferred to the other one?
if (over_region == (MouseWatcherRegion *)NULL ||
*region < *over_region) {
over_region = region;
}
}
}
// Also check all of our sub-groups.
Groups::const_iterator gi;
for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
MouseWatcherGroup *group = (*gi);
for (ri = group->_regions.begin(); ri != group->_regions.end(); ++ri) {
MouseWatcherRegion *region = (*ri);
const LVecBase4f &frame = region->get_frame();
if (region->get_active() &&
pos[0] >= frame[0] && pos[0] <= frame[1] &&
pos[1] >= frame[2] && pos[1] <= frame[3]) {
// We're over this region. Is it preferred to the other one?
if (over_region == (MouseWatcherRegion *)NULL ||
*region < *over_region) {
over_region = region;
}
}
}
}
return over_region;
VRegions regions;
get_over_regions(regions, pos);
return get_preferred_region(regions);
}
@ -140,7 +106,15 @@ get_over_region(const LPoint2f &pos) const {
void MouseWatcher::
output(ostream &out) const {
DataNode::output(out);
out << " (" << _regions.size() << " regions)";
int count = _regions.size();
Groups::const_iterator gi;
for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
MouseWatcherGroup *group = (*gi);
count += group->_regions.size();
}
out << " (" << count << " regions)";
}
////////////////////////////////////////////////////////////////////
@ -203,44 +177,317 @@ add_group(MouseWatcherGroup *group) {
////////////////////////////////////////////////////////////////////
bool MouseWatcher::
remove_group(MouseWatcherGroup *group) {
if (group->has_region(_current_region)) {
_current_region = (MouseWatcherRegion *)NULL;
remove_regions_from(_current_regions, group);
if (group->has_region(_preferred_region)) {
_preferred_region = (MouseWatcherRegion *)NULL;
}
if (group->has_region(_button_down_region)) {
_button_down_region = (MouseWatcherRegion *)NULL;
if (group->has_region(_preferred_button_down_region)) {
_preferred_button_down_region = (MouseWatcherRegion *)NULL;
}
return _groups.erase(group) != 0;
}
////////////////////////////////////////////////////////////////////
// Function: MouseWatcher::set_current_region
// Function: MouseWatcher::get_over_regions
// Access: Private
// Description: Changes the "current" region--the one we consider the
// mouse to be over--to the indicated one, and throws
// whatever events are appropriate because of that.
// Description: Fills up the "regions" list with the set of regions
// that the indicated point is over, sorted in order by
// pointer.
////////////////////////////////////////////////////////////////////
void MouseWatcher::
set_current_region(MouseWatcherRegion *region) {
#ifndef NDEBUG
if (region != (MouseWatcherRegion *)NULL) {
region->test_ref_count_integrity();
get_over_regions(MouseWatcher::VRegions &regions, const LPoint2f &pos) const {
// Ensure the vector is empty before we begin.
regions.clear();
Regions::const_iterator ri;
for (ri = _regions.begin(); ri != _regions.end(); ++ri) {
MouseWatcherRegion *region = (*ri);
const LVecBase4f &frame = region->get_frame();
if (region->get_active() &&
pos[0] >= frame[0] && pos[0] <= frame[1] &&
pos[1] >= frame[2] && pos[1] <= frame[3]) {
regions.push_back(region);
}
#endif
if (region != _current_region) {
}
// Also check all of our sub-groups.
Groups::const_iterator gi;
for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
MouseWatcherGroup *group = (*gi);
for (ri = group->_regions.begin(); ri != group->_regions.end(); ++ri) {
MouseWatcherRegion *region = (*ri);
const LVecBase4f &frame = region->get_frame();
if (region->get_active() &&
pos[0] >= frame[0] && pos[0] <= frame[1] &&
pos[1] >= frame[2] && pos[1] <= frame[3]) {
regions.push_back(region);
}
}
}
// Now sort the regions by pointer. By convention, the Regions
// vectors are always kept in order by pointer, so we can do easy
// linear comparison and intersection operations.
sort(regions.begin(), regions.end());
}
////////////////////////////////////////////////////////////////////
// Function: MouseWatcher::get_preferred_region
// Access: Private, Static
// Description: Returns the innermost region of all the regions
// indicated in the given vector (usually, the regions
// the mouse is over). This is the "preferred" region
// that gets some special treatment.
////////////////////////////////////////////////////////////////////
MouseWatcherRegion *MouseWatcher::
get_preferred_region(const MouseWatcher::VRegions &regions) {
if (regions.empty()) {
return (MouseWatcherRegion *)NULL;
}
VRegions::const_iterator ri;
ri = regions.begin();
MouseWatcherRegion *preferred = *ri;
++ri;
while (ri != regions.end()) {
MouseWatcherRegion *region = *ri;
if (*region < *preferred) {
preferred = region;
}
++ri;
}
return preferred;
}
////////////////////////////////////////////////////////////////////
// Function: MouseWatcher::set_current_regions
// Access: Private
// Description: Changes the "current" regions--the one we consider the
// mouse to be over--to the indicated list, and throws
// whatever events are appropriate because of that.
//
// The list passed in is destroyed.
////////////////////////////////////////////////////////////////////
void MouseWatcher::
set_current_regions(MouseWatcher::VRegions &regions) {
// Set up a parameter for passing through any change events.
MouseWatcherParameter param;
param.set_modifier_buttons(_mods);
param.set_mouse(_mouse);
if (_current_region != (MouseWatcherRegion *)NULL) {
_current_region->exit(param);
throw_event_pattern(_leave_pattern, _current_region, ButtonHandle::none());
}
_current_region = region;
if (_current_region != (MouseWatcherRegion *)NULL) {
_current_region->enter(param);
throw_event_pattern(_enter_pattern, _current_region, ButtonHandle::none());
// Now do a standard sorted comparison between the two vectors.
VRegions::const_iterator new_ri = regions.begin();
VRegions::const_iterator old_ri = _current_regions.begin();
bool any_changes = false;
while (new_ri != regions.end() && old_ri != _current_regions.end()) {
if ((*new_ri) < (*old_ri)) {
// Here's a new region that we didn't have last frame.
MouseWatcherRegion *new_region = (*new_ri);
new_region->within(param);
throw_event_pattern(_within_pattern, new_region, ButtonHandle::none());
any_changes = true;
++new_ri;
} else if ((*old_ri) < (*new_ri)) {
// Here's a region we don't have any more.
MouseWatcherRegion *old_region = (*old_ri);
old_region->without(param);
throw_event_pattern(_without_pattern, old_region, ButtonHandle::none());
any_changes = true;
++old_ri;
} else {
// Here's a region that hasn't changed.
++new_ri;
++old_ri;
}
}
while (new_ri != regions.end()) {
// Here's a new region that we didn't have last frame.
MouseWatcherRegion *new_region = (*new_ri);
new_region->within(param);
throw_event_pattern(_within_pattern, new_region, ButtonHandle::none());
any_changes = true;
++new_ri;
}
while (old_ri != _current_regions.end()) {
// Here's a region we don't have any more.
MouseWatcherRegion *old_region = (*old_ri);
old_region->without(param);
throw_event_pattern(_without_pattern, old_region, ButtonHandle::none());
any_changes = true;
++old_ri;
}
if (any_changes) {
// Now that we've compared the two vectors, simply swap them to set
// the new vector.
_current_regions.swap(regions);
// Determine which is the "preferred region", if any. This is the
// topmost region that the mouse cursor is over, and the one that
// we are considred "entered" into.
MouseWatcherRegion *new_preferred_region =
get_preferred_region(_current_regions);
if (_button_down && new_preferred_region != _preferred_button_down_region) {
// If the button's being held down, we're only allowed to select
// the preferred button down region.
new_preferred_region = (MouseWatcherRegion *)NULL;
}
if (new_preferred_region != _preferred_region) {
if (_preferred_region != (MouseWatcherRegion *)NULL) {
_preferred_region->exit(param);
throw_event_pattern(_leave_pattern, _preferred_region, ButtonHandle::none());
}
_preferred_region = new_preferred_region;
if (_preferred_region != (MouseWatcherRegion *)NULL) {
_preferred_region->enter(param);
throw_event_pattern(_enter_pattern, _preferred_region, ButtonHandle::none());
}
}
}
}
////////////////////////////////////////////////////////////////////
// Function: MouseWatcher::clear_current_regions
// Access: Private
// Description: Empties the set of current regions.
////////////////////////////////////////////////////////////////////
void MouseWatcher::
clear_current_regions() {
if (!_current_regions.empty()) {
// Set up a parameter for passing through any change events.
MouseWatcherParameter param;
param.set_modifier_buttons(_mods);
param.set_mouse(_mouse);
VRegions::const_iterator old_ri = _current_regions.begin();
while (old_ri != _current_regions.end()) {
// Here's a region we don't have any more.
MouseWatcherRegion *old_region = (*old_ri);
old_region->exit(param);
throw_event_pattern(_leave_pattern, old_region, ButtonHandle::none());
++old_ri;
}
_current_regions.clear();
if (_preferred_region != (MouseWatcherRegion *)NULL) {
_preferred_region->exit(param);
throw_event_pattern(_leave_pattern, _preferred_region, ButtonHandle::none());
_preferred_region = (MouseWatcherRegion *)NULL;
}
}
}
////////////////////////////////////////////////////////////////////
// Function: MouseWatcher::intersect_regions
// Access: Private, Static
// Description: Sets result to be the intersection of the list of
// regions in regions_a and regions_b. It is assumed
// that both vectors are already sorted in pointer
// order.
////////////////////////////////////////////////////////////////////
void MouseWatcher::
intersect_regions(MouseWatcher::VRegions &result,
const MouseWatcher::VRegions &regions_a,
const MouseWatcher::VRegions &regions_b) {
// Get a temporary vector for storing the result in. We don't use
// result directly, because it might be the same vector as one of a
// or b.
VRegions temp;
// Now do a standard sorted intersection between the two vectors.
VRegions::const_iterator a_ri = regions_a.begin();
VRegions::const_iterator b_ri = regions_b.begin();
while (a_ri != regions_a.end() && b_ri != regions_b.end()) {
if ((*a_ri) < (*b_ri)) {
// Here's a region in a, not in b.
++a_ri;
} else if ((*b_ri) < (*a_ri)) {
// Here's a region in b, not in a.
++b_ri;
} else {
// Here's a region in both vectors.
temp.push_back(*a_ri);
++a_ri;
++b_ri;
}
}
// Now store the result!
result.swap(temp);
}
////////////////////////////////////////////////////////////////////
// Function: MouseWatcher::remove_region_from
// Access: Private, Static
// Description: Removes the indicated region from the given vector.
// Assumes the vector is sorted in pointer order.
////////////////////////////////////////////////////////////////////
void MouseWatcher::
remove_region_from(MouseWatcher::VRegions &regions,
MouseWatcherRegion *region) {
VRegions::iterator ri =
lower_bound(regions.begin(), regions.end(), region);
if (ri != regions.end() && (*ri) == region) {
// The region is in the vector. Remove it.
regions.erase(ri);
}
}
////////////////////////////////////////////////////////////////////
// Function: MouseWatcher::remove_regions_from
// Access: Private, Static
// Description: Removes all the regions in the indicated group from
// the given vector. Assumes the vector is sorted in
// pointer order.
////////////////////////////////////////////////////////////////////
void MouseWatcher::
remove_regions_from(MouseWatcher::VRegions &regions,
MouseWatcherGroup *group) {
// Since the group stores a set of regions, which are also sorted in
// pointer order, we can just do an intersection operation here.
VRegions temp;
VRegions::const_iterator a_ri = regions.begin();
MouseWatcherGroup::Regions::const_iterator b_ri = group->_regions.begin();
while (a_ri != regions.end() && b_ri != group->_regions.end()) {
if ((*a_ri) < (*b_ri)) {
// Here's a region in the group, not in regions.
++a_ri;
} else if ((*b_ri) < (*a_ri)) {
// Here's a region in regions, not in the group.
temp.push_back(*b_ri);
++b_ri;
} else {
// Here's a region in the group and in regions.
++a_ri;
++b_ri;
}
}
// Now store the result!
regions.swap(temp);
}
////////////////////////////////////////////////////////////////////
@ -318,24 +565,25 @@ press(ButtonHandle button) {
// Mouse buttons are inextricably linked to the mouse position.
if (!_button_down) {
_button_down_region = _current_region;
_preferred_button_down_region = _preferred_region;
}
_button_down = true;
if (_button_down_region != (MouseWatcherRegion *)NULL) {
_button_down_region->press(param);
throw_event_pattern(_button_down_pattern, _button_down_region,
button);
if (_preferred_button_down_region != (MouseWatcherRegion *)NULL) {
_preferred_button_down_region->press(param);
throw_event_pattern(_button_down_pattern,
_preferred_button_down_region, button);
}
} else {
// It's a keyboard button; therefore, send the event to every
// region that wants keyboard buttons, regardless of the mouse
// position.
if (_current_region != (MouseWatcherRegion *)NULL) {
if (_preferred_region != (MouseWatcherRegion *)NULL) {
// Our current region, the one under the mouse, always get
// all the keyboard events, even if it doesn't set its
// keyboard flag.
_current_region->press(param);
_preferred_region->press(param);
}
if ((_suppress_flags & MouseWatcherRegion::SF_other_button) == 0) {
@ -362,26 +610,28 @@ release(ButtonHandle button) {
param.set_mouse(_mouse);
if (MouseButton::is_mouse_button(button)) {
// Button up. Send the up event associated with the region we
// Button up. Send the up event associated with the region(s) we
// were over when the button went down.
// There is some danger of losing button-up events here. If
// more than one button goes down together, we won't detect
// both of the button-up events properly.
if (_button_down_region != (MouseWatcherRegion *)NULL) {
param.set_outside(_current_region != _button_down_region);
_button_down_region->release(param);
throw_event_pattern(_button_up_pattern, _button_down_region,
button);
if (_preferred_button_down_region != (MouseWatcherRegion *)NULL) {
param.set_outside(_preferred_button_down_region != _preferred_region);
_preferred_button_down_region->release(param);
throw_event_pattern(_button_up_pattern,
_preferred_button_down_region, button);
}
_button_down = false;
_preferred_button_down_region = (MouseWatcherRegion *)NULL;
} else {
// It's a keyboard button; therefore, send the event to every
// region that wants keyboard buttons, regardless of the mouse
// position.
if (_current_region != (MouseWatcherRegion *)NULL) {
_current_region->release(param);
if (_preferred_region != (MouseWatcherRegion *)NULL) {
_preferred_region->release(param);
}
param.set_outside(true);
@ -402,7 +652,7 @@ global_keyboard_press(const MouseWatcherParameter &param) {
for (ri = _regions.begin(); ri != _regions.end(); ++ri) {
MouseWatcherRegion *region = (*ri);
if (region != _current_region && region->get_keyboard()) {
if (region != _preferred_region && region->get_keyboard()) {
region->press(param);
}
}
@ -414,7 +664,7 @@ global_keyboard_press(const MouseWatcherParameter &param) {
for (ri = group->_regions.begin(); ri != group->_regions.end(); ++ri) {
MouseWatcherRegion *region = (*ri);
if (region != _current_region && region->get_keyboard()) {
if (region != _preferred_region && region->get_keyboard()) {
region->press(param);
}
}
@ -434,7 +684,7 @@ global_keyboard_release(const MouseWatcherParameter &param) {
for (ri = _regions.begin(); ri != _regions.end(); ++ri) {
MouseWatcherRegion *region = (*ri);
if (region != _current_region && region->get_keyboard()) {
if (region != _preferred_region && region->get_keyboard()) {
region->release(param);
}
}
@ -446,7 +696,7 @@ global_keyboard_release(const MouseWatcherParameter &param) {
for (ri = group->_regions.begin(); ri != group->_regions.end(); ++ri) {
MouseWatcherRegion *region = (*ri);
if (region != _current_region && region->get_keyboard()) {
if (region != _preferred_region && region->get_keyboard()) {
region->release(param);
}
}
@ -473,7 +723,7 @@ transmit_data(AllTransitionsWrapper &data) {
_has_mouse = false;
// If the mouse is outside the window, do nothing; let all the
// events continue down the pipe unmolested.
set_current_region(NULL);
clear_current_regions();
return;
}
@ -492,26 +742,13 @@ transmit_data(AllTransitionsWrapper &data) {
_has_mouse = true;
if (!_button_down) {
// If the button is not currently being held down, we are free to
// set the mouse into whichever region we like.
set_current_region(get_over_region(_mouse));
} else {
// If the button *is* currently being held down, we can only move
// the mouse into a region if the region is the same region we
// started from.
MouseWatcherRegion *region = get_over_region(_mouse);
if (region == _button_down_region) {
set_current_region(region);
} else {
set_current_region((MouseWatcherRegion *)NULL);
}
}
VRegions regions;
get_over_regions(regions, _mouse);
set_current_regions(regions);
_suppress_flags = 0;
if (_current_region != (MouseWatcherRegion *)NULL) {
_suppress_flags = _current_region->get_suppress_flags();
if (_preferred_region != (MouseWatcherRegion *)NULL) {
_suppress_flags = _preferred_region->get_suppress_flags();
}
// Look for button events.

View File

@ -89,6 +89,12 @@ PUBLISHED:
INLINE void set_leave_pattern(const string &pattern);
INLINE const string &get_leave_pattern() const;
INLINE void set_within_pattern(const string &pattern);
INLINE const string &get_within_pattern() const;
INLINE void set_without_pattern(const string &pattern);
INLINE const string &get_without_pattern() const;
INLINE void set_geometry(NodeRelation *arc);
INLINE bool has_geometry() const;
INLINE NodeRelation *get_geometry() const;
@ -108,7 +114,21 @@ public:
bool remove_group(MouseWatcherGroup *group);
private:
void set_current_region(MouseWatcherRegion *region);
typedef pvector< PT(MouseWatcherRegion) > VRegions;
void get_over_regions(VRegions &regions, const LPoint2f &pos) const;
static MouseWatcherRegion *get_preferred_region(const VRegions &regions);
void set_current_regions(VRegions &regions);
void clear_current_regions();
static void intersect_regions(MouseWatcher::VRegions &result,
const MouseWatcher::VRegions &regions_a,
const MouseWatcher::VRegions &regions_b);
static void remove_region_from(MouseWatcher::VRegions &regions,
MouseWatcherRegion *region);
static void remove_regions_from(MouseWatcher::VRegions &regions,
MouseWatcherGroup *group);
void throw_event_pattern(const string &pattern,
const MouseWatcherRegion *region,
const ButtonHandle &button);
@ -125,14 +145,17 @@ private:
int _suppress_flags;
LPoint2f _mouse;
PT(MouseWatcherRegion) _current_region;
PT(MouseWatcherRegion) _button_down_region;
VRegions _current_regions;
PT(MouseWatcherRegion) _preferred_region;
PT(MouseWatcherRegion) _preferred_button_down_region;
bool _button_down;
string _button_down_pattern;
string _button_up_pattern;
string _enter_pattern;
string _leave_pattern;
string _within_pattern;
string _without_pattern;
PT_NodeRelation _geometry;

View File

@ -47,7 +47,10 @@ write(ostream &out, int indent_level) const {
// Function: MouseWatcherRegion::enter
// Access: Public, Virtual
// Description: This is a callback hook function, called whenever the
// mouse enters the region.
// mouse enters the region. The mouse is only
// considered to be "entered" in one region at a time;
// in the case of nested regions, it exits the outer
// region before entering the inner one.
////////////////////////////////////////////////////////////////////
void MouseWatcherRegion::
enter(const MouseWatcherParameter &) {
@ -57,12 +60,40 @@ enter(const MouseWatcherParameter &) {
// Function: MouseWatcherRegion::exit
// Access: Public, Virtual
// Description: This is a callback hook function, called whenever the
// mouse exits the region.
// mouse exits the region. The mouse is only considered
// to be "entered" in one region at a time; in the case
// of nested regions, it exits the outer region before
// entering the inner one.
////////////////////////////////////////////////////////////////////
void MouseWatcherRegion::
exit(const MouseWatcherParameter &) {
}
////////////////////////////////////////////////////////////////////
// Function: MouseWatcherRegion::within
// Access: Public, Virtual
// Description: This is a callback hook function, called whenever the
// mouse moves within the boundaries of the region, even
// if it is also within the boundaries of a nested
// region. This is different from "enter", which is
// only called whenever the mouse is within only that
// region.
////////////////////////////////////////////////////////////////////
void MouseWatcherRegion::
within(const MouseWatcherParameter &) {
}
////////////////////////////////////////////////////////////////////
// Function: MouseWatcherRegion::without
// Access: Public, Virtual
// Description: This is a callback hook function, called whenever the
// mouse moves completely outside the boundaries of the
// region. See within().
////////////////////////////////////////////////////////////////////
void MouseWatcherRegion::
without(const MouseWatcherParameter &) {
}
////////////////////////////////////////////////////////////////////
// Function: MouseWatcherRegion::press
// Access: Public, Virtual

View File

@ -72,6 +72,8 @@ public:
virtual void enter(const MouseWatcherParameter &param);
virtual void exit(const MouseWatcherParameter &param);
virtual void within(const MouseWatcherParameter &param);
virtual void without(const MouseWatcherParameter &param);
virtual void press(const MouseWatcherParameter &param);
virtual void release(const MouseWatcherParameter &param);