1 /***********************************************************************
2     created:    1/3/2005
3     author:     Paul D Turner
4  *************************************************************************/
5 /***************************************************************************
6  *   Copyright (C) 2004 - 2015 Paul D Turner & The CEGUI Development Team
7  *
8  *   Permission is hereby granted, free of charge, to any person obtaining
9  *   a copy of this software and associated documentation files (the
10  *   "Software"), to deal in the Software without restriction, including
11  *   without limitation the rights to use, copy, modify, merge, publish,
12  *   distribute, sublicense, and/or sell copies of the Software, and to
13  *   permit persons to whom the Software is furnished to do so, subject to
14  *   the following conditions:
15  *
16  *   The above copyright notice and this permission notice shall be
17  *   included in all copies or substantial portions of the Software.
18  *
19  *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20  *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21  *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22  *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
23  *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
24  *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25  *   OTHER DEALINGS IN THE SOFTWARE.
26  ***************************************************************************/
27 #include "CEGUI/widgets/ScrollablePane.h"
28 #include "CEGUI/widgets/ScrolledContainer.h"
29 #include "CEGUI/widgets/Scrollbar.h"
30 #include "CEGUI/WindowManager.h"
31 #include "CEGUI/Exceptions.h"
32 #include <math.h>
33 
34 // Start of CEGUI namespace section
35 namespace CEGUI
36 {
37 //----------------------------------------------------------------------------//
38 const String ScrollablePane::WidgetTypeName("CEGUI/ScrollablePane");
39 const String ScrollablePane::EventNamespace("ScrollablePane");
40 const String ScrollablePane::EventContentPaneChanged("ContentPaneChanged");
41 const String ScrollablePane::EventVertScrollbarModeChanged("VertScrollbarModeChanged");
42 const String ScrollablePane::EventHorzScrollbarModeChanged("HorzScrollbarModeChanged");
43 const String ScrollablePane::EventAutoSizeSettingChanged("AutoSizeSettingChanged");
44 const String ScrollablePane::EventContentPaneScrolled("ContentPaneScrolled");
45 const String ScrollablePane::VertScrollbarName( "__auto_vscrollbar__" );
46 const String ScrollablePane::HorzScrollbarName( "__auto_hscrollbar__" );
47 const String ScrollablePane::ScrolledContainerName( "__auto_container__" );
48 //----------------------------------------------------------------------------//
ScrollablePaneWindowRenderer(const String & name)49 ScrollablePaneWindowRenderer::ScrollablePaneWindowRenderer(const String& name) :
50 WindowRenderer(name, ScrollablePane::EventNamespace)
51 {
52 }
53 
54 //----------------------------------------------------------------------------//
ScrollablePane(const String & type,const String & name)55 ScrollablePane::ScrollablePane(const String& type, const String& name) :
56     Window(type, name),
57     d_forceVertScroll(false),
58     d_forceHorzScroll(false),
59     d_contentRect(0, 0, 0, 0),
60     d_vertStep(0.1f),
61     d_vertOverlap(0.01f),
62     d_horzStep(0.1f),
63     d_horzOverlap(0.01f)
64 {
65     addScrollablePaneProperties();
66 
67     // create scrolled container widget
68     ScrolledContainer* container = static_cast<ScrolledContainer*>(
69         WindowManager::getSingleton().createWindow(
70             ScrolledContainer::WidgetTypeName,
71             ScrolledContainerName));
72     container->setAutoWindow(true);
73 
74     // add scrolled container widget as child
75     addChild(container);
76 }
77 
78 //----------------------------------------------------------------------------//
~ScrollablePane(void)79 ScrollablePane::~ScrollablePane(void)
80 {}
81 
82 //----------------------------------------------------------------------------//
getContentPane(void) const83 const ScrolledContainer* ScrollablePane::getContentPane(void) const
84 {
85     return getScrolledContainer();
86 }
87 
88 //----------------------------------------------------------------------------//
isVertScrollbarAlwaysShown(void) const89 bool ScrollablePane::isVertScrollbarAlwaysShown(void) const
90 {
91     return d_forceVertScroll;
92 }
93 
94 //----------------------------------------------------------------------------//
setShowVertScrollbar(bool setting)95 void ScrollablePane::setShowVertScrollbar(bool setting)
96 {
97     if (d_forceVertScroll != setting)
98     {
99         d_forceVertScroll = setting;
100 
101         configureScrollbars();
102         WindowEventArgs args(this);
103         onVertScrollbarModeChanged(args);
104     }
105 }
106 
107 //----------------------------------------------------------------------------//
isHorzScrollbarAlwaysShown(void) const108 bool ScrollablePane::isHorzScrollbarAlwaysShown(void) const
109 {
110     return d_forceHorzScroll;
111 }
112 
113 //----------------------------------------------------------------------------//
setShowHorzScrollbar(bool setting)114 void ScrollablePane::setShowHorzScrollbar(bool setting)
115 {
116     if (d_forceHorzScroll != setting)
117     {
118         d_forceHorzScroll = setting;
119 
120         configureScrollbars();
121         WindowEventArgs args(this);
122         onHorzScrollbarModeChanged(args);
123     }
124 }
125 
126 //----------------------------------------------------------------------------//
isContentPaneAutoSized(void) const127 bool ScrollablePane::isContentPaneAutoSized(void) const
128 {
129     return getScrolledContainer()->isContentPaneAutoSized();
130 }
131 
132 //----------------------------------------------------------------------------//
setContentPaneAutoSized(bool setting)133 void ScrollablePane::setContentPaneAutoSized(bool setting)
134 {
135     getScrolledContainer()->setContentPaneAutoSized(setting);
136 }
137 
138 //----------------------------------------------------------------------------//
getContentPaneArea(void) const139 const Rectf& ScrollablePane::getContentPaneArea(void) const
140 {
141     return getScrolledContainer()->getContentArea();
142 }
143 
144 //----------------------------------------------------------------------------//
setContentPaneArea(const Rectf & area)145 void ScrollablePane::setContentPaneArea(const Rectf& area)
146 {
147     getScrolledContainer()->setContentArea(area);
148 }
149 
150 //----------------------------------------------------------------------------//
getHorizontalStepSize(void) const151 float ScrollablePane::getHorizontalStepSize(void) const
152 {
153     return d_horzStep;
154 }
155 
156 //----------------------------------------------------------------------------//
setHorizontalStepSize(float step)157 void ScrollablePane::setHorizontalStepSize(float step)
158 {
159     d_horzStep = step;
160     configureScrollbars();
161 }
162 
163 //----------------------------------------------------------------------------//
getHorizontalOverlapSize(void) const164 float ScrollablePane::getHorizontalOverlapSize(void) const
165 {
166     return d_horzOverlap;
167 }
168 
169 //----------------------------------------------------------------------------//
setHorizontalOverlapSize(float overlap)170 void ScrollablePane::setHorizontalOverlapSize(float overlap)
171 {
172     d_horzOverlap = overlap;
173     configureScrollbars();
174 }
175 
176 //----------------------------------------------------------------------------//
getHorizontalScrollPosition(void) const177 float ScrollablePane::getHorizontalScrollPosition(void) const
178 {
179     return getHorzScrollbar()->getUnitIntervalScrollPosition();
180 }
181 
182 //----------------------------------------------------------------------------//
setHorizontalScrollPosition(float position)183 void ScrollablePane::setHorizontalScrollPosition(float position)
184 {
185     getHorzScrollbar()->setUnitIntervalScrollPosition(position);
186 }
187 
188 //----------------------------------------------------------------------------//
getVerticalStepSize(void) const189 float ScrollablePane::getVerticalStepSize(void) const
190 {
191     return d_vertStep;
192 }
193 
194 //----------------------------------------------------------------------------//
setVerticalStepSize(float step)195 void ScrollablePane::setVerticalStepSize(float step)
196 {
197     d_vertStep = step;
198     configureScrollbars();
199 }
200 
201 //----------------------------------------------------------------------------//
getVerticalOverlapSize(void) const202 float ScrollablePane::getVerticalOverlapSize(void) const
203 {
204     return d_vertOverlap;
205 }
206 
207 //----------------------------------------------------------------------------//
setVerticalOverlapSize(float overlap)208 void ScrollablePane::setVerticalOverlapSize(float overlap)
209 {
210     d_vertOverlap = overlap;
211     configureScrollbars();
212 }
213 
214 //----------------------------------------------------------------------------//
getVerticalScrollPosition(void) const215 float ScrollablePane::getVerticalScrollPosition(void) const
216 {
217     return getVertScrollbar()->getUnitIntervalScrollPosition();
218 }
219 
220 //----------------------------------------------------------------------------//
setVerticalScrollPosition(float position)221 void ScrollablePane::setVerticalScrollPosition(float position)
222 {
223     getVertScrollbar()->setUnitIntervalScrollPosition(position);
224 }
225 
226 //----------------------------------------------------------------------------//
initialiseComponents(void)227 void ScrollablePane::initialiseComponents(void)
228 {
229     // get horizontal scrollbar
230     Scrollbar* horzScrollbar = getHorzScrollbar();
231 
232     // get vertical scrollbar
233     Scrollbar* vertScrollbar = getVertScrollbar();
234 
235     // get scrolled container widget
236     ScrolledContainer* container = getScrolledContainer();
237 
238     // ban properties forwarded from here
239     container->banPropertyFromXML(Window::MouseInputPropagationEnabledPropertyName);
240     container->banPropertyFromXML("ContentArea");
241     container->banPropertyFromXML("ContentPaneAutoSized");
242     horzScrollbar->banPropertyFromXML(Window::AlwaysOnTopPropertyName);
243     vertScrollbar->banPropertyFromXML(Window::AlwaysOnTopPropertyName);
244 
245     // do a bit of initialisation
246     horzScrollbar->setAlwaysOnTop(true);
247     vertScrollbar->setAlwaysOnTop(true);
248     // container pane is always same size as this parent pane,
249     // scrolling is actually implemented via positioning and clipping tricks.
250     container->setSize(USize(cegui_reldim(1.0f), cegui_reldim(1.0f)));
251 
252     // subscribe to events we need to hear about
253     vertScrollbar->subscribeEvent(
254             Scrollbar::EventScrollPositionChanged,
255             Event::Subscriber(&ScrollablePane::handleScrollChange, this));
256 
257     horzScrollbar->subscribeEvent(
258             Scrollbar::EventScrollPositionChanged,
259             Event::Subscriber(&ScrollablePane::handleScrollChange, this));
260 
261     d_contentChangedConn = container->subscribeEvent(
262             ScrolledContainer::EventContentChanged,
263             Event::Subscriber(&ScrollablePane::handleContentAreaChange, this));
264 
265     d_autoSizeChangedConn = container->subscribeEvent(
266             ScrolledContainer::EventAutoSizeSettingChanged,
267             Event::Subscriber(&ScrollablePane::handleAutoSizePaneChanged, this));
268 
269     // finalise setup
270     configureScrollbars();
271 }
272 
273 //----------------------------------------------------------------------------//
configureScrollbars(void)274 void ScrollablePane::configureScrollbars(void)
275 {
276     // controls should all be valid by this stage
277     Scrollbar* const vertScrollbar = getVertScrollbar();
278     Scrollbar* const horzScrollbar = getHorzScrollbar();
279 
280     const bool horzScrollBarWasVisible = horzScrollbar->isVisible();
281     const bool vertScrollBarWasVisible = vertScrollbar->isVisible();
282 
283     // enable required scrollbars
284     vertScrollbar->setVisible(isVertScrollbarNeeded());
285     horzScrollbar->setVisible(isHorzScrollbarNeeded());
286 
287     // Check if the addition of the horizontal scrollbar means we
288     // now also need the vertical bar.
289     if (horzScrollbar->isVisible())
290         vertScrollbar->setVisible(isVertScrollbarNeeded());
291 
292     if (horzScrollBarWasVisible != horzScrollbar->isVisible() ||
293         vertScrollBarWasVisible != vertScrollbar->isVisible())
294     {
295         ElementEventArgs args(this);
296         onSized(args);
297     }
298 
299     performChildWindowLayout();
300 
301     // get viewable area
302     const Rectf viewableArea(getViewableArea());
303 
304     // set up vertical scroll bar values
305     vertScrollbar->setDocumentSize(fabsf(d_contentRect.getHeight()));
306     vertScrollbar->setPageSize(viewableArea.getHeight());
307     vertScrollbar->setStepSize(ceguimax(1.0f, viewableArea.getHeight() * d_vertStep));
308     vertScrollbar->setOverlapSize(ceguimax(1.0f, viewableArea.getHeight() * d_vertOverlap));
309     vertScrollbar->setScrollPosition(vertScrollbar->getScrollPosition());
310 
311     // set up horizontal scroll bar values
312     horzScrollbar->setDocumentSize(fabsf(d_contentRect.getWidth()));
313     horzScrollbar->setPageSize(viewableArea.getWidth());
314     horzScrollbar->setStepSize(ceguimax(1.0f, viewableArea.getWidth() * d_horzStep));
315     horzScrollbar->setOverlapSize(ceguimax(1.0f, viewableArea.getWidth() * d_horzOverlap));
316     horzScrollbar->setScrollPosition(horzScrollbar->getScrollPosition());
317 }
318 
319 //----------------------------------------------------------------------------//
isHorzScrollbarNeeded(void) const320 bool ScrollablePane::isHorzScrollbarNeeded(void) const
321 {
322     return ((fabs(d_contentRect.getWidth()) > getViewableArea().getWidth()) ||
323             d_forceHorzScroll);
324 }
325 
326 //----------------------------------------------------------------------------//
isVertScrollbarNeeded(void) const327 bool ScrollablePane::isVertScrollbarNeeded(void) const
328 {
329     return ((fabs(d_contentRect.getHeight()) > getViewableArea().getHeight()) ||
330             d_forceVertScroll);
331 }
332 
333 //----------------------------------------------------------------------------//
updateContainerPosition(void)334 void ScrollablePane::updateContainerPosition(void)
335 {
336     // basePos is the position represented by the scrollbars
337     // (these are negated so pane is scrolled in the correct directions)
338     UVector2 basePos(cegui_absdim(-getHorzScrollbar()->getScrollPosition()),
339                      cegui_absdim(-getVertScrollbar()->getScrollPosition()));
340 
341     // this bias is the absolute position that 0 on the scrollbars represent.
342     // Allows the pane to function correctly with negatively positioned content.
343     UVector2 bias(cegui_absdim(d_contentRect.d_min.d_x),
344                   cegui_absdim(d_contentRect.d_min.d_y));
345 
346     // set the new container pane position to be what the scrollbars request
347     // minus any bias generated by the location of the content.
348     getScrolledContainer()->setPosition(basePos - bias);
349 }
350 
351 //----------------------------------------------------------------------------//
validateWindowRenderer(const WindowRenderer * renderer) const352 bool ScrollablePane::validateWindowRenderer(const WindowRenderer* renderer) const
353 {
354 	return dynamic_cast<const ScrollablePaneWindowRenderer*>(renderer) != 0;
355 }
356 
357 //----------------------------------------------------------------------------//
onContentPaneChanged(WindowEventArgs & e)358 void ScrollablePane::onContentPaneChanged(WindowEventArgs& e)
359 {
360     fireEvent(EventContentPaneChanged, e, EventNamespace);
361 }
362 
363 //----------------------------------------------------------------------------//
onVertScrollbarModeChanged(WindowEventArgs & e)364 void ScrollablePane::onVertScrollbarModeChanged(WindowEventArgs& e)
365 {
366     fireEvent(EventVertScrollbarModeChanged, e, EventNamespace);
367 }
368 
369 //----------------------------------------------------------------------------//
onHorzScrollbarModeChanged(WindowEventArgs & e)370 void ScrollablePane::onHorzScrollbarModeChanged(WindowEventArgs& e)
371 {
372     fireEvent(EventHorzScrollbarModeChanged, e, EventNamespace);
373 }
374 
375 //----------------------------------------------------------------------------//
onAutoSizeSettingChanged(WindowEventArgs & e)376 void ScrollablePane::onAutoSizeSettingChanged(WindowEventArgs& e)
377 {
378     fireEvent(EventAutoSizeSettingChanged, e, EventNamespace);
379 }
380 
381 //----------------------------------------------------------------------------//
onContentPaneScrolled(WindowEventArgs & e)382 void ScrollablePane::onContentPaneScrolled(WindowEventArgs& e)
383 {
384     updateContainerPosition();
385     fireEvent(EventContentPaneScrolled, e, EventNamespace);
386 }
387 
388 //----------------------------------------------------------------------------//
handleScrollChange(const EventArgs &)389 bool ScrollablePane::handleScrollChange(const EventArgs&)
390 {
391     WindowEventArgs args(this);
392     onContentPaneScrolled(args);
393     return true;
394 }
395 
396 //----------------------------------------------------------------------------//
handleContentAreaChange(const EventArgs &)397 bool ScrollablePane::handleContentAreaChange(const EventArgs&)
398 {
399     // get updated extents of the content
400     const Rectf contentArea(getScrolledContainer()->getContentArea());
401 
402     // calculate any change on the top and left edges.
403     const float xChange = contentArea.d_min.d_x - d_contentRect.d_min.d_x;
404     const float yChange = contentArea.d_min.d_y - d_contentRect.d_min.d_y;
405 
406     // store new content extents information
407     d_contentRect = contentArea;
408 
409     configureScrollbars();
410 
411     // update scrollbar positions (which causes container pane to be moved as needed).
412     Scrollbar* const horzScrollbar = getHorzScrollbar();
413     horzScrollbar->setScrollPosition(horzScrollbar->getScrollPosition() - xChange);
414     Scrollbar* const vertScrollbar = getVertScrollbar();
415     vertScrollbar->setScrollPosition(vertScrollbar->getScrollPosition() - yChange);
416 
417     // this call may already have been made if the scroll positions changed.  The call
418     // is required here for cases where the top/left 'bias' has changed; in which
419     // case the scroll position notification may or may not have been fired.
420     if (xChange || yChange)
421         updateContainerPosition();
422 
423     // fire event
424     WindowEventArgs args(this);
425     onContentPaneChanged(args);
426 
427     return true;
428 }
429 
430 //----------------------------------------------------------------------------//
handleAutoSizePaneChanged(const EventArgs &)431 bool ScrollablePane::handleAutoSizePaneChanged(const EventArgs&)
432 {
433     // just forward event to client.
434     WindowEventArgs args(this);
435     fireEvent(EventAutoSizeSettingChanged, args, EventNamespace);
436     return args.handled > 0;
437 }
438 
439 //----------------------------------------------------------------------------//
addChild_impl(Element * element)440 void ScrollablePane::addChild_impl(Element* element)
441 {
442     Window* wnd = dynamic_cast<Window*>(element);
443 
444     if (!wnd)
445         CEGUI_THROW(InvalidRequestException(
446             "ScrollablePane can only have Elements of "
447             "type Window added as children (Window path: " +
448             getNamePath() + ")."));
449 
450     if (wnd->isAutoWindow())
451     {
452         // This is an internal widget, so should be added normally.
453         Window::addChild_impl(wnd);
454     }
455     // this is a client window/widget, so should be added to the pane container.
456     else
457     {
458         // container should always be valid by the time we're adding client
459         // controls
460         getScrolledContainer()->addChild(wnd);
461     }
462 }
463 
464 //----------------------------------------------------------------------------//
removeChild_impl(Element * element)465 void ScrollablePane::removeChild_impl(Element* element)
466 {
467     Window* wnd = static_cast<Window*>(element);
468 
469     if (wnd->isAutoWindow())
470     {
471         // This is an internal widget, so should be removed normally.
472         Window::removeChild_impl(wnd);
473     }
474     // this is a client window/widget, so should be removed from the pane
475     // container.
476     else
477     {
478         // container should always be valid by the time we're handling client
479         // controls
480         getScrolledContainer()->removeChild(wnd);
481     }
482 }
483 
484 //----------------------------------------------------------------------------//
onSized(ElementEventArgs & e)485 void ScrollablePane::onSized(ElementEventArgs& e)
486 {
487     configureScrollbars();
488     updateContainerPosition();
489     Window::onSized(e);
490 
491     ++e.handled;
492 }
493 
494 //----------------------------------------------------------------------------//
onMouseWheel(MouseEventArgs & e)495 void ScrollablePane::onMouseWheel(MouseEventArgs& e)
496 {
497     // base class processing.
498     Window::onMouseWheel(e);
499 
500     Scrollbar* vertScrollbar = getVertScrollbar();
501     Scrollbar* horzScrollbar = getHorzScrollbar();
502 
503     if (vertScrollbar->isEffectiveVisible() &&
504         (vertScrollbar->getDocumentSize() > vertScrollbar->getPageSize()))
505     {
506         vertScrollbar->setScrollPosition(vertScrollbar->getScrollPosition() +
507                             vertScrollbar->getStepSize() * -e.wheelChange);
508     }
509     else if (horzScrollbar->isEffectiveVisible() &&
510              (horzScrollbar->getDocumentSize() > horzScrollbar->getPageSize()))
511     {
512         horzScrollbar->setScrollPosition(horzScrollbar->getScrollPosition() +
513                             horzScrollbar->getStepSize() * -e.wheelChange);
514     }
515 
516     ++e.handled;
517 }
518 
519 //----------------------------------------------------------------------------//
addScrollablePaneProperties(void)520 void ScrollablePane::addScrollablePaneProperties(void)
521 {
522     const String& propertyOrigin = WidgetTypeName;
523 
524     CEGUI_DEFINE_PROPERTY(ScrollablePane, bool,
525         "ForceVertScrollbar", "Property to get/set the 'always show' setting for the vertical scroll "
526         "bar of the tree.  Value is either \"true\" or \"false\".",
527         &ScrollablePane::setShowVertScrollbar, &ScrollablePane::isVertScrollbarAlwaysShown, false /* TODO: Inconsistency */
528     );
529 
530     CEGUI_DEFINE_PROPERTY(ScrollablePane, bool,
531         "ForceHorzScrollbar", "Property to get/set the 'always show' setting for the horizontal "
532         "scroll bar of the tree.  Value is either \"true\" or \"false\".",
533         &ScrollablePane::setShowHorzScrollbar, &ScrollablePane::isHorzScrollbarAlwaysShown, false /* TODO: Inconsistency */
534     );
535 
536     CEGUI_DEFINE_PROPERTY(ScrollablePane, float,
537         "HorzStepSize", "Property to get/set the step size for the horizontal Scrollbar.  Value is a float.",
538         &ScrollablePane::setHorizontalStepSize, &ScrollablePane::getHorizontalStepSize, 0.1f /* TODO: Inconsistency */
539     );
540 
541     CEGUI_DEFINE_PROPERTY(ScrollablePane, float,
542         "HorzOverlapSize", "Property to get/set the overlap size for the horizontal Scrollbar.  Value is a float.",
543         &ScrollablePane::setHorizontalOverlapSize, &ScrollablePane::getHorizontalOverlapSize, 0.01f /* TODO: Inconsistency */
544     );
545 
546     CEGUI_DEFINE_PROPERTY(ScrollablePane, float,
547         "HorzScrollPosition", "Property to get/set the scroll position of the horizontal Scrollbar as a fraction.  Value is a float.",
548         &ScrollablePane::setHorizontalScrollPosition, &ScrollablePane::getHorizontalScrollPosition, 0.0f
549     );
550 
551     CEGUI_DEFINE_PROPERTY(ScrollablePane, float,
552         "VertStepSize", "Property to get/set the step size for the vertical Scrollbar.  Value is a float.",
553         &ScrollablePane::setVerticalStepSize, &ScrollablePane::getVerticalStepSize, 0.1f /* TODO: Inconsistency */
554     );
555 
556     CEGUI_DEFINE_PROPERTY(ScrollablePane, float,
557         "VertOverlapSize", "Property to get/set the overlap size for the vertical Scrollbar.  Value is a float.",
558         &ScrollablePane::setVerticalOverlapSize, &ScrollablePane::getVerticalOverlapSize, 0.01f /* TODO: Inconsistency */
559     );
560 
561     CEGUI_DEFINE_PROPERTY(ScrollablePane, float,
562         "VertScrollPosition", "Property to get/set the scroll position of the vertical Scrollbar as a fraction.  Value is a float.",
563         &ScrollablePane::setVerticalScrollPosition, &ScrollablePane::getVerticalScrollPosition, 0.0f /* TODO: Inconsistency */
564     );
565 
566     CEGUI_DEFINE_PROPERTY(ScrollablePane, bool,
567         "ContentPaneAutoSized", "Property to get/set the setting which controls whether the content pane will auto-size itself.  Value is either \"true\" or \"false\".",
568         &ScrollablePane::setContentPaneAutoSized, &ScrollablePane::isContentPaneAutoSized, true
569     );
570 
571     CEGUI_DEFINE_PROPERTY(ScrollablePane, Rectf,
572         "ContentArea", "Property to get/set the current content area rectangle of the content pane.  Value is \"l:[float] t:[float] r:[float] b:[float]\" (where l is left, t is top, r is right, and b is bottom).",
573         &ScrollablePane::setContentPaneArea, &ScrollablePane::getContentPaneArea, Rectf::zero() /* TODO: Inconsistency */
574     );
575 }
576 
577 //----------------------------------------------------------------------------//
getVertScrollbar() const578 Scrollbar* ScrollablePane::getVertScrollbar() const
579 {
580     return static_cast<Scrollbar*>(getChild(VertScrollbarName));
581 }
582 
583 //----------------------------------------------------------------------------//
getHorzScrollbar() const584 Scrollbar* ScrollablePane::getHorzScrollbar() const
585 {
586     return static_cast<Scrollbar*>(getChild(HorzScrollbarName));
587 }
588 
589 //----------------------------------------------------------------------------//
getScrolledContainer() const590 ScrolledContainer* ScrollablePane::getScrolledContainer() const
591 {
592     return static_cast<ScrolledContainer*>(getChild(ScrolledContainerName));
593 }
594 
595 //----------------------------------------------------------------------------//
getViewableArea() const596 Rectf ScrollablePane::getViewableArea() const
597 {
598     if (!d_windowRenderer)
599         CEGUI_THROW(InvalidRequestException(
600             "This function must be implemented by the window renderer module"));
601 
602     ScrollablePaneWindowRenderer* wr =
603         static_cast<ScrollablePaneWindowRenderer*>(d_windowRenderer);
604     return wr->getViewableArea();
605 }
606 
607 //----------------------------------------------------------------------------//
destroy(void)608 void ScrollablePane::destroy(void)
609 {
610     // detach from events on content pane
611     d_contentChangedConn->disconnect();
612     d_autoSizeChangedConn->disconnect();
613 
614     // now do the cleanup
615     Window::destroy();
616 }
617 
618 //----------------------------------------------------------------------------//
getChildByNamePath_impl(const String & name_path) const619 NamedElement* ScrollablePane::getChildByNamePath_impl(const String& name_path) const
620 {
621     // FIXME: This is horrible
622     //
623     if (name_path.substr(0, 7) == "__auto_")
624         return Window::getChildByNamePath_impl(name_path);
625 	else
626         return Window::getChildByNamePath_impl(ScrolledContainerName + '/' + name_path);
627 }
628 //----------------------------------------------------------------------------//
629 
writeChildWindowsXML(XMLSerializer & xml_stream) const630 int ScrollablePane::writeChildWindowsXML(XMLSerializer& xml_stream) const
631 {
632     // This is an easy and safe workaround for not writing out the buttonPane and contentPane. While in fact
633     // we would eventually want to write these two to XML themselves, we do not want to write out their children
634     // but there is no way to control this from inside these windows and currently there is also no way to do it
635     // from the outside. This was determined to be the best solution, others would break ABI or are too hacky
636     // Negative side-effects: any changes to AutoWindows (properties etc) will be lost in the output
637     bool wasContentPaneWritingAllowed = getScrolledContainer()->isWritingXMLAllowed();
638 
639     getScrolledContainer()->setWritingXMLAllowed(false);
640 
641     int childOutputCount = Window::writeChildWindowsXML(xml_stream);
642 
643     getScrolledContainer()->setWritingXMLAllowed(wasContentPaneWritingAllowed);
644 
645     // since TabControl content is actually added to the component tab
646     // content pane window, this overridden function exists to dump those
647     // out as if they were our own children.
648     const size_t childCount = getContentPane()->getChildCount();
649     for (size_t i = 0; i < childCount; ++i)
650     {
651         getScrolledContainer()->getChildAtIdx(i)->writeXMLToStream(xml_stream);
652         ++childOutputCount;
653     }
654 
655     return childOutputCount;
656 }
657 
658 }
659