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  */
90 bool PGScrollFrame::
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  */
109 void PGScrollFrame::
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  */
122 void PGScrollFrame::
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  */
175 void PGScrollFrame::
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.
288  */
289 void PGScrollFrame::
290 frame_changed() {
291  LightReMutexHolder holder(_lock);
292  PGVirtualFrame::frame_changed();
293  _needs_remanage = true;
294  _needs_recompute_clip = true;
295 }
296 
297 /**
298  * Called whenever a watched PGItem's local transform has been changed.
299  */
300 void PGScrollFrame::
301 item_transform_changed(PGItem *) {
302  LightReMutexHolder holder(_lock);
303  _needs_recompute_clip = true;
304 }
305 
306 /**
307  * Called whenever a watched PGItem's frame has been changed.
308  */
309 void PGScrollFrame::
310 item_frame_changed(PGItem *) {
311  LightReMutexHolder holder(_lock);
312  _needs_recompute_clip = true;
313 }
314 
315 /**
316  * Called whenever a watched PGItem's draw_mask has been changed.
317  */
318 void PGScrollFrame::
319 item_draw_mask_changed(PGItem *) {
320  LightReMutexHolder holder(_lock);
321  _needs_remanage = true;
322  _needs_recompute_clip = true;
323 }
324 
325 /**
326  * Called whenever a watched PGSliderBar's value has been changed by the user
327  * or programmatically.
328  */
329 void PGScrollFrame::
330 slider_bar_adjust(PGSliderBar *) {
331  // Indicate that recompute_canvas() needs to be called.
332  _canvas_computed.clear();
333 }
334 
335 /**
336  * Recomputes the clipping window of the PGScrollFrame, based on the position
337  * of the slider bars.
338  *
339  * Assumes the lock is held.
340  */
341 void PGScrollFrame::
342 recompute_clip() {
343  _needs_recompute_clip = false;
344  _canvas_computed.clear();
345 
346  // Figure out how to remove the scroll bars from the clip region.
347  LVecBase4 clip = get_frame_style(get_state()).get_internal_frame(get_frame());
348  reduce_region(clip, _horizontal_slider);
349  reduce_region(clip, _vertical_slider);
350 
351  set_clip_frame(clip);
352 
353  if (_horizontal_slider != nullptr) {
354  _horizontal_slider->set_page_size((clip[1] - clip[0]) / (_virtual_frame[1] - _virtual_frame[0]));
355  }
356  if (_vertical_slider != nullptr) {
357  _vertical_slider->set_page_size((clip[3] - clip[2]) / (_virtual_frame[3] - _virtual_frame[2]));
358  }
359 }
360 
361 /**
362  * Recomputes the portion of the virtual canvas that is visible within the
363  * PGScrollFrame, based on the values of the slider bars.
364  *
365  * Assumes the lock is held.
366  */
367 void PGScrollFrame::
368 recompute_canvas() {
369  const LVecBase4 &clip = _has_clip_frame ? _clip_frame : get_frame();
370 
371  // Set this to true before we sample the slider bar ratios.
372  // If slider_bar_adjust happens to get called while we do this, no big deal,
373  // this method will just be called again next frame.
374  _canvas_computed.test_and_set();
375 
376  PN_stdfloat cx, cy;
377  cx = interpolate_canvas(clip[0], clip[1],
378  _virtual_frame[0], _virtual_frame[1],
379  _horizontal_slider);
380 
381  cy = interpolate_canvas(clip[3], clip[2],
382  _virtual_frame[3], _virtual_frame[2],
383  _vertical_slider);
384 
385  _canvas_node->set_transform(TransformState::make_pos(LVector3::rfu(cx, 0, cy)));
386 }
387 
388 /**
389  * Computes the linear translation that should be applied to the virtual
390  * canvas node, based on the corresponding slider bar's position.
391  *
392  * Assumes the lock is held.
393  */
394 PN_stdfloat PGScrollFrame::
395 interpolate_canvas(PN_stdfloat clip_min, PN_stdfloat clip_max,
396  PN_stdfloat canvas_min, PN_stdfloat canvas_max,
397  PGSliderBar *slider_bar) {
398  PN_stdfloat t = 0.0f;
399  if (slider_bar != nullptr) {
400  t = slider_bar->get_ratio();
401  }
402 
403  PN_stdfloat min = clip_min - canvas_min;
404  PN_stdfloat max = clip_max - canvas_max;
405 
406  return min + t * (max - min);
407 }
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 recompute()
Forces the PGScrollFrame to recompute itself right now.
A basic node of the scene graph or data graph.
Definition: pandaNode.h:64
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
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:979
void remove_child(int child_index, Thread *current_thread=Thread::get_current_thread())
Removes the nth child from the node.
Definition: pandaNode.cxx:570
This is the base class for all the various kinds of gui widget objects.
Definition: pgItem.h:53
int get_state() const
Returns the "state" of this particular PGItem.
Definition: pgItem.I:150
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.
void set_type(Type type)
Sets the basic type of frame.
Definition: pgFrameStyle.I:64
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
This collects together the pieces of data that are accumulated for each node while walking the scene ...
const LVecBase4 & get_virtual_frame() const
Returns the bounding rectangle of the virtual frame.
Definition: pgScrollFrame.I:45
virtual PandaNode * make_copy() const
Returns a newly-allocated Node that is a shallow copy of this one.
void set_manage_pieces(bool manage_pieces)
Sets the manage_pieces flag.
Definition: pgScrollFrame.I:79
void set_vertical_slider(PGSliderBar *vertical_slider)
Sets the PGSliderBar object that will serve as the vertical scroll bar for this frame.
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_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:992
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Similar to MutexHolder, but for a light reentrant mutex.
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_state(int state)
Sets the "state" of this particular PGItem.
Definition: pgItem.I:141
void set_auto_hide(bool auto_hide)
Sets the auto_hide flag.
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:944
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 remanage()
Manages the position and size of the scroll bars.
const LVecBase4 & get_frame() const
Returns the bounding rectangle of the item.
Definition: pgItem.I:106
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
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.
PN_stdfloat get_ratio() const
Returns the current value of the slider, expressed in the range 0 .
Definition: pgSliderBar.I:174
This is a special kind of frame that pretends to be much larger than it actually is.
Definition: pgScrollFrame.h:39
This object performs a depth-first traversal of the scene graph, with optional view-frustum culling,...
Definition: cullTraverser.h:45
void set_horizontal_slider(PGSliderBar *horizontal_slider)
Sets the PGSliderBar object that will serve as the horizontal 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