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 */
66 PandaNode *PGScrollFrame::
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::
91 cull_callback(CullTraverser *trav, CullTraverserData &data) {
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::
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 }
CullTraverserData
This collects together the pieces of data that are accumulated for each node while walking the scene ...
Definition: cullTraverserData.h:40
CullTraverser
This object performs a depth-first traversal of the scene graph, with optional view-frustum culling,...
Definition: cullTraverser.h:45
LightReMutexHolder
Similar to MutexHolder, but for a light reentrant mutex.
Definition: lightReMutexHolder.h:25
PGFrameStyle
Definition: pgFrameStyle.h:29
PGFrameStyle::set_type
void set_type(Type type)
Sets the basic type of frame.
Definition: pgFrameStyle.I:64
PGFrameStyle::set_color
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
PGFrameStyle::get_internal_frame
LVecBase4 get_internal_frame(const LVecBase4 &frame) const
Computes the size of the internal frame, given the indicated external frame, appropriate for this kin...
Definition: pgFrameStyle.cxx:70
PGFrameStyle::set_width
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
PGItem
This is the base class for all the various kinds of gui widget objects.
Definition: pgItem.h:53
PGItem::get_frame
const LVecBase4 & get_frame() const
Returns the bounding rectangle of the item.
Definition: pgItem.I:106
PGItem::set_frame_style
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
PGItem::clear_state_def
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
PGItem::set_frame
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
PGItem::get_frame_style
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
PGItem::get_state
int get_state() const
Returns the "state" of this particular PGItem.
Definition: pgItem.I:150
PGScrollFrame
This is a special kind of frame that pretends to be much larger than it actually is.
Definition: pgScrollFrame.h:39
PGScrollFrame::set_manage_pieces
void set_manage_pieces(bool manage_pieces)
Sets the manage_pieces flag.
Definition: pgScrollFrame.I:79
PGScrollFrame::make_copy
virtual PandaNode * make_copy() const
Returns a newly-allocated Node that is a shallow copy of this one.
Definition: pgScrollFrame.cxx:67
PGScrollFrame::xform
virtual void xform(const LMatrix4 &mat)
Transforms the contents of this node by the indicated matrix, if it means anything to do so.
Definition: pgScrollFrame.cxx:110
PGScrollFrame::set_virtual_frame
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
PGScrollFrame::setup
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.
Definition: pgScrollFrame.cxx:123
PGScrollFrame::get_virtual_frame
const LVecBase4 & get_virtual_frame() const
Returns the bounding rectangle of the virtual frame.
Definition: pgScrollFrame.I:45
PGScrollFrame::set_auto_hide
void set_auto_hide(bool auto_hide)
Sets the auto_hide flag.
Definition: pgScrollFrame.I:104
PGScrollFrame::set_horizontal_slider
void set_horizontal_slider(PGSliderBar *horizontal_slider)
Sets the PGSliderBar object that will serve as the horizontal scroll bar for this frame.
Definition: pgScrollFrame.I:128
PGScrollFrame::remanage
void remanage()
Manages the position and size of the scroll bars.
Definition: pgScrollFrame.cxx:176
PGScrollFrame::recompute
void recompute()
Forces the PGScrollFrame to recompute itself right now.
Definition: pgScrollFrame.I:201
PGScrollFrame::cull_callback
virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data)
This function will be called during the cull traversal to perform any additional operations that shou...
Definition: pgScrollFrame.cxx:91
PGScrollFrame::set_vertical_slider
void set_vertical_slider(PGSliderBar *vertical_slider)
Sets the PGSliderBar object that will serve as the vertical scroll bar for this frame.
Definition: pgScrollFrame.I:165
PGSliderBar
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
PGSliderBar::get_ratio
PN_stdfloat get_ratio() const
Returns the current value of the slider, expressed in the range 0 .
Definition: pgSliderBar.I:174
PGVirtualFrame
This represents a frame that is rendered as a window onto another (possibly much larger) canvas.
Definition: pgVirtualFrame.h:43
PGVirtualFrame::set_clip_frame
void set_clip_frame(PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_stdfloat top)
Sets the bounding rectangle of the clip frame.
Definition: pgVirtualFrame.I:21
PandaNode
A basic node of the scene graph or data graph.
Definition: pandaNode.h:65
PandaNode::remove_child
void remove_child(int child_index, Thread *current_thread=Thread::get_current_thread())
Removes the nth child from the node.
Definition: pandaNode.cxx:564
PandaNode::set_state
set_state
Sets the complete RenderState that will be applied to all nodes at this level and below.
Definition: pandaNode.h:173
TypeHandle
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
pgScrollFrame.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.