Panda3D
pgScrollFrame.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file pgScrollFrame.cxx
10  * @author drose
11  * @date 2005-08-17
12  */
13 
14 #include "pgScrollFrame.h"
15 
16 TypeHandle PGScrollFrame::_type_handle;
17 
18 /**
19  *
20  */
21 PGScrollFrame::
22 PGScrollFrame(const std::string &name) :
23  PGVirtualFrame(name),
24  _needs_remanage(false),
25  _needs_recompute_clip(false),
26  _has_virtual_frame(false),
27  _virtual_frame(0.0f, 0.0f, 0.0f, 0.0f),
28  _manage_pieces(false),
29  _auto_hide(false)
30 {
31  _canvas_computed.test_and_set();
32 
33  set_cull_callback();
34 }
35 
36 /**
37  *
38  */
39 PGScrollFrame::
40 ~PGScrollFrame() {
41  set_horizontal_slider(nullptr);
42  set_vertical_slider(nullptr);
43 }
44 
45 /**
46  *
47  */
48 PGScrollFrame::
49 PGScrollFrame(const PGScrollFrame &copy) :
50  PGVirtualFrame(copy),
51  _has_virtual_frame(copy._has_virtual_frame),
52  _virtual_frame(copy._virtual_frame),
53  _manage_pieces(copy._manage_pieces),
54  _auto_hide(copy._auto_hide)
55 {
56  _needs_remanage = false;
57  _needs_recompute_clip = true;
58  _canvas_computed.clear();
59 }
60 
61 /**
62  * Returns a newly-allocated Node that is a shallow copy of this one. It will
63  * be a different Node pointer, but its internal data may or may not be shared
64  * with that of the original Node.
65  */
67 make_copy() const {
68  LightReMutexHolder holder(_lock);
69  return new PGScrollFrame(*this);
70 }
71 
72 /**
73  * This function will be called during the cull traversal to perform any
74  * additional operations that should be performed at cull time. This may
75  * include additional manipulation of render state or additional
76  * visible/invisible decisions, or any other arbitrary operation.
77  *
78  * Note that this function will *not* be called unless set_cull_callback() is
79  * called in the constructor of the derived class. It is necessary to call
80  * set_cull_callback() to indicated that we require cull_callback() to be
81  * called.
82  *
83  * By the time this function is called, the node has already passed the
84  * bounding-volume test for the viewing frustum, and the node's transform and
85  * state have already been applied to the indicated CullTraverserData object.
86  *
87  * The return value is true if this node should be visible, or false if it
88  * should be culled.
89  */
92  LightReMutexHolder holder(_lock);
93  if (_manage_pieces && _needs_remanage) {
94  remanage();
95  }
96  if (_needs_recompute_clip) {
97  recompute_clip();
98  }
99  if (!_canvas_computed.test_and_set()) {
100  recompute_canvas();
101  }
102  return PGVirtualFrame::cull_callback(trav, data);
103 }
104 
105 /**
106  * Transforms the contents of this node by the indicated matrix, if it means
107  * anything to do so. For most kinds of nodes, this does nothing.
108  */
110 xform(const LMatrix4 &mat) {
111  LightReMutexHolder holder(_lock);
112  PGVirtualFrame::xform(mat);
113 
114  _needs_remanage = true;
115  _needs_recompute_clip = true;
116 }
117 
118 /**
119  * Creates a PGScrollFrame with the indicated dimensions, and the indicated
120  * virtual frame.
121  */
123 setup(PN_stdfloat width, PN_stdfloat height,
124  PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_stdfloat top,
125  PN_stdfloat slider_width, PN_stdfloat bevel) {
126  LightReMutexHolder holder(_lock);
127  set_state(0);
128  clear_state_def(0);
129 
130  set_frame(0, width, 0, height);
131 
132  PGFrameStyle style;
133  style.set_width(bevel, bevel);
134 
135  style.set_color(0.8f, 0.8f, 0.8f, 1.0f);
136  style.set_type(PGFrameStyle::T_ridge);
137  set_frame_style(0, style);
138 
139  set_clip_frame(bevel, width - bevel,
140  bevel, height - bevel);
141 
142  set_virtual_frame(left, right, bottom, top);
143 
144  // Remove the slider nodes created by a previous call to setup(), if any.
145  if (_horizontal_slider != nullptr) {
146  remove_child(_horizontal_slider);
147  set_horizontal_slider(nullptr);
148  }
149  if (_vertical_slider != nullptr) {
150  remove_child(_vertical_slider);
151  set_vertical_slider(nullptr);
152  }
153 
154  // Create new slider bars.
155  PT(PGSliderBar) horizontal_slider = new PGSliderBar("horizontal");
156  horizontal_slider->setup_scroll_bar(false, width - slider_width - bevel * 2, slider_width, bevel);
157  horizontal_slider->set_transform(TransformState::make_pos(LVector3::rfu(width / 2.0f - slider_width / 2.0f, 0, slider_width / 2.0f + bevel)));
158  add_child(horizontal_slider);
159  set_horizontal_slider(horizontal_slider);
160 
161  PT(PGSliderBar) vertical_slider = new PGSliderBar("vertical");
162  vertical_slider->setup_scroll_bar(true, width - slider_width - bevel * 2, slider_width, bevel);
163  add_child(vertical_slider);
164  vertical_slider->set_transform(TransformState::make_pos(LVector3::rfu(width - slider_width / 2.0f - bevel, 0, width / 2.0f + slider_width / 2.0f)));
165  set_vertical_slider(vertical_slider);
166 
167  set_manage_pieces(true);
168  set_auto_hide(true);
169 }
170 
171 /**
172  * Manages the position and size of the scroll bars. Normally this should not
173  * need to be called directly.
174  */
176 remanage() {
177  LightReMutexHolder holder(_lock);
178  _needs_remanage = false;
179 
180  const LVecBase4 &frame = get_frame();
181  LVecBase4 clip = get_frame_style(get_state()).get_internal_frame(frame);
182 
183  // Determine which scroll bars we have in the frame.
184 
185  bool got_horizontal = false;
186  PN_stdfloat horizontal_width = 0.0f;
187  if (_horizontal_slider != nullptr) {
188  got_horizontal = true;
189  const LVecBase4 &slider_frame = _horizontal_slider->get_frame();
190  horizontal_width = slider_frame[3] - slider_frame[2];
191  }
192 
193  bool got_vertical = false;
194  PN_stdfloat vertical_width = 0.0f;
195  if (_vertical_slider != nullptr) {
196  got_vertical = true;
197  const LVecBase4 &slider_frame = _vertical_slider->get_frame();
198  vertical_width = slider_frame[1] - slider_frame[0];
199  }
200 
201  if (_auto_hide) {
202  // Should we hide or show either of the scroll bars? TODO.
203 
204  // Start by figuring out what our maximum clipping area will be.
205  PN_stdfloat clip_width = clip[1] - clip[0];
206  PN_stdfloat clip_height = clip[3] - clip[2];
207 
208  // And our virtual frame too.
209  const LVecBase4 &virtual_frame = get_virtual_frame();
210  PN_stdfloat virtual_width = virtual_frame[1] - virtual_frame[0];
211  PN_stdfloat virtual_height = virtual_frame[3] - virtual_frame[2];
212 
213  if (virtual_width <= clip_width &&
214  virtual_height <= clip_height) {
215  // No need for either slider.
216  got_horizontal = false;
217  got_vertical = false;
218 
219  } else {
220  if (virtual_width <= clip_width - vertical_width) {
221  // No need for the horizontal slider.
222  got_horizontal = false;
223  }
224 
225  if (virtual_height <= clip_height - horizontal_width) {
226  // No need for the vertical slider.
227  got_vertical = false;
228 
229  // Now reconsider the need for the horizontal slider.
230  if (virtual_width <= clip_width) {
231  got_horizontal = false;
232  }
233  }
234  }
235 
236  // Now show or hide the sliders appropriately.
237  if (_horizontal_slider != nullptr) {
238  if (got_horizontal) {
239  _horizontal_slider->set_overall_hidden(false);
240  } else {
241  _horizontal_slider->set_overall_hidden(true);
242  _horizontal_slider->set_ratio(0.0f);
243  horizontal_width = 0.0f;
244  }
245  }
246  if (_vertical_slider != nullptr) {
247  if (got_vertical) {
248  _vertical_slider->set_overall_hidden(false);
249  } else {
250  _vertical_slider->set_overall_hidden(true);
251  _vertical_slider->set_ratio(0.0f);
252  vertical_width = 0.0f;
253  }
254  }
255 
256  // Showing or hiding one of the scroll bars might have set this flag again
257  // indirectly; we clear it again to avoid a feedback loop.
258  _needs_remanage = false;
259  }
260 
261  // Are either or both of the scroll bars hidden?
262  if (got_horizontal && _horizontal_slider->is_overall_hidden()) {
263  got_horizontal = false;
264  horizontal_width = 0.0f;
265  }
266  if (got_vertical && _vertical_slider->is_overall_hidden()) {
267  got_vertical = false;
268  vertical_width = 0.0f;
269  }
270 
271  if (got_horizontal) {
272  _horizontal_slider->set_frame(clip[0], clip[1] - vertical_width,
273  clip[2], clip[2] + horizontal_width);
274  _horizontal_slider->clear_transform();
275  }
276  if (got_vertical) {
277  _vertical_slider->set_frame(clip[1] - vertical_width, clip[1],
278  clip[2] + horizontal_width, clip[3]);
279  _vertical_slider->clear_transform();
280  }
281 
282 
283  recompute();
284 }
285 
286 /**
287  * Called when the user changes the frame size. Assumes the lock is held.
288  */
289 void PGScrollFrame::
290 frame_changed() {
291  PGVirtualFrame::frame_changed();
292  _needs_remanage = true;
293  _needs_recompute_clip = true;
294 }
295 
296 /**
297  * Called whenever a watched PGItem's local transform has been changed.
298  */
299 void PGScrollFrame::
300 item_transform_changed(PGItem *) {
301  LightReMutexHolder holder(_lock);
302  _needs_recompute_clip = true;
303 }
304 
305 /**
306  * Called whenever a watched PGItem's frame has been changed.
307  */
308 void PGScrollFrame::
309 item_frame_changed(PGItem *) {
310  LightReMutexHolder holder(_lock);
311  _needs_recompute_clip = true;
312 }
313 
314 /**
315  * Called whenever a watched PGItem's draw_mask has been changed.
316  */
317 void PGScrollFrame::
318 item_draw_mask_changed(PGItem *) {
319  LightReMutexHolder holder(_lock);
320  _needs_remanage = true;
321  _needs_recompute_clip = true;
322 }
323 
324 /**
325  * Called whenever a watched PGSliderBar's value has been changed by the user
326  * or programmatically.
327  */
328 void PGScrollFrame::
329 slider_bar_adjust(PGSliderBar *) {
330  // Indicate that recompute_canvas() needs to be called.
331  _canvas_computed.clear();
332 }
333 
334 /**
335  * Recomputes the clipping window of the PGScrollFrame, based on the position
336  * of the slider bars.
337  *
338  * Assumes the lock is held.
339  */
340 void PGScrollFrame::
341 recompute_clip() {
342  _needs_recompute_clip = false;
343  _canvas_computed.clear();
344 
345  // Figure out how to remove the scroll bars from the clip region.
346  LVecBase4 clip = get_frame_style(get_state()).get_internal_frame(get_frame());
347  reduce_region(clip, _horizontal_slider);
348  reduce_region(clip, _vertical_slider);
349 
350  set_clip_frame(clip);
351 
352  if (_horizontal_slider != nullptr) {
353  _horizontal_slider->set_page_size((clip[1] - clip[0]) / (_virtual_frame[1] - _virtual_frame[0]));
354  }
355  if (_vertical_slider != nullptr) {
356  _vertical_slider->set_page_size((clip[3] - clip[2]) / (_virtual_frame[3] - _virtual_frame[2]));
357  }
358 }
359 
360 /**
361  * Recomputes the portion of the virtual canvas that is visible within the
362  * PGScrollFrame, based on the values of the slider bars.
363  *
364  * Assumes the lock is held.
365  */
366 void PGScrollFrame::
367 recompute_canvas() {
368  const LVecBase4 &clip = _has_clip_frame ? _clip_frame : get_frame();
369 
370  // Set this to true before we sample the slider bar ratios.
371  // If slider_bar_adjust happens to get called while we do this, no big deal,
372  // this method will just be called again next frame.
373  _canvas_computed.test_and_set();
374 
375  PN_stdfloat cx, cy;
376  cx = interpolate_canvas(clip[0], clip[1],
377  _virtual_frame[0], _virtual_frame[1],
378  _horizontal_slider);
379 
380  cy = interpolate_canvas(clip[3], clip[2],
381  _virtual_frame[3], _virtual_frame[2],
382  _vertical_slider);
383 
384  _canvas_node->set_transform(TransformState::make_pos(LVector3::rfu(cx, 0, cy)));
385 }
386 
387 /**
388  * Computes the linear translation that should be applied to the virtual
389  * canvas node, based on the corresponding slider bar's position.
390  *
391  * Assumes the lock is held.
392  */
393 PN_stdfloat PGScrollFrame::
394 interpolate_canvas(PN_stdfloat clip_min, PN_stdfloat clip_max,
395  PN_stdfloat canvas_min, PN_stdfloat canvas_max,
396  PGSliderBar *slider_bar) {
397  PN_stdfloat t = 0.0f;
398  if (slider_bar != nullptr) {
399  t = slider_bar->get_ratio();
400  }
401 
402  PN_stdfloat min = clip_min - canvas_min;
403  PN_stdfloat max = clip_max - canvas_max;
404 
405  return min + t * (max - min);
406 }
This collects together the pieces of data that are accumulated for each node while walking the scene ...
This object performs a depth-first traversal of the scene graph, with optional view-frustum culling,...
Definition: cullTraverser.h:45
Similar to MutexHolder, but for a light reentrant mutex.
void set_type(Type type)
Sets the basic type of frame.
Definition: pgFrameStyle.I:64
void set_color(PN_stdfloat r, PN_stdfloat g, PN_stdfloat b, PN_stdfloat a)
Sets the dominant color of the frame.
Definition: pgFrameStyle.I:80
LVecBase4 get_internal_frame(const LVecBase4 &frame) const
Computes the size of the internal frame, given the indicated external frame, appropriate for this kin...
void set_width(PN_stdfloat x, PN_stdfloat y)
Sets the width parameter, which has meaning only for certain frame types.
Definition: pgFrameStyle.I:139
This is the base class for all the various kinds of gui widget objects.
Definition: pgItem.h:53
const LVecBase4 & get_frame() const
Returns the bounding rectangle of the item.
Definition: pgItem.I:106
void set_frame_style(int state, const PGFrameStyle &style)
Changes the kind of frame that will be drawn behind the item when it is in the indicated state.
Definition: pgItem.cxx:1007
void clear_state_def(int state)
Resets the NodePath assigned to the indicated state to its initial default, with only a frame represe...
Definition: pgItem.cxx:953
void set_frame(PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_stdfloat top)
Sets the bounding rectangle of the item, in local coordinates.
Definition: pgItem.I:81
PGFrameStyle get_frame_style(int state)
Returns the kind of frame that will be drawn behind the item when it is in the indicated state.
Definition: pgItem.cxx:994
int get_state() const
Returns the "state" of this particular PGItem.
Definition: pgItem.I:150
This is a special kind of frame that pretends to be much larger than it actually is.
Definition: pgScrollFrame.h:39
void set_manage_pieces(bool manage_pieces)
Sets the manage_pieces flag.
Definition: pgScrollFrame.I:79
virtual PandaNode * make_copy() const
Returns a newly-allocated Node that is a shallow copy of this one.
virtual void xform(const LMatrix4 &mat)
Transforms the contents of this node by the indicated matrix, if it means anything to do so.
void set_virtual_frame(PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_stdfloat top)
Sets the bounding rectangle of the virtual frame.
Definition: pgScrollFrame.I:19
void setup(PN_stdfloat width, PN_stdfloat height, PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_stdfloat top, PN_stdfloat slider_width, PN_stdfloat bevel)
Creates a PGScrollFrame with the indicated dimensions, and the indicated virtual frame.
const LVecBase4 & get_virtual_frame() const
Returns the bounding rectangle of the virtual frame.
Definition: pgScrollFrame.I:45
void set_auto_hide(bool auto_hide)
Sets the auto_hide flag.
void set_horizontal_slider(PGSliderBar *horizontal_slider)
Sets the PGSliderBar object that will serve as the horizontal scroll bar for this frame.
void remanage()
Manages the position and size of the scroll bars.
void recompute()
Forces the PGScrollFrame to recompute itself right now.
virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data)
This function will be called during the cull traversal to perform any additional operations that shou...
void set_vertical_slider(PGSliderBar *vertical_slider)
Sets the PGSliderBar object that will serve as the vertical scroll bar for this frame.
This is a particular kind of PGItem that draws a little bar with a slider that moves from left to rig...
Definition: pgSliderBar.h:31
PN_stdfloat get_ratio() const
Returns the current value of the slider, expressed in the range 0 .
Definition: pgSliderBar.I:174
This represents a frame that is rendered as a window onto another (possibly much larger) canvas.
void set_clip_frame(PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_stdfloat top)
Sets the bounding rectangle of the clip frame.
A basic node of the scene graph or data graph.
Definition: pandaNode.h:65
void remove_child(int child_index, Thread *current_thread=Thread::get_current_thread())
Removes the nth child from the node.
Definition: pandaNode.cxx:564
set_state
Sets the complete RenderState that will be applied to all nodes at this level and below.
Definition: pandaNode.h:173
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.