MyGUI  3.0.3
MyGUI_VScroll.cpp
Go to the documentation of this file.
00001 
00007 /*
00008     This file is part of MyGUI.
00009 
00010     MyGUI is free software: you can redistribute it and/or modify
00011     it under the terms of the GNU Lesser General Public License as published by
00012     the Free Software Foundation, either version 3 of the License, or
00013     (at your option) any later version.
00014 
00015     MyGUI is distributed in the hope that it will be useful,
00016     but WITHOUT ANY WARRANTY; without even the implied warranty of
00017     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018     GNU Lesser General Public License for more details.
00019 
00020     You should have received a copy of the GNU Lesser General Public License
00021     along with MyGUI.  If not, see <http://www.gnu.org/licenses/>.
00022 */
00023 #include "MyGUI_Precompiled.h"
00024 #include "MyGUI_VScroll.h"
00025 #include "MyGUI_InputManager.h"
00026 #include "MyGUI_Button.h"
00027 #include "MyGUI_ResourceSkin.h"
00028 
00029 namespace MyGUI
00030 {
00031 
00032     const int SCROLL_MOUSE_WHEEL = 50; // колличество пикселей для колеса мыши
00033 
00034     VScroll::VScroll() :
00035         mWidgetStart(nullptr),
00036         mWidgetEnd(nullptr),
00037         mWidgetTrack(nullptr),
00038         mWidgetFirstPart(nullptr),
00039         mWidgetSecondPart(nullptr),
00040         mSkinRangeStart(0),
00041         mSkinRangeEnd(0),
00042         mScrollRange(0),
00043         mScrollPosition(0),
00044         mScrollPage(0),
00045         mScrollViewPage(0),
00046         mMinTrackSize(0),
00047         mMoveToClick(false)
00048     {
00049     }
00050 
00051     void VScroll::_initialise(WidgetStyle _style, const IntCoord& _coord, Align _align, ResourceSkin* _info, Widget* _parent, ICroppedRectangle * _croppedParent, IWidgetCreator * _creator, const std::string& _name)
00052     {
00053         Base::_initialise(_style, _coord, _align, _info, _parent, _croppedParent, _creator, _name);
00054 
00055         initialiseWidgetSkin(_info);
00056     }
00057 
00058     VScroll::~VScroll()
00059     {
00060         shutdownWidgetSkin();
00061     }
00062 
00063     void VScroll::baseChangeWidgetSkin(ResourceSkin* _info)
00064     {
00065         shutdownWidgetSkin();
00066         Base::baseChangeWidgetSkin(_info);
00067         initialiseWidgetSkin(_info);
00068     }
00069 
00070     void VScroll::initialiseWidgetSkin(ResourceSkin* _info)
00071     {
00072         // при нуле, будет игнорировать кнопки
00073         mScrollPage = 1;
00074         mScrollViewPage = 1;
00075 
00076         for (VectorWidgetPtr::iterator iter = mWidgetChildSkin.begin(); iter!=mWidgetChildSkin.end(); ++iter)
00077         {
00078             if (*(*iter)->_getInternalData<std::string>() == "Start")
00079             {
00080                 MYGUI_DEBUG_ASSERT( ! mWidgetStart, "widget already assigned");
00081                 mWidgetStart = (*iter)->castType<Button>();
00082                 mWidgetStart->eventMouseButtonPressed = newDelegate(this, &VScroll::notifyMousePressed);
00083                 mWidgetStart->eventMouseWheel = newDelegate(this, &VScroll::notifyMouseWheel);
00084             }
00085             else if (*(*iter)->_getInternalData<std::string>() == "End")
00086             {
00087                 MYGUI_DEBUG_ASSERT( ! mWidgetEnd, "widget already assigned");
00088                 mWidgetEnd = (*iter)->castType<Button>();
00089                 mWidgetEnd->eventMouseButtonPressed = newDelegate(this, &VScroll::notifyMousePressed);
00090                 mWidgetEnd->eventMouseWheel = newDelegate(this, &VScroll::notifyMouseWheel);
00091             }
00092             else if (*(*iter)->_getInternalData<std::string>() == "Track")
00093             {
00094                 MYGUI_DEBUG_ASSERT( ! mWidgetTrack, "widget already assigned");
00095                 mWidgetTrack = (*iter)->castType<Button>();
00096                 mWidgetTrack->eventMouseDrag = newDelegate(this, &VScroll::notifyMouseDrag);
00097                 mWidgetTrack->eventMouseButtonPressed = newDelegate(this, &VScroll::notifyMousePressed);
00098                 mWidgetTrack->eventMouseButtonReleased = newDelegate(this, &VScroll::notifyMouseReleased);
00099                 mWidgetTrack->eventMouseWheel = newDelegate(this, &VScroll::notifyMouseWheel);
00100                 mWidgetTrack->setVisible(false);
00101             }
00102             else if (*(*iter)->_getInternalData<std::string>() == "FirstPart")
00103             {
00104                 MYGUI_DEBUG_ASSERT( ! mWidgetFirstPart, "widget already assigned");
00105                 mWidgetFirstPart = (*iter)->castType<Button>();
00106                 mWidgetFirstPart->eventMouseButtonPressed = newDelegate(this, &VScroll::notifyMousePressed);
00107                 mWidgetFirstPart->eventMouseWheel = newDelegate(this, &VScroll::notifyMouseWheel);
00108             }
00109             else if (*(*iter)->_getInternalData<std::string>() == "SecondPart")
00110             {
00111                 MYGUI_DEBUG_ASSERT( ! mWidgetSecondPart, "widget already assigned");
00112                 mWidgetSecondPart = (*iter)->castType<Button>();
00113                 mWidgetSecondPart->eventMouseButtonPressed = newDelegate(this, &VScroll::notifyMousePressed);
00114                 mWidgetSecondPart->eventMouseWheel = newDelegate(this, &VScroll::notifyMouseWheel);
00115             }
00116         }
00117 
00118         // slider don't have buttons
00119         //MYGUI_ASSERT(nullptr != mWidgetTrack, "Child Button Track not found in skin (Scroll must have Track)");
00120 
00121         // парсим свойства
00122         const MapString& properties = _info->getProperties();
00123         MapString::const_iterator iter = properties.find("TrackRangeMargins");
00124         if (iter != properties.end())
00125         {
00126             IntSize range = IntSize::parse(iter->second);
00127             mSkinRangeStart = range.width;
00128             mSkinRangeEnd = range.height;
00129         }
00130         else
00131         {
00132             mSkinRangeStart = 0;
00133             mSkinRangeEnd = 0;
00134         }
00135         iter = properties.find("MinTrackSize");
00136         if (iter != properties.end()) mMinTrackSize = utility::parseInt(iter->second);
00137         else mMinTrackSize = 0;
00138 
00139         iter = properties.find("MoveToClick");
00140         if (iter != properties.end()) mMoveToClick = utility::parseBool(iter->second);
00141     }
00142 
00143     void VScroll::shutdownWidgetSkin()
00144     {
00145         mWidgetStart = nullptr;
00146         mWidgetEnd = nullptr;
00147         mWidgetTrack = nullptr;
00148         mWidgetFirstPart = nullptr;
00149         mWidgetSecondPart = nullptr;
00150     }
00151 
00152     void VScroll::updateTrack()
00153     {
00154         if (mWidgetTrack == nullptr)
00155             return;
00156 
00157         _forcePeek(mWidgetTrack);
00158         // размер диапазана в пикселях
00159         int pos = getLineSize();
00160 
00161         // скрываем если диапазан маленький или места мало
00162         if ((mScrollRange < 2) || (pos <= mWidgetTrack->getHeight()))
00163         {
00164             mWidgetTrack->setVisible(false);
00165             if ( nullptr != mWidgetFirstPart ) mWidgetFirstPart->setSize(mWidgetFirstPart->getWidth(), pos/2);
00166             if ( nullptr != mWidgetSecondPart ) mWidgetSecondPart->setCoord(mWidgetSecondPart->getLeft(), pos/2 + (int)mSkinRangeStart, mWidgetSecondPart->getWidth(), pos - pos/2);
00167             return;
00168         }
00169         // если скрыт то покажем
00170         if (!mWidgetTrack->isVisible())
00171         {
00172             mWidgetTrack->setVisible(true);
00173         }
00174 
00175         // и обновляем позицию
00176         pos = (int)(((size_t)(pos-getTrackSize()) * mScrollPosition) / (mScrollRange-1) + mSkinRangeStart);
00177 
00178         mWidgetTrack->setPosition(mWidgetTrack->getLeft(), pos);
00179         if ( nullptr != mWidgetFirstPart )
00180         {
00181             int height = pos + mWidgetTrack->getHeight()/2 - mWidgetFirstPart->getTop();
00182             mWidgetFirstPart->setSize(mWidgetFirstPart->getWidth(), height);
00183         }
00184         if ( nullptr != mWidgetSecondPart )
00185         {
00186             int top = pos + mWidgetTrack->getHeight()/2;
00187             int height = mWidgetSecondPart->getHeight() + mWidgetSecondPart->getTop() - top;
00188             mWidgetSecondPart->setCoord(mWidgetSecondPart->getLeft(), top, mWidgetSecondPart->getWidth(), height);
00189         }
00190     }
00191 
00192     void VScroll::TrackMove(int _left, int _top)
00193     {
00194         if (mWidgetTrack == nullptr)
00195             return;
00196 
00197         const IntPoint& point = InputManager::getInstance().getLastLeftPressed();
00198 
00199         // расчитываем позицию виджета
00200         int start = mPreActionOffset.top + (_top - point.top);
00201         if (start < (int)mSkinRangeStart) start = (int)mSkinRangeStart;
00202         else if (start > (mCoord.height - (int)mSkinRangeEnd - mWidgetTrack->getHeight())) start = (mCoord.height - (int)mSkinRangeEnd - mWidgetTrack->getHeight());
00203         if (mWidgetTrack->getTop() != start) mWidgetTrack->setPosition(mWidgetTrack->getLeft(), start);
00204 
00205         // расчитываем положение соответствующее позиции
00206         // плюс пол позиции
00207         int pos = start - (int)mSkinRangeStart + (getLineSize() - getTrackSize()) / (((int)mScrollRange-1) * 2);
00208         // высчитываем ближайшее значение и обновляем
00209         pos = pos * (int)(mScrollRange-1) / (getLineSize() - getTrackSize());
00210 
00211         // проверяем на выходы и изменения
00212         if (pos < 0) pos = 0;
00213         else if (pos >= (int)mScrollRange) pos = (int)mScrollRange - 1;
00214         if (pos == (int)mScrollPosition) return;
00215 
00216         mScrollPosition = pos;
00217         // отсылаем событие
00218         eventScrollChangePosition(this, (int)mScrollPosition);
00219     }
00220 
00221     void VScroll::notifyMousePressed(Widget* _sender, int _left, int _top, MouseButton _id)
00222     {
00223         // диспечерезируем нажатие своих детей как свое
00224         eventMouseButtonPressed(this, _left, _top, _id);
00225 
00226         if (MouseButton::Left != _id) return;
00227 
00228         if (mMoveToClick && mWidgetTrack != _sender)
00229         {
00230             mPreActionOffset = InputManager::getInstance().getLastLeftPressed();
00231             const IntPoint& point = InputManager::getInstance().getMousePositionByLayer() - getAbsolutePosition();
00232 
00233             TrackMove(point.left, point.top);
00234 
00235         }
00236         else if (_sender == mWidgetStart)
00237         {
00238             // минимальное значение
00239             if (mScrollPosition == 0) return;
00240 
00241             // расчитываем следующее положение
00242             if (mScrollPosition > mScrollPage) mScrollPosition -= mScrollPage;
00243             else mScrollPosition = 0;
00244 
00245             // оповещаем
00246             eventScrollChangePosition(this, (int)mScrollPosition);
00247             updateTrack();
00248 
00249         }
00250         else if (_sender == mWidgetEnd)
00251         {
00252             // максимальное значение
00253             if ( (mScrollRange < 2) || (mScrollPosition >= (mScrollRange-1)) ) return;
00254 
00255             // расчитываем следующее положение
00256             if ((mScrollPosition + mScrollPage) < (mScrollRange-1)) mScrollPosition += mScrollPage;
00257             else mScrollPosition = mScrollRange - 1;
00258 
00259             // оповещаем
00260             eventScrollChangePosition(this, (int)mScrollPosition);
00261             updateTrack();
00262 
00263         }
00264         else if (_sender == mWidgetFirstPart)
00265         {
00266             // минимальное значение
00267             if (mScrollPosition == 0) return;
00268 
00269             // расчитываем следующее положение
00270             if (mScrollPosition > mScrollViewPage) mScrollPosition -= mScrollViewPage;
00271             else mScrollPosition = 0;
00272 
00273             // оповещаем
00274             eventScrollChangePosition(this, (int)mScrollPosition);
00275             updateTrack();
00276 
00277         }
00278         else if (_sender == mWidgetSecondPart)
00279         {
00280             // максимальное значение
00281             if ( (mScrollRange < 2) || (mScrollPosition >= (mScrollRange-1)) ) return;
00282 
00283             // расчитываем следующее положение
00284             if ((mScrollPosition + mScrollViewPage) < (mScrollRange-1)) mScrollPosition += mScrollViewPage;
00285             else mScrollPosition = mScrollRange - 1;
00286 
00287             // оповещаем
00288             eventScrollChangePosition(this, (int)mScrollPosition);
00289             updateTrack();
00290 
00291         }
00292         else if (_sender == mWidgetTrack)
00293         {
00294             mPreActionOffset.left = _sender->getLeft();
00295             mPreActionOffset.top = _sender->getTop();
00296         }
00297     }
00298 
00299     void VScroll::notifyMouseReleased(Widget* _sender, int _left, int _top, MouseButton _id)
00300     {
00301         updateTrack();
00302     }
00303 
00304     void VScroll::notifyMouseDrag(Widget* _sender, int _left, int _top)
00305     {
00306         TrackMove(_left, _top);
00307     }
00308 
00309     void VScroll::setScrollRange(size_t _range)
00310     {
00311         if (_range == mScrollRange) return;
00312         mScrollRange = _range;
00313         mScrollPosition = (mScrollPosition < mScrollRange) ? mScrollPosition : 0;
00314         updateTrack();
00315     }
00316 
00317     void VScroll::setScrollPosition(size_t _position)
00318     {
00319         if (_position == mScrollPosition) return;
00320         if (_position >= mScrollRange) _position = 0;
00321         mScrollPosition = _position;
00322         updateTrack();
00323     }
00324 
00325     void VScroll::setPosition(const IntPoint& _point)
00326     {
00327         Base::setPosition(_point);
00328     }
00329 
00330     void VScroll::setSize(const IntSize& _size)
00331     {
00332         Base::setSize(_size);
00333         // обновляем трек
00334         updateTrack();
00335     }
00336 
00337     void VScroll::setCoord(const IntCoord& _coord)
00338     {
00339         Base::setCoord(_coord);
00340         // обновляем трек
00341         updateTrack();
00342     }
00343 
00344     void VScroll::setTrackSize(int _size)
00345     {
00346         if (mWidgetTrack != nullptr)
00347             mWidgetTrack->setSize(mWidgetTrack->getWidth(), ((int)_size < (int)mMinTrackSize)? (int)mMinTrackSize : (int)_size);
00348         updateTrack();
00349     }
00350 
00351     int VScroll::getTrackSize()
00352     {
00353         return mWidgetTrack == nullptr ? 1 : mWidgetTrack->getHeight();
00354     }
00355 
00356     int VScroll::getLineSize()
00357     {
00358         return mCoord.height - (int)(mSkinRangeStart + mSkinRangeEnd);
00359     }
00360 
00361     void VScroll::onMouseWheel(int _rel)
00362     {
00363         notifyMouseWheel(nullptr, _rel);
00364 
00365         Base::onMouseWheel(_rel);
00366     }
00367 
00368     void VScroll::notifyMouseWheel(Widget* _sender, int _rel)
00369     {
00370         if (mScrollRange < 2) return;
00371 
00372         int offset = mScrollPosition;
00373         if (_rel < 0) offset += SCROLL_MOUSE_WHEEL;
00374         else offset -= SCROLL_MOUSE_WHEEL;
00375 
00376         if (offset < 0) offset = 0;
00377         else if (offset > (int)(mScrollRange - 1)) offset = mScrollRange - 1;
00378 
00379         if ((size_t)offset != mScrollPosition)
00380         {
00381             mScrollPosition = offset;
00382             // оповещаем
00383             eventScrollChangePosition(this, (int)mScrollPosition);
00384             updateTrack();
00385         }
00386     }
00387 
00388     void VScroll::setProperty(const std::string& _key, const std::string& _value)
00389     {
00390         if (_key == "Scroll_Range") setScrollRange(utility::parseValue<size_t>(_value));
00391         else if (_key == "Scroll_Position") setScrollPosition(utility::parseValue<size_t>(_value));
00392         else if (_key == "Scroll_Page") setScrollPage(utility::parseValue<size_t>(_value));
00393         else if (_key == "Scroll_ViewPage") setScrollViewPage(utility::parseValue<size_t>(_value));
00394         else if (_key == "Scroll_MoveToClick") setMoveToClick(utility::parseValue<bool>(_value));
00395         else
00396         {
00397             Base::setProperty(_key, _value);
00398             return;
00399         }
00400         eventChangeProperty(this, _key, _value);
00401     }
00402 
00403 } // namespace MyGUI