Panda3D
|
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 ©) : 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 }