Panda3D

pgScrollFrame.cxx

00001 // Filename: pgScrollFrame.cxx
00002 // Created by:  drose (17Aug05)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "pgScrollFrame.h"
00016 
00017 TypeHandle PGScrollFrame::_type_handle;
00018 
00019 ////////////////////////////////////////////////////////////////////
00020 //     Function: PGScrollFrame::Constructor
00021 //       Access: Published
00022 //  Description: 
00023 ////////////////////////////////////////////////////////////////////
00024 PGScrollFrame::
00025 PGScrollFrame(const string &name) : PGVirtualFrame(name)
00026 {
00027   set_cull_callback();
00028 
00029   _needs_remanage = false;
00030   _needs_recompute_canvas = false;
00031   _needs_recompute_clip = false;
00032   _has_virtual_frame = false;
00033   _virtual_frame.set(0.0f, 0.0f, 0.0f, 0.0f);
00034   _manage_pieces = false;
00035   _auto_hide = false;
00036   _horizontal_slider = NULL;
00037   _vertical_slider = NULL;
00038 }
00039 
00040 ////////////////////////////////////////////////////////////////////
00041 //     Function: PGScrollFrame::Destructor
00042 //       Access: Public, Virtual
00043 //  Description: 
00044 ////////////////////////////////////////////////////////////////////
00045 PGScrollFrame::
00046 ~PGScrollFrame() {
00047   set_horizontal_slider(NULL);
00048   set_vertical_slider(NULL);
00049 }
00050 
00051 ////////////////////////////////////////////////////////////////////
00052 //     Function: PGScrollFrame::Copy Constructor
00053 //       Access: Protected
00054 //  Description: 
00055 ////////////////////////////////////////////////////////////////////
00056 PGScrollFrame::
00057 PGScrollFrame(const PGScrollFrame &copy) :
00058   PGVirtualFrame(copy),
00059   _has_virtual_frame(copy._has_virtual_frame),
00060   _virtual_frame(copy._virtual_frame),
00061   _manage_pieces(copy._manage_pieces),
00062   _auto_hide(copy._auto_hide)
00063 {
00064   _needs_remanage = false;
00065   _needs_recompute_canvas = true;
00066   _needs_recompute_clip = true;
00067 }
00068 
00069 ////////////////////////////////////////////////////////////////////
00070 //     Function: PGScrollFrame::make_copy
00071 //       Access: Public, Virtual
00072 //  Description: Returns a newly-allocated Node that is a shallow copy
00073 //               of this one.  It will be a different Node pointer,
00074 //               but its internal data may or may not be shared with
00075 //               that of the original Node.
00076 ////////////////////////////////////////////////////////////////////
00077 PandaNode *PGScrollFrame::
00078 make_copy() const {
00079   LightReMutexHolder holder(_lock);
00080   return new PGScrollFrame(*this);
00081 }
00082 
00083 ////////////////////////////////////////////////////////////////////
00084 //     Function: PGScrollFrame::cull_callback
00085 //       Access: Protected, Virtual
00086 //  Description: This function will be called during the cull
00087 //               traversal to perform any additional operations that
00088 //               should be performed at cull time.  This may include
00089 //               additional manipulation of render state or additional
00090 //               visible/invisible decisions, or any other arbitrary
00091 //               operation.
00092 //
00093 //               Note that this function will *not* be called unless
00094 //               set_cull_callback() is called in the constructor of
00095 //               the derived class.  It is necessary to call
00096 //               set_cull_callback() to indicated that we require
00097 //               cull_callback() to be called.
00098 //
00099 //               By the time this function is called, the node has
00100 //               already passed the bounding-volume test for the
00101 //               viewing frustum, and the node's transform and state
00102 //               have already been applied to the indicated
00103 //               CullTraverserData object.
00104 //
00105 //               The return value is true if this node should be
00106 //               visible, or false if it should be culled.
00107 ////////////////////////////////////////////////////////////////////
00108 bool PGScrollFrame::
00109 cull_callback(CullTraverser *trav, CullTraverserData &data) {
00110   LightReMutexHolder holder(_lock);
00111   if (_manage_pieces && _needs_remanage) {
00112     remanage();
00113   }
00114   if (_needs_recompute_clip) {
00115     recompute_clip();
00116   }
00117   if (_needs_recompute_canvas) {
00118     recompute_canvas();
00119   }
00120   return PGVirtualFrame::cull_callback(trav, data);
00121 }
00122 
00123 ////////////////////////////////////////////////////////////////////
00124 //     Function: PGScrollFrame::xform
00125 //       Access: Public, Virtual
00126 //  Description: Transforms the contents of this node by the indicated
00127 //               matrix, if it means anything to do so.  For most
00128 //               kinds of nodes, this does nothing.
00129 ////////////////////////////////////////////////////////////////////
00130 void PGScrollFrame::
00131 xform(const LMatrix4 &mat) {
00132   LightReMutexHolder holder(_lock);
00133   PGVirtualFrame::xform(mat);
00134 
00135   _needs_remanage = true;
00136   _needs_recompute_clip = true;
00137 }
00138   
00139 ////////////////////////////////////////////////////////////////////
00140 //     Function: PGScrollFrame::setup
00141 //       Access: Published
00142 //  Description: Creates a PGScrollFrame with the indicated 
00143 //               dimensions, and the indicated virtual frame.
00144 ////////////////////////////////////////////////////////////////////
00145 void PGScrollFrame::
00146 setup(PN_stdfloat width, PN_stdfloat height,
00147       PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_stdfloat top,
00148       PN_stdfloat slider_width, PN_stdfloat bevel) {
00149   LightReMutexHolder holder(_lock);
00150   set_state(0);
00151   clear_state_def(0);
00152 
00153   set_frame(0, width, 0, height);
00154 
00155   PGFrameStyle style;
00156   style.set_width(bevel, bevel);
00157 
00158   style.set_color(0.8f, 0.8f, 0.8f, 1.0f);
00159   style.set_type(PGFrameStyle::T_ridge);
00160   set_frame_style(0, style);
00161 
00162   set_clip_frame(bevel, width - bevel,
00163                  bevel, height - bevel);
00164 
00165   set_virtual_frame(left, right, bottom, top);
00166 
00167   // Remove the slider nodes created by a previous call to setup(), if
00168   // any.
00169   if (_horizontal_slider != (PGSliderBar *)NULL) {
00170     remove_child(_horizontal_slider);
00171     set_horizontal_slider(NULL);
00172   }
00173   if (_vertical_slider != (PGSliderBar *)NULL) {
00174     remove_child(_vertical_slider);
00175     set_vertical_slider(NULL);
00176   }
00177 
00178   // Create new slider bars.
00179   PT(PGSliderBar) horizontal_slider = new PGSliderBar("horizontal");
00180   horizontal_slider->setup_scroll_bar(false, width - slider_width - bevel * 2, slider_width, bevel);
00181   horizontal_slider->set_transform(TransformState::make_pos(LVector3::rfu(width / 2.0f - slider_width / 2.0f, 0, slider_width / 2.0f + bevel)));
00182   add_child(horizontal_slider);
00183   set_horizontal_slider(horizontal_slider);
00184 
00185   PT(PGSliderBar) vertical_slider = new PGSliderBar("vertical");
00186   vertical_slider->setup_scroll_bar(true, width - slider_width - bevel * 2, slider_width, bevel);
00187   add_child(vertical_slider);
00188   vertical_slider->set_transform(TransformState::make_pos(LVector3::rfu(width - slider_width / 2.0f - bevel, 0, width / 2.0f + slider_width / 2.0f)));
00189   set_vertical_slider(vertical_slider);
00190 
00191   set_manage_pieces(true);
00192   set_auto_hide(true);
00193 }
00194 
00195 ////////////////////////////////////////////////////////////////////
00196 //     Function: PGScrollFrame::remanage
00197 //       Access: Published
00198 //  Description: Manages the position and size of the scroll bars.
00199 //               Normally this should not need to be called directly.
00200 ////////////////////////////////////////////////////////////////////
00201 void PGScrollFrame::
00202 remanage() {
00203   LightReMutexHolder holder(_lock);
00204   _needs_remanage = false;
00205 
00206   const LVecBase4 &frame = get_frame();
00207   LVecBase4 clip = get_frame_style(get_state()).get_internal_frame(frame);
00208 
00209   // Determine which scroll bars we have in the frame.
00210   
00211   bool got_horizontal = false;
00212   PN_stdfloat horizontal_width = 0.0f;
00213   if (_horizontal_slider != (PGSliderBar *)NULL) {
00214     got_horizontal = true;
00215     const LVecBase4 &slider_frame = _horizontal_slider->get_frame();
00216     horizontal_width = slider_frame[3] - slider_frame[2];
00217   }
00218   
00219   bool got_vertical = false;
00220   PN_stdfloat vertical_width = 0.0f;
00221   if (_vertical_slider != (PGSliderBar *)NULL) {
00222     got_vertical = true;
00223     const LVecBase4 &slider_frame = _vertical_slider->get_frame();
00224     vertical_width = slider_frame[1] - slider_frame[0];
00225   }
00226 
00227   if (_auto_hide) {
00228     // Should we hide or show either of the scroll bars?  TODO.
00229 
00230     // Start by figuring out what our maximum clipping area will be.
00231     PN_stdfloat clip_width = clip[1] - clip[0];
00232     PN_stdfloat clip_height = clip[3] - clip[2];
00233 
00234     // And our virtual frame too.
00235     const LVecBase4 &virtual_frame = get_virtual_frame();
00236     PN_stdfloat virtual_width = virtual_frame[1] - virtual_frame[0];
00237     PN_stdfloat virtual_height = virtual_frame[3] - virtual_frame[2];
00238 
00239     if (virtual_width <= clip_width &&
00240         virtual_height <= clip_height) {
00241       // No need for either slider.
00242       got_horizontal = false;
00243       got_vertical = false;
00244 
00245     } else {
00246       if (virtual_width <= clip_width - vertical_width) {
00247         // No need for the horizontal slider.
00248         got_horizontal = false;
00249       }
00250       
00251       if (virtual_height <= clip_height - horizontal_width) {
00252         // No need for the vertical slider.
00253         got_vertical = false;
00254         
00255         // Now reconsider the need for the horizontal slider.
00256         if (virtual_width <= clip_width) {
00257           got_horizontal = false;
00258         }
00259       }
00260     }
00261 
00262     // Now show or hide the sliders appropriately.
00263     if (_horizontal_slider != (PGSliderBar *)NULL) {
00264       if (got_horizontal) {
00265         _horizontal_slider->set_overall_hidden(false);
00266       } else {
00267         _horizontal_slider->set_overall_hidden(true);
00268         _horizontal_slider->set_ratio(0.0f);
00269         horizontal_width = 0.0f;
00270       }
00271     }
00272     if (_vertical_slider != (PGSliderBar *)NULL) {
00273       if (got_vertical) {
00274         _vertical_slider->set_overall_hidden(false);
00275       } else {
00276         _vertical_slider->set_overall_hidden(true);
00277         _vertical_slider->set_ratio(0.0f);
00278         vertical_width = 0.0f;
00279       }
00280     }
00281 
00282     // Showing or hiding one of the scroll bars might have set this
00283     // flag again indirectly; we clear it again to avoid a feedback
00284     // loop.
00285     _needs_remanage = false;
00286 }
00287 
00288   // Are either or both of the scroll bars hidden?
00289   if (got_horizontal && _horizontal_slider->is_overall_hidden()) {
00290     got_horizontal = false;
00291     horizontal_width = 0.0f;
00292   }
00293   if (got_vertical && _vertical_slider->is_overall_hidden()) {
00294     got_vertical = false;
00295     vertical_width = 0.0f;
00296   }
00297 
00298   if (got_horizontal) {
00299     _horizontal_slider->set_frame(clip[0], clip[1] - vertical_width,
00300                                   clip[2], clip[2] + horizontal_width);
00301     _horizontal_slider->clear_transform();
00302   }
00303   if (got_vertical) {
00304     _vertical_slider->set_frame(clip[1] - vertical_width, clip[1],
00305                                 clip[2] + horizontal_width, clip[3]);
00306     _vertical_slider->clear_transform();
00307   }
00308     
00309 
00310   recompute();
00311 }
00312 
00313 ////////////////////////////////////////////////////////////////////
00314 //     Function: PGScrollFrame::frame_changed
00315 //       Access: Protected, Virtual
00316 //  Description: Called when the user changes the frame size.
00317 ////////////////////////////////////////////////////////////////////
00318 void PGScrollFrame::
00319 frame_changed() {
00320   LightReMutexHolder holder(_lock);
00321   PGVirtualFrame::frame_changed();
00322   _needs_remanage = true;
00323   _needs_recompute_clip = true;
00324 }
00325 
00326 ////////////////////////////////////////////////////////////////////
00327 //     Function: PGScrollFrame::item_transform_changed
00328 //       Access: Protected, Virtual
00329 //  Description: Called whenever a watched PGItem's local transform
00330 //               has been changed.
00331 ////////////////////////////////////////////////////////////////////
00332 void PGScrollFrame::
00333 item_transform_changed(PGItem *) {
00334   LightReMutexHolder holder(_lock);
00335   _needs_recompute_clip = true;
00336 }
00337 
00338 ////////////////////////////////////////////////////////////////////
00339 //     Function: PGScrollFrame::item_frame_changed
00340 //       Access: Protected, Virtual
00341 //  Description: Called whenever a watched PGItem's frame
00342 //               has been changed.
00343 ////////////////////////////////////////////////////////////////////
00344 void PGScrollFrame::
00345 item_frame_changed(PGItem *) {
00346   LightReMutexHolder holder(_lock);
00347   _needs_recompute_clip = true;
00348 }
00349 
00350 ////////////////////////////////////////////////////////////////////
00351 //     Function: PGScrollFrame::item_draw_mask_changed
00352 //       Access: Protected, Virtual
00353 //  Description: Called whenever a watched PGItem's draw_mask
00354 //               has been changed.
00355 ////////////////////////////////////////////////////////////////////
00356 void PGScrollFrame::
00357 item_draw_mask_changed(PGItem *) {
00358   LightReMutexHolder holder(_lock);
00359   _needs_remanage = true;
00360   _needs_recompute_clip = true;
00361 }
00362 
00363 ////////////////////////////////////////////////////////////////////
00364 //     Function: PGScrollFrame::slider_bar_adjust
00365 //       Access: Protected, Virtual
00366 //  Description: Called whenever a watched PGSliderBar's value
00367 //               has been changed by the user or programmatically.
00368 ////////////////////////////////////////////////////////////////////
00369 void PGScrollFrame::
00370 slider_bar_adjust(PGSliderBar *) {
00371   LightReMutexHolder holder(_lock);
00372   _needs_recompute_canvas = true;
00373 }
00374 
00375 ////////////////////////////////////////////////////////////////////
00376 //     Function: PGScrollFrame::recompute_clip
00377 //       Access: Private
00378 //  Description: Recomputes the clipping window of the PGScrollFrame,
00379 //               based on the position of the slider bars.
00380 ////////////////////////////////////////////////////////////////////
00381 void PGScrollFrame::
00382 recompute_clip() {
00383   LightReMutexHolder holder(_lock);
00384   _needs_recompute_clip = false;
00385   _needs_recompute_canvas = true;
00386 
00387   // Figure out how to remove the scroll bars from the clip region.
00388   LVecBase4 clip = get_frame_style(get_state()).get_internal_frame(get_frame());
00389   reduce_region(clip, _horizontal_slider);
00390   reduce_region(clip, _vertical_slider);
00391 
00392   set_clip_frame(clip);
00393 
00394   if (_horizontal_slider != (PGSliderBar *)NULL) {
00395     _horizontal_slider->set_page_size((clip[1] - clip[0]) / (_virtual_frame[1] - _virtual_frame[0]));
00396   }
00397   if (_vertical_slider != (PGSliderBar *)NULL) {
00398     _vertical_slider->set_page_size((clip[3] - clip[2]) / (_virtual_frame[3] - _virtual_frame[2]));
00399   }
00400 }
00401 
00402 ////////////////////////////////////////////////////////////////////
00403 //     Function: PGScrollFrame::recompute_canvas
00404 //       Access: Private
00405 //  Description: Recomputes the portion of the virtual canvas that is
00406 //               visible within the PGScrollFrame, based on the values
00407 //               of the slider bars.
00408 ////////////////////////////////////////////////////////////////////
00409 void PGScrollFrame::
00410 recompute_canvas() {
00411   LightReMutexHolder holder(_lock);
00412   _needs_recompute_canvas = false;
00413 
00414   const LVecBase4 &clip = get_clip_frame();
00415 
00416   PN_stdfloat x = interpolate_canvas(clip[0], clip[1], 
00417                                _virtual_frame[0], _virtual_frame[1],
00418                                _horizontal_slider);
00419 
00420   PN_stdfloat y = interpolate_canvas(clip[3], clip[2], 
00421                                _virtual_frame[3], _virtual_frame[2],
00422                                _vertical_slider);
00423 
00424   get_canvas_node()->set_transform(TransformState::make_pos(LVector3::rfu(x, 0, y)));
00425 }
00426 
00427 ////////////////////////////////////////////////////////////////////
00428 //     Function: PGScrollFrame::interpolate_canvas
00429 //       Access: Private
00430 //  Description: Computes the linear translation that should be
00431 //               applied to the virtual canvas node, based on the
00432 //               corresponding slider bar's position.
00433 ////////////////////////////////////////////////////////////////////
00434 PN_stdfloat PGScrollFrame::
00435 interpolate_canvas(PN_stdfloat clip_min, PN_stdfloat clip_max, 
00436                    PN_stdfloat canvas_min, PN_stdfloat canvas_max,
00437                    PGSliderBar *slider_bar) {
00438   LightReMutexHolder holder(_lock);
00439   PN_stdfloat t = 0.0f;
00440   if (slider_bar != (PGSliderBar *)NULL) {
00441     t = slider_bar->get_ratio();
00442   }
00443 
00444   PN_stdfloat min = clip_min - canvas_min;
00445   PN_stdfloat max = clip_max - canvas_max;
00446 
00447   return min + t * (max - min);
00448 }
 All Classes Functions Variables Enumerations