1 /***********************************************************************
2     created:    1/3/2005
3     author:     Paul D Turner
4 *************************************************************************/
5 /***************************************************************************
6  *   Copyright (C) 2004 - 2009 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/ScrolledContainer.h"
28 #include "CEGUI/CoordConverter.h"
29 #include "CEGUI/RenderingSurface.h"
30 
31 #if defined(_MSC_VER)
32 #   pragma warning(push)
33 #   pragma warning(disable : 4355)
34 #endif
35 
36 // Start of CEGUI namespace section
37 namespace CEGUI
38 {
39 //----------------------------------------------------------------------------//
40 const String ScrolledContainer::WidgetTypeName("ScrolledContainer");
41 const String ScrolledContainer::EventNamespace("ScrolledContainer");
42 const String ScrolledContainer::EventContentChanged("ContentChanged");
43 const String ScrolledContainer::EventAutoSizeSettingChanged("AutoSizeSettingChanged");
44 
45 //----------------------------------------------------------------------------//
ScrolledContainer(const String & type,const String & name)46 ScrolledContainer::ScrolledContainer(const String& type, const String& name) :
47     Window(type, name),
48     d_contentArea(0, 0, 0, 0),
49     d_autosizePane(true),
50 
51     d_clientChildContentArea(this, static_cast<Element::CachedRectf::DataGenerator>(&ScrolledContainer::getClientChildContentArea_impl))
52 {
53     addScrolledContainerProperties();
54     setMouseInputPropagationEnabled(true);
55 }
56 
57 //----------------------------------------------------------------------------//
~ScrolledContainer(void)58 ScrolledContainer::~ScrolledContainer(void)
59 {
60 }
61 
62 //----------------------------------------------------------------------------//
isContentPaneAutoSized(void) const63 bool ScrolledContainer::isContentPaneAutoSized(void) const
64 {
65     return d_autosizePane;
66 }
67 
68 //----------------------------------------------------------------------------//
setContentPaneAutoSized(bool setting)69 void ScrolledContainer::setContentPaneAutoSized(bool setting)
70 {
71     if (d_autosizePane != setting)
72     {
73         d_autosizePane = setting;
74 
75         // Fire events
76         WindowEventArgs args1(this);
77         onAutoSizeSettingChanged(args1);
78     }
79 }
80 
81 //----------------------------------------------------------------------------//
getContentArea(void) const82 const Rectf& ScrolledContainer::getContentArea(void) const
83 {
84     return d_contentArea;
85 }
86 
87 //----------------------------------------------------------------------------//
setContentArea(const Rectf & area)88 void ScrolledContainer::setContentArea(const Rectf& area)
89 {
90     if (!d_autosizePane)
91     {
92         d_contentArea = area;
93 
94         // Fire event
95         WindowEventArgs args(this);
96         onContentChanged(args);
97    }
98 
99 }
100 
101 //----------------------------------------------------------------------------//
getClientChildContentArea() const102 const Element::CachedRectf& ScrolledContainer::getClientChildContentArea() const
103 {
104     return d_clientChildContentArea;
105 }
106 
107 //----------------------------------------------------------------------------//
getNonClientChildContentArea() const108 const Element::CachedRectf& ScrolledContainer::getNonClientChildContentArea() const
109 {
110     return d_clientChildContentArea;
111 }
112 
113 //----------------------------------------------------------------------------//
notifyScreenAreaChanged(bool recursive)114 void ScrolledContainer::notifyScreenAreaChanged(bool recursive)
115 {
116     d_clientChildContentArea.invalidateCache();
117 
118     Window::notifyScreenAreaChanged(recursive);
119 }
120 
121 //----------------------------------------------------------------------------//
getChildExtentsArea(void) const122 Rectf ScrolledContainer::getChildExtentsArea(void) const
123 {
124     Rectf extents(0, 0, 0, 0);
125 
126     const size_t childCount = getChildCount();
127     if (childCount == 0)
128         return extents;
129 
130     for (size_t i = 0; i < childCount; ++i)
131     {
132         const Window* const wnd = getChildAtIdx(i);
133         Rectf area(
134             CoordConverter::asAbsolute(wnd->getPosition(), d_pixelSize),
135             wnd->getPixelSize());
136 
137         if (wnd->getHorizontalAlignment() == HA_CENTRE)
138             area.setPosition(area.getPosition() - CEGUI::Vector2f(area.getWidth() * 0.5f - d_pixelSize.d_width * 0.5f, 0.0f));
139         if (wnd->getVerticalAlignment() == VA_CENTRE)
140             area.setPosition(area.getPosition() - CEGUI::Vector2f(0.0f, area.getHeight() * 0.5f - d_pixelSize.d_height * 0.5f));
141 
142         if (area.d_min.d_x < extents.d_min.d_x)
143             extents.d_min.d_x = area.d_min.d_x;
144 
145         if (area.d_min.d_y < extents.d_min.d_y)
146             extents.d_min.d_y = area.d_min.d_y;
147 
148         if (area.d_max.d_x > extents.d_max.d_x)
149             extents.d_max.d_x = area.d_max.d_x;
150 
151         if (area.d_max.d_y > extents.d_max.d_y)
152             extents.d_max.d_y = area.d_max.d_y;
153     }
154 
155     return extents;
156 }
157 
158 //----------------------------------------------------------------------------//
onContentChanged(WindowEventArgs & e)159 void ScrolledContainer::onContentChanged(WindowEventArgs& e)
160 {
161     if (d_autosizePane)
162     {
163         d_contentArea = getChildExtentsArea();
164     }
165 
166     fireEvent(EventContentChanged, e, EventNamespace);
167 }
168 
169 //----------------------------------------------------------------------------//
onAutoSizeSettingChanged(WindowEventArgs & e)170 void ScrolledContainer::onAutoSizeSettingChanged(WindowEventArgs& e)
171 {
172     fireEvent(EventAutoSizeSettingChanged, e, EventNamespace);
173 
174     if (d_autosizePane)
175     {
176         WindowEventArgs args(this);
177         onContentChanged(args);
178     }
179 }
180 
181 //----------------------------------------------------------------------------//
handleChildSized(const EventArgs &)182 bool ScrolledContainer::handleChildSized(const EventArgs&)
183 {
184     // Fire event that notifies that a child's area has changed.
185     WindowEventArgs args(this);
186     onContentChanged(args);
187     return true;
188 }
189 
190 //----------------------------------------------------------------------------//
handleChildMoved(const EventArgs &)191 bool ScrolledContainer::handleChildMoved(const EventArgs&)
192 {
193     // Fire event that notifies that a child's area has changed.
194     WindowEventArgs args(this);
195     onContentChanged(args);
196     return true;
197 }
198 
199 //----------------------------------------------------------------------------//
getUnclippedInnerRect_impl(bool skipAllPixelAlignment) const200 Rectf ScrolledContainer::getUnclippedInnerRect_impl(bool skipAllPixelAlignment) const
201 {
202     return d_parent ?
203         (skipAllPixelAlignment ? d_parent->getUnclippedInnerRect().getFresh(true) : d_parent->getUnclippedInnerRect().get()) :
204         Window::getUnclippedInnerRect_impl(skipAllPixelAlignment);
205 }
206 
207 //----------------------------------------------------------------------------//
getInnerRectClipper_impl() const208 Rectf ScrolledContainer::getInnerRectClipper_impl() const
209 {
210     return d_parent ?
211         getParent()->getInnerRectClipper() :
212         Window::getInnerRectClipper_impl();
213 }
214 
215 //----------------------------------------------------------------------------//
getHitTestRect_impl() const216 Rectf ScrolledContainer::getHitTestRect_impl() const
217 {
218     return d_parent ? getParent()->getHitTestRect() :
219                       Window::getHitTestRect_impl();
220 }
221 
222 //----------------------------------------------------------------------------//
getClientChildContentArea_impl(bool skipAllPixelAlignment) const223 Rectf ScrolledContainer::getClientChildContentArea_impl(bool skipAllPixelAlignment) const
224 {
225     if (!d_parent)
226     {
227         return skipAllPixelAlignment ? Window::getUnclippedInnerRect().getFresh(true) : Window::getUnclippedInnerRect().get();
228     }
229     else
230     {
231         if (skipAllPixelAlignment)
232         {
233             return Rectf(getUnclippedOuterRect().getFresh(true).getPosition(),
234                          getParent()->getUnclippedInnerRect().getFresh(true).getSize());
235         }
236         else
237         {
238             return Rectf(getUnclippedOuterRect().get().getPosition(),
239                          getParent()->getUnclippedInnerRect().get().getSize());
240         }
241     }
242 }
243 
244 //----------------------------------------------------------------------------//
onChildAdded(ElementEventArgs & e)245 void ScrolledContainer::onChildAdded(ElementEventArgs& e)
246 {
247     Window::onChildAdded(e);
248 
249     // subscribe to some events on this child
250     d_eventConnections.insert(std::make_pair(static_cast<Window*>(e.element),
251         static_cast<Window*>(e.element)->subscribeEvent(Window::EventSized,
252             Event::Subscriber(&ScrolledContainer::handleChildSized, this))));
253     d_eventConnections.insert(std::make_pair(static_cast<Window*>(e.element),
254         static_cast<Window*>(e.element)->subscribeEvent(Window::EventMoved,
255             Event::Subscriber(&ScrolledContainer::handleChildMoved, this))));
256 
257     // force window to update what it thinks it's screen / pixel areas are.
258     static_cast<Window*>(e.element)->notifyScreenAreaChanged(false);
259 
260     // perform notification.
261     WindowEventArgs args(this);
262     onContentChanged(args);
263 }
264 
265 //----------------------------------------------------------------------------//
onChildRemoved(ElementEventArgs & e)266 void ScrolledContainer::onChildRemoved(ElementEventArgs& e)
267 {
268     Window::onChildRemoved(e);
269 
270     // disconnect from events for this window.
271     ConnectionTracker::iterator conn;
272     while ((conn = d_eventConnections.find(static_cast<Window*>(e.element))) != d_eventConnections.end())
273     {
274         conn->second->disconnect();
275         d_eventConnections.erase(conn);
276     }
277 
278     // perform notification only if we're not currently being destroyed
279     if (!d_destructionStarted)
280     {
281         WindowEventArgs args(this);
282         onContentChanged(args);
283     }
284 }
285 
286 //----------------------------------------------------------------------------//
onParentSized(ElementEventArgs & e)287 void ScrolledContainer::onParentSized(ElementEventArgs& e)
288 {
289     Window::onParentSized(e);
290 
291     // perform notification.
292     WindowEventArgs args(this);
293     onContentChanged(args);
294 }
295 
296 //----------------------------------------------------------------------------//
addScrolledContainerProperties(void)297 void ScrolledContainer::addScrolledContainerProperties(void)
298 {
299     const String& propertyOrigin = WidgetTypeName;
300 
301     CEGUI_DEFINE_PROPERTY(ScrolledContainer, bool,
302         "ContentPaneAutoSized", "Property to get/set the setting which controls whether the content pane will auto-size itself."
303         "  Value is either \"true\" or \"false\".",
304         &ScrolledContainer::setContentPaneAutoSized, &ScrolledContainer::isContentPaneAutoSized, true
305     );
306 
307     CEGUI_DEFINE_PROPERTY(ScrolledContainer, Rectf,
308         "ContentArea", "Property to get/set the current content area rectangle of the content pane."
309         "  Value is \"l:[float] t:[float] r:[float] b:[float]\" (where l is left, t is top, r is right, and b is bottom).",
310         &ScrolledContainer::setContentArea, &ScrolledContainer::getContentArea, Rectf::zero()
311     );
312 
313     CEGUI_DEFINE_PROPERTY(ScrolledContainer, Rectf,
314         "ChildExtentsArea", "Property to get the current content extents rectangle."
315         "  Value is \"l:[float] t:[float] r:[float] b:[float]\" (where l is left, t is top, r is right, and b is bottom).",
316         0, &ScrolledContainer::getChildExtentsArea, Rectf::zero()
317     );
318 }
319 
320 //----------------------------------------------------------------------------//
setArea_impl(const UVector2 & pos,const USize & size,bool topLeftSizing,bool fireEvents)321 void ScrolledContainer::setArea_impl(const UVector2& pos, const USize& size,
322                                      bool topLeftSizing, bool fireEvents)
323 {
324     d_clientChildContentArea.invalidateCache();
325     Window::setArea_impl(pos, size, topLeftSizing, fireEvents);
326 }
327 
328 //----------------------------------------------------------------------------//
329 
330 #if defined(_MSC_VER)
331 #   pragma warning(pop)
332 #endif
333 
334 } // End of  CEGUI namespace section
335