From a79af737d2ad6814deb3e9e340baa8336a962206 Mon Sep 17 00:00:00 2001 From: David Rose Date: Fri, 5 Mar 2004 20:10:19 +0000 Subject: [PATCH] add MouseWatcher::set_display_region() --- panda/src/framework/pandaFramework.cxx | 91 ++++++++++- panda/src/framework/pandaFramework.h | 7 + panda/src/framework/windowFramework.cxx | 207 +++++++++++++++++------- panda/src/framework/windowFramework.h | 13 ++ panda/src/putil/mouseButton.cxx | 2 +- panda/src/testbed/pview.cxx | 17 ++ panda/src/tform/mouseWatcher.I | 57 +++++++ panda/src/tform/mouseWatcher.cxx | 136 +++++++++++----- panda/src/tform/mouseWatcher.h | 12 +- 9 files changed, 439 insertions(+), 103 deletions(-) diff --git a/panda/src/framework/pandaFramework.cxx b/panda/src/framework/pandaFramework.cxx index 2433f51efd..6bd8d854d9 100644 --- a/panda/src/framework/pandaFramework.cxx +++ b/panda/src/framework/pandaFramework.cxx @@ -26,6 +26,8 @@ #include "graphicsPipeSelection.h" #include "nodePathCollection.h" #include "textNode.h" +#include "mouseAndKeyboard.h" +#include "mouseRecorder.h" //////////////////////////////////////////////////////////////////// // Function: PandaFramework::Constructor @@ -157,6 +159,59 @@ get_default_pipe() { return _default_pipe; } +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::get_mouse +// Access: Public +// Description: Returns a NodePath to the MouseAndKeyboard associated +// with the indicated GraphicsWindow object. If there's +// not yet a mouse associated with the window, creates +// one. +// +// This allows multiple WindowFramework objects that +// represent different display regions of the same +// GraphicsWindow to share the same mouse. +//////////////////////////////////////////////////////////////////// +NodePath PandaFramework:: +get_mouse(GraphicsWindow *window) { + Mouses::iterator mi = _mouses.find(window); + if (mi != _mouses.end()) { + return (*mi).second; + } + + NodePath mouse; + + NodePath data_root = get_data_root(); + MouseAndKeyboard *mouse_node = new MouseAndKeyboard(window, 0, "mouse"); + mouse = data_root.attach_new_node(mouse_node); + + RecorderController *recorder = get_recorder(); + if (recorder != (RecorderController *)NULL) { + // If we're in recording or playback mode, associate a recorder. + MouseRecorder *mouse_recorder = new MouseRecorder("mouse"); + mouse = mouse.attach_new_node(mouse_recorder); + recorder->add_recorder("mouse", mouse_recorder); + } + + _mouses[window] = mouse; + + return mouse; +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaFramework::remove_mouse +// Access: Public +// Description: Removes the mouse that may have been created by an +// earlier call to get_mouse(). +//////////////////////////////////////////////////////////////////// +void PandaFramework:: +remove_mouse(const GraphicsWindow *window) { + Mouses::iterator mi = _mouses.find(window); + if (mi != _mouses.end()) { + (*mi).second.remove_node(); + _mouses.erase(mi); + } +} + //////////////////////////////////////////////////////////////////// // Function: PandaFramework::define_key // Access: Public @@ -276,9 +331,9 @@ open_window(const WindowProperties &props, GraphicsPipe *pipe, //////////////////////////////////////////////////////////////////// // Function: PandaFramework::find_window // Access: Public -// Description: Returns the index of the WindowFramework object that -// references the indicated GraphicsWindow pointer, or -// -1 if none do. +// Description: Returns the index of the first WindowFramework object +// found that references the indicated GraphicsWindow +// pointer, or -1 if none do. //////////////////////////////////////////////////////////////////// int PandaFramework:: find_window(const GraphicsWindow *win) const { @@ -351,7 +406,13 @@ close_all_windows() { wf->close_window(); } + Mouses::iterator mi; + for (mi = _mouses.begin(); mi != _mouses.end(); ++mi) { + (*mi).second.remove_node(); + } + _windows.clear(); + _mouses.clear(); } //////////////////////////////////////////////////////////////////// @@ -758,9 +819,21 @@ event_esc(CPT_Event event, void *data) { WindowFramework *wf; DCAST_INTO_V(wf, param.get_ptr()); + PT(GraphicsWindow) win = wf->get_graphics_window(); + PandaFramework *self = (PandaFramework *)data; self->close_window(wf); + // Also close any other WindowFrameworks on the same window. + int window_index = self->find_window(win); + while (window_index != -1) { + self->close_window(window_index); + window_index = self->find_window(win); + } + + // Free up the mouse for that window. + self->remove_mouse(win); + // If we closed the last window, shut down. if (self->_windows.empty()) { self->_exit_flag = true; @@ -1221,10 +1294,20 @@ event_window_event(CPT_Event event, void *data) { if (window_index == -1) { framework_cat.debug() << "Ignoring message from unknown window.\n"; + } else { if (!win->get_properties().get_open()) { + int window_index = self->find_window(win); + while (window_index != -1) { + self->close_window(window_index); + window_index = self->find_window(win); + } + + // Free up the mouse for that window. + self->remove_mouse(win); + // If the last window was closed, exit the application. - if (self->all_windows_closed() && !self->_exit_flag) { + if (self->_windows.empty() && !self->_exit_flag) { framework_cat.info() << "Last window was closed by user.\n"; self->_exit_flag = true; diff --git a/panda/src/framework/pandaFramework.h b/panda/src/framework/pandaFramework.h index e5cb75daeb..d98a0d19df 100644 --- a/panda/src/framework/pandaFramework.h +++ b/panda/src/framework/pandaFramework.h @@ -51,6 +51,8 @@ public: INLINE GraphicsEngine *get_graphics_engine(); INLINE const NodePath &get_data_root() const; INLINE EventHandler &get_event_handler(); + NodePath get_mouse(GraphicsWindow *window); + void remove_mouse(const GraphicsWindow *window); void define_key(const string &event_name, const string &description, @@ -155,6 +157,9 @@ private: typedef pvector< PT(WindowFramework) > Windows; Windows _windows; + typedef pmap< const GraphicsWindow *, NodePath > Mouses; + Mouses _mouses; + NodePath _models; // For counting frame rate. @@ -187,6 +192,8 @@ private: double _screenshot_clear_time; PT(RecorderController) _recorder; + + friend class WindowFramework; }; #include "pandaFramework.I" diff --git a/panda/src/framework/windowFramework.cxx b/panda/src/framework/windowFramework.cxx index 717563ce20..7e022deff8 100644 --- a/panda/src/framework/windowFramework.cxx +++ b/panda/src/framework/windowFramework.cxx @@ -18,8 +18,7 @@ #include "windowFramework.h" #include "pandaFramework.h" -#include "mouseAndKeyboard.h" -#include "mouseRecorder.h" +#include "displayRegion.h" #include "buttonThrower.h" #include "transform2sg.h" #include "dSearchPath.h" @@ -77,6 +76,34 @@ WindowFramework(PandaFramework *panda_framework) : _background_type = BT_default; } +//////////////////////////////////////////////////////////////////// +// Function: WindowFramework::Copy Constructor +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +WindowFramework:: +WindowFramework(const WindowFramework ©, DisplayRegion *display_region) : + _panda_framework(copy._panda_framework), + _window(copy._window), + _display_region_3d(display_region) +{ + _alight = (AmbientLight *)NULL; + _dlight = (DirectionalLight *)NULL; + _got_keyboard = false; + _got_trackball = false; + _got_lights = false; + _wireframe_enabled = false; + _texture_enabled = true; + _two_sided_enabled = false; + _one_sided_reverse_enabled = false; + _lighting_enabled = false; + _background_type = BT_default; + + set_background_type(copy._background_type); + // Set up a 3-d camera for the window by default. + make_camera(); +} + //////////////////////////////////////////////////////////////////// // Function: WindowFramework::Destructor // Access: Public, Virtual @@ -120,6 +147,21 @@ open_window(const WindowProperties &props, GraphicsEngine *engine, _window = engine->make_window(ptgsg, name, 0); if (_window != (GraphicsWindow *)NULL) { _window->request_properties(props); + + // Get the first channel on the window. This will be the only + // channel on non-SGI hardware. + PT(GraphicsChannel) channel = _window->get_channel(0); + + // Make a layer on the channel to hold our display region. + PT(GraphicsLayer) layer = channel->make_layer(); + + // And create a display region that covers the entire window. + _display_region_3d = layer->make_display_region(); + + // Make sure the DisplayRegion does the clearing, not the window, + // so we can have multiple DisplayRegions of different colors. + _window->set_clear_color_active(false); + _window->set_clear_depth_active(false); set_background_type(_background_type); // Set up a 3-d camera for the window by default. @@ -228,8 +270,11 @@ get_render_2d() { // Make a layer on the channel to hold our display region. PT(GraphicsLayer) layer = channel->make_layer(10); - // And create a display region that covers the entire window. - PT(DisplayRegion) dr = layer->make_display_region(); + // And create a display region that matches the size of the 3-d + // display region. + float l, r, b, t; + _display_region_3d->get_dimensions(l, r, b, t); + _display_region_2d = layer->make_display_region(l, r, b, t); // Finally, we need a camera to associate with the display region. PT(Camera) camera = new Camera("camera2d"); @@ -247,7 +292,7 @@ get_render_2d() { camera->set_lens(lens); camera->set_scene(_render_2d); - dr->set_camera(camera_np); + _display_region_2d->set_camera(camera_np); } return _render_2d; @@ -294,19 +339,16 @@ get_aspect_2d() { const NodePath &WindowFramework:: get_mouse() { if (_mouse.is_empty()) { - nassertr(_window->get_num_input_devices() > 0, _mouse); - NodePath data_root = _panda_framework->get_data_root(); - MouseAndKeyboard *mouse_node = - new MouseAndKeyboard(_window, 0, "mouse"); - _mouse = data_root.attach_new_node(mouse_node); + NodePath mouse = _panda_framework->get_mouse(_window); - RecorderController *recorder = _panda_framework->get_recorder(); - if (recorder != (RecorderController *)NULL) { - // If we're in recording or playback mode, associate a recorder. - MouseRecorder *mouse_recorder = new MouseRecorder("mouse"); - _mouse = _mouse.attach_new_node(mouse_recorder); - recorder->add_recorder("mouse", mouse_recorder); - } + // Create a MouseWatcher to filter the mouse input. We do this + // mainly so we can constrain the mouse input to our particular + // display region, if we have one. This means the node we return + // from get_mouse() is actually a MouseWatcher, but since it + // presents the same interface as a Mouse, no one should mind. + PT(MouseWatcher) mw = new MouseWatcher("watcher"); + mw->set_display_region(_display_region_3d); + _mouse = mouse.attach_new_node(mw); } return _mouse; } @@ -326,6 +368,8 @@ enable_keyboard() { if (_window->get_num_input_devices() > 0) { NodePath mouse = get_mouse(); + // Create a button thrower to listen for our keyboard events and + // associate this WindowFramework pointer with each one. PT(ButtonThrower) bt = new ButtonThrower("kb-events"); bt->add_parameter(EventParameter(this)); ModifierButtons mods; @@ -479,8 +523,6 @@ bool WindowFramework:: load_models(const NodePath &parent, const pvector &files) { bool all_ok = true; - NodePath render = get_render(); - pvector::const_iterator fi; for (fi = files.begin(); fi != files.end(); ++fi) { const Filename &filename = (*fi); @@ -610,6 +652,59 @@ loop_animations() { _anim_controls.loop_all(true); } +//////////////////////////////////////////////////////////////////// +// Function: WindowFramework::split_window +// Access: Public +// Description: Divides the window into two display regions, each of +// which gets its own trackball and keyboard events. +// The new window pointer is returned. +// +// There is not an interface for recombining divided +// windows. +//////////////////////////////////////////////////////////////////// +WindowFramework *WindowFramework:: +split_window(SplitType split_type) { + DisplayRegion *new_region = NULL; + + if (split_type == ST_default) { + // Choose either horizontal or vertical according to the largest + // dimension. + + if (_display_region_3d->get_pixel_width() > + _display_region_3d->get_pixel_height()) { + split_type = ST_horizontal; + } else { + split_type = ST_vertical; + } + } + + float left, right, bottom, top; + _display_region_3d->get_dimensions(left, right, bottom, top); + new_region = _display_region_3d->get_layer()->make_display_region(); + + if (split_type == ST_vertical) { + _display_region_3d->set_dimensions(left, right, bottom, (top + bottom) / 2.0f); + if (_display_region_2d != (DisplayRegion *)NULL) { + _display_region_2d->set_dimensions(left, right, bottom, (top + bottom) / 2.0f); + } + + new_region->set_dimensions(left, right, (top + bottom) / 2.0f, top); + + } else { + _display_region_3d->set_dimensions(left, (left + right) / 2.0f, bottom, top); + if (_display_region_2d != (DisplayRegion *)NULL) { + _display_region_2d->set_dimensions(left, (left + right) / 2.0f, bottom, top); + } + + new_region->set_dimensions((left + right) / 2.0f, right, bottom, top); + } + + PT(WindowFramework) wf = new WindowFramework(*this, new_region); + _panda_framework->_windows.push_back(wf); + + return wf; +} + //////////////////////////////////////////////////////////////////// // Function: WindowFramework::set_wireframe // Access: Public @@ -756,38 +851,40 @@ void WindowFramework:: set_background_type(WindowFramework::BackgroundType type) { _background_type = type; - if (_window != (GraphicsWindow *)NULL) { - switch (_background_type) { - case BT_other: - break; - - case BT_default: - _window->set_clear_color_active(true); - _window->set_clear_depth_active(true); - _window->set_clear_color(Colorf(win_background_r, win_background_g, - win_background_b, 1.0f)); - _window->set_clear_depth(1.0f); - break; - - case BT_gray: - _window->set_clear_color_active(true); - _window->set_clear_depth_active(true); - _window->set_clear_color(Colorf(0.3f, 0.3f, 0.3f, 1.0f)); - _window->set_clear_depth(1.0f); - break; - - case BT_black: - _window->set_clear_color_active(true); - _window->set_clear_depth_active(true); - _window->set_clear_color(Colorf(0.0f, 0.0f, 0.0f, 1.0f)); - _window->set_clear_depth(1.0f); + if (_display_region_3d == (DisplayRegion *)NULL) { + return; + } + + switch (_background_type) { + case BT_other: + break; + + case BT_default: + _display_region_3d->set_clear_color_active(true); + _display_region_3d->set_clear_depth_active(true); + _display_region_3d->set_clear_color(Colorf(win_background_r, win_background_g, + win_background_b, 1.0f)); + _display_region_3d->set_clear_depth(1.0f); + break; + + case BT_gray: + _display_region_3d->set_clear_color_active(true); + _display_region_3d->set_clear_depth_active(true); + _display_region_3d->set_clear_color(Colorf(0.3f, 0.3f, 0.3f, 1.0f)); + _display_region_3d->set_clear_depth(1.0f); + break; + + case BT_black: + _display_region_3d->set_clear_color_active(true); + _display_region_3d->set_clear_depth_active(true); + _display_region_3d->set_clear_color(Colorf(0.0f, 0.0f, 0.0f, 1.0f)); + _display_region_3d->set_clear_depth(1.0f); break; - case BT_none: - _window->set_clear_color_active(false); - _window->set_clear_depth_active(false); - break; - } + case BT_none: + _display_region_3d->set_clear_color_active(false); + _display_region_3d->set_clear_depth_active(false); + break; } } @@ -798,16 +895,6 @@ set_background_type(WindowFramework::BackgroundType type) { //////////////////////////////////////////////////////////////////// PT(Camera) WindowFramework:: make_camera() { - // Get the first channel on the window. This will be the only - // channel on non-SGI hardware. - PT(GraphicsChannel) channel = _window->get_channel(0); - - // Make a layer on the channel to hold our display region. - PT(GraphicsLayer) layer = channel->make_layer(); - - // And create a display region that covers the entire window. - PT(DisplayRegion) dr = layer->make_display_region(); - // Finally, we need a camera to associate with the display region. PT(Camera) camera = new Camera("camera"); NodePath camera_np = get_camera_group().attach_new_node(camera); @@ -833,7 +920,7 @@ make_camera() { camera->set_lens(lens); camera->set_scene(get_render()); - dr->set_camera(camera_np); + _display_region_3d->set_camera(camera_np); return camera; } diff --git a/panda/src/framework/windowFramework.h b/panda/src/framework/windowFramework.h index 45e9f4db29..1e95c961b6 100644 --- a/panda/src/framework/windowFramework.h +++ b/panda/src/framework/windowFramework.h @@ -37,6 +37,7 @@ class AmbientLight; class DirectionalLight; class GraphicsEngine; class GraphicsPipe; +class DisplayRegion; //////////////////////////////////////////////////////////////////// // Class : WindowFramework @@ -46,6 +47,8 @@ class GraphicsPipe; class EXPCL_FRAMEWORK WindowFramework : public TypedWritableReferenceCount { protected: WindowFramework(PandaFramework *panda_framework); + WindowFramework(const WindowFramework ©, DisplayRegion *display_region); + public: virtual ~WindowFramework(); @@ -58,6 +61,7 @@ protected: public: INLINE PandaFramework *get_panda_framework() const; INLINE GraphicsWindow *get_graphics_window() const; + INLINE DisplayRegion *get_display_region() const; const NodePath &get_camera_group(); INLINE int get_num_cameras() const; @@ -88,6 +92,13 @@ public: BT_none }; + enum SplitType { + ST_default, + ST_horizontal, + ST_vertical, + }; + WindowFramework *split_window(SplitType split_type = ST_default); + void set_wireframe(bool enable); void set_texture(bool enable); void set_two_sided(bool enable); @@ -112,6 +123,8 @@ private: private: PandaFramework *_panda_framework; PT(GraphicsWindow) _window; + PT(DisplayRegion) _display_region_2d; + PT(DisplayRegion) _display_region_3d; NodePath _camera_group; typedef pvector< PT(Camera) > Cameras; diff --git a/panda/src/putil/mouseButton.cxx b/panda/src/putil/mouseButton.cxx index 2b640538aa..ac139c675f 100644 --- a/panda/src/putil/mouseButton.cxx +++ b/panda/src/putil/mouseButton.cxx @@ -111,7 +111,7 @@ is_mouse_button(ButtonHandle button) { } } - return false; + return button == _wheel_up || button == _wheel_down; } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/testbed/pview.cxx b/panda/src/testbed/pview.cxx index 75ad9f0236..8047c977a7 100644 --- a/panda/src/testbed/pview.cxx +++ b/panda/src/testbed/pview.cxx @@ -52,6 +52,22 @@ event_W(CPT_Event, void *) { } } +void +event_2(CPT_Event event, void *) { + // 2: split the window into two display regions. + + EventParameter param = event->get_parameter(0); + WindowFramework *wf; + DCAST_INTO_V(wf, param.get_ptr()); + + WindowFramework *split = wf->split_window(); + if (split != (WindowFramework *)NULL) { + split->enable_keyboard(); + split->setup_trackball(); + framework.get_models().instance_to(split->get_render()); + } +} + void usage() { cerr << @@ -162,6 +178,7 @@ main(int argc, char *argv[]) { framework.enable_default_keys(); framework.define_key("shift-w", "open a new window", event_W, NULL); + framework.define_key("2", "split the window", event_2, NULL); framework.main_loop(); framework.report_frame_rate(nout); } diff --git a/panda/src/tform/mouseWatcher.I b/panda/src/tform/mouseWatcher.I index 713cb340f6..14e0b46107 100644 --- a/panda/src/tform/mouseWatcher.I +++ b/panda/src/tform/mouseWatcher.I @@ -406,6 +406,63 @@ get_modifier_buttons() const { return _mods; } +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::set_display_region +// Access: Published +// Description: Constrains the MouseWatcher to watching the mouse +// within a particular indicated region of the screen. +// DataNodes parented under the MouseWatcher will +// observe the mouse and keyboard events only when the +// mouse is within the indicated region, and the +// observed range will be from -1 .. 1 across the +// region. +// +// Do not delete the DisplayRegion while it is owned by +// the MouseWatcher. +//////////////////////////////////////////////////////////////////// +INLINE void MouseWatcher:: +set_display_region(DisplayRegion *dr) { + _display_region = dr; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::clear_display_region +// Access: Published +// Description: Removes the display region constraint from the +// MouseWatcher, and restores it to the default behavior +// of watching the whole window. +//////////////////////////////////////////////////////////////////// +INLINE void MouseWatcher:: +clear_display_region() { + _display_region = NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::get_display_region +// Access: Published +// Description: Returns the display region the MouseWatcher is +// constrained to by set_display_region(), or NULL if it +// is not constrained. +//////////////////////////////////////////////////////////////////// +INLINE DisplayRegion *MouseWatcher:: +get_display_region() const { + return _display_region; +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::has_display_region +// Access: Published +// Description: Returns true if the MouseWatcher has been constrained +// to a particular region of the screen via +// set_display_region(), or false otherwise. If this +// returns true, get_display_region() may be used to +// return the particular region. +//////////////////////////////////////////////////////////////////// +INLINE bool MouseWatcher:: +has_display_region() const { + return (_display_region != (DisplayRegion *)NULL); +} + //////////////////////////////////////////////////////////////////// // Function: MouseWatcher::within_region // Access: Protected diff --git a/panda/src/tform/mouseWatcher.cxx b/panda/src/tform/mouseWatcher.cxx index 34531a616a..01a1956c2a 100644 --- a/panda/src/tform/mouseWatcher.cxx +++ b/panda/src/tform/mouseWatcher.cxx @@ -27,6 +27,7 @@ #include "eventParameter.h" #include "dataNodeTransmit.h" #include "transformState.h" +#include "displayRegion.h" #include "dcast.h" #include @@ -59,7 +60,8 @@ MouseWatcher(const string &name) : _preferred_region = (MouseWatcherRegion *)NULL; _preferred_button_down_region = (MouseWatcherRegion *)NULL; _button_down = false; - _eh = (EventHandler*)0L; + _eh = (EventHandler *)NULL; + _display_region = (DisplayRegion *)NULL; // When this flag is true, the mouse pointer is allowed to be // "entered" into multiple regions simultaneously; when false, it @@ -707,8 +709,8 @@ keystroke(int keycode) { // Keystrokes go to all those regions that want keyboard events, // regardless of which is the "preferred" region (that is, without // respect to the mouse position). However, we do set the outside - // flag according to whether the given region is preferred region or - // not. + // flag according to whether the given region is the preferred + // region or not. Regions::const_iterator ri; for (ri = _regions.begin(); ri != _regions.end(); ++ri) { @@ -832,6 +834,52 @@ exit_region(MouseWatcherRegion *region, const MouseWatcherParameter ¶m) { throw_event_pattern(_leave_pattern, region, ButtonHandle::none()); } +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::set_no_mouse +// Access: Protected +// Description: Called from do_transmit_data() to indicate the mouse +// is not within the window. +//////////////////////////////////////////////////////////////////// +void MouseWatcher:: +set_no_mouse() { + if (_has_mouse) { + // Hide the mouse pointer. + if (!_geometry.is_null()) { + _geometry->set_draw_mask(DrawMask::all_off()); + } + } + + _has_mouse = false; + clear_current_regions(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MouseWatcher::set_mouse +// Access: Protected +// Description: Called from do_transmit_data() to indicate the mouse +// is within the window, and to specify its current +// position. +//////////////////////////////////////////////////////////////////// +void MouseWatcher:: +set_mouse(const LVecBase2f &xy, const LVecBase2f &pixel_xy) { + if (!_geometry.is_null()) { + // Transform the mouse pointer. + _geometry->set_transform(TransformState::make_pos(LVecBase3f(xy[0], 0, xy[1]))); + if (!_has_mouse) { + // Show the mouse pointer. + _geometry->set_draw_mask(DrawMask::all_on()); + } + } + + _has_mouse = true; + _mouse = xy; + _mouse_pixel = pixel_xy; + + VRegions regions; + get_over_regions(regions, _mouse); + set_current_regions(regions); +} + //////////////////////////////////////////////////////////////////// // Function: MouseWatcher::do_transmit_data // Access: Protected, Virtual @@ -847,43 +895,58 @@ exit_region(MouseWatcherRegion *region, const MouseWatcherParameter ¶m) { //////////////////////////////////////////////////////////////////// void MouseWatcher:: do_transmit_data(const DataNodeTransmit &input, DataNodeTransmit &output) { + // Initially, we do not suppress any events to objects below us in + // the data graph. + _suppress_flags = 0; + if (!input.has_data(_xy_input)) { // No mouse in the window. - - if (_has_mouse) { - // Hide the mouse pointer. - if (!_geometry.is_null()) { - _geometry->set_draw_mask(DrawMask::all_off()); - } - } - - _has_mouse = false; - clear_current_regions(); + set_no_mouse(); } else { // The mouse is within the window. Get the current mouse position. - const EventStoreVec2 *xy; + const EventStoreVec2 *xy, *pixel_xy; DCAST_INTO_V(xy, input.get_data(_xy_input).get_ptr()); - const LVecBase2f &p = xy->get_value(); - _mouse.set(p[0], p[1]); - - if (!_geometry.is_null()) { - // Transform the mouse pointer. - _geometry->set_transform(TransformState::make_pos(LVecBase3f(p[0], 0, p[1]))); - if (!_has_mouse) { - // Show the mouse pointer. - _geometry->set_draw_mask(DrawMask::all_on()); + DCAST_INTO_V(pixel_xy, input.get_data(_pixel_xy_input).get_ptr()); + + const LVecBase2f &f = xy->get_value(); + const LVecBase2f &p = pixel_xy->get_value(); + + if (_display_region != (DisplayRegion *)NULL) { + // If we've got a display region, constrain the mouse to it. + int xo, yo, width, height; + _display_region->get_region_pixels_i(xo, yo, width, height); + + if (p[0] < xo || p[0] >= xo + width || + p[1] < yo || p[1] >= yo + height) { + // The mouse is outside the display region, even though it's + // within the window. This is considered not having a mouse. + set_no_mouse(); + + // This also means we should suppress button events below us. + _suppress_flags = MouseWatcherRegion::SF_any_button; + + } else { + // The mouse is within the display region; rescale it. + float left, right, bottom, top; + _display_region->get_dimensions(left, right, bottom, top); + + LVecBase2f new_f((f[0] - left) / (right - left), + (f[1] - bottom) / (top - bottom)); + LVecBase2f new_p(p[0] - xo, p[1] - xo); + + set_mouse(new_f, new_p); } + + } else { + // No display region; respect the whole window. + set_mouse(f, p); } - - _has_mouse = true; - - VRegions regions; - get_over_regions(regions, _mouse); - set_current_regions(regions); } - _suppress_flags = 0; + // If the mouse is over a particular region, or still considered + // owned by a region because of a recent button-down event, that + // region determines whether we suppress events below us. if (_preferred_region != (MouseWatcherRegion *)NULL) { _suppress_flags = _preferred_region->get_suppress_flags(); } @@ -920,15 +983,15 @@ do_transmit_data(const DataNodeTransmit &input, DataNodeTransmit &output) { if (_has_mouse && (_suppress_flags & MouseWatcherRegion::SF_mouse_position) == 0) { // Transmit the mouse position. - output.set_data(_xy_output, input.get_data(_xy_input)); - output.set_data(_pixel_xy_output, input.get_data(_pixel_xy_input)); + _xy->set_value(_mouse); + output.set_data(_xy_output, EventParameter(_xy)); + _pixel_xy->set_value(_mouse_pixel); + output.set_data(_pixel_xy_output, EventParameter(_pixel_xy)); } int suppress_buttons = (_suppress_flags & MouseWatcherRegion::SF_any_button); - if (suppress_buttons == MouseWatcherRegion::SF_any_button) { - // Suppress all buttons. - } else if (suppress_buttons != 0) { + if (suppress_buttons != 0) { // Suppress some buttons. _button_events->clear(); @@ -947,7 +1010,7 @@ do_transmit_data(const DataNodeTransmit &input, DataNodeTransmit &output) { suppress = ((suppress_buttons & MouseWatcherRegion::SF_other_button) != 0); } - if (!suppress) { + if (!suppress || be._type == ButtonEvent::T_up) { // Don't suppress this button event; pass it through. _button_events->add_event(be); } @@ -963,4 +1026,3 @@ do_transmit_data(const DataNodeTransmit &input, DataNodeTransmit &output) { output.set_data(_button_events_output, input.get_data(_button_events_input)); } } - diff --git a/panda/src/tform/mouseWatcher.h b/panda/src/tform/mouseWatcher.h index 9db7f5afb1..e40e0b6be1 100644 --- a/panda/src/tform/mouseWatcher.h +++ b/panda/src/tform/mouseWatcher.h @@ -34,6 +34,7 @@ #include "pvector.h" class MouseWatcherParameter; +class DisplayRegion; //////////////////////////////////////////////////////////////////// // Class : MouseWatcher @@ -103,6 +104,11 @@ PUBLISHED: INLINE void set_modifier_buttons(const ModifierButtons &mods); INLINE ModifierButtons get_modifier_buttons() const; + INLINE void set_display_region(DisplayRegion *dr); + INLINE void clear_display_region(); + INLINE DisplayRegion *get_display_region() const; + INLINE bool has_display_region() const; + public: virtual void output(ostream &out) const; virtual void write(ostream &out, int indent_level = 0) const; @@ -141,6 +147,9 @@ protected: void enter_region(MouseWatcherRegion *region, const MouseWatcherParameter ¶m); void exit_region(MouseWatcherRegion *region, const MouseWatcherParameter ¶m); + void set_no_mouse(); + void set_mouse(const LVecBase2f &xy, const LVecBase2f &pixel_xy); + // This wants to be a set, but because you cannot export sets across // dlls in windows, we will make it a vector instead typedef pvector< PT(MouseWatcherGroup) > Groups; @@ -149,6 +158,7 @@ protected: bool _has_mouse; int _suppress_flags; LPoint2f _mouse; + LPoint2f _mouse_pixel; VRegions _current_regions; PT(MouseWatcherRegion) _preferred_region; @@ -168,8 +178,8 @@ protected: PT(PandaNode) _geometry; EventHandler *_eh; - ModifierButtons _mods; + DisplayRegion *_display_region; protected: // Inherited from DataNode