| 1 | /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ |
| 2 | /* If you are missing that file, acquire a complete release at teeworlds.com. */ |
| 3 | #ifndef GAME_CLIENT_UI_SCROLLREGION_H |
| 4 | #define GAME_CLIENT_UI_SCROLLREGION_H |
| 5 | |
| 6 | #include "ui.h" |
| 7 | |
| 8 | struct CScrollRegionParams |
| 9 | { |
| 10 | float m_ScrollbarWidth; |
| 11 | float m_ScrollbarMargin; |
| 12 | bool m_ScrollbarNoMarginRight; |
| 13 | float m_SliderMinHeight; |
| 14 | float m_ScrollUnit; |
| 15 | ColorRGBA m_ClipBgColor; |
| 16 | ColorRGBA m_ScrollbarBgColor; |
| 17 | ColorRGBA m_RailBgColor; |
| 18 | ColorRGBA m_SliderColor; |
| 19 | ColorRGBA m_SliderColorHover; |
| 20 | ColorRGBA m_SliderColorGrabbed; |
| 21 | unsigned m_Flags; |
| 22 | |
| 23 | enum |
| 24 | { |
| 25 | FLAG_CONTENT_STATIC_WIDTH = 1 << 0, |
| 26 | }; |
| 27 | |
| 28 | CScrollRegionParams() |
| 29 | { |
| 30 | m_ScrollbarWidth = 20.0f; |
| 31 | m_ScrollbarMargin = 5.0f; |
| 32 | m_ScrollbarNoMarginRight = false; |
| 33 | m_SliderMinHeight = 25.0f; |
| 34 | m_ScrollUnit = 10.0f; |
| 35 | m_ClipBgColor = ColorRGBA(0.0f, 0.0f, 0.0f, 0.0f); |
| 36 | m_ScrollbarBgColor = ColorRGBA(0.0f, 0.0f, 0.0f, 0.0f); |
| 37 | m_RailBgColor = ColorRGBA(1.0f, 1.0f, 1.0f, 0.25f); |
| 38 | m_SliderColor = ColorRGBA(0.8f, 0.8f, 0.8f, 1.0f); |
| 39 | m_SliderColorHover = ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f); |
| 40 | m_SliderColorGrabbed = ColorRGBA(0.9f, 0.9f, 0.9f, 1.0f); |
| 41 | m_Flags = 0; |
| 42 | } |
| 43 | |
| 44 | ColorRGBA SliderColor(bool Active, bool Hovered) const |
| 45 | { |
| 46 | if(Active) |
| 47 | return m_SliderColorGrabbed; |
| 48 | else if(Hovered) |
| 49 | return m_SliderColorHover; |
| 50 | return m_SliderColor; |
| 51 | } |
| 52 | }; |
| 53 | |
| 54 | /* |
| 55 | Usage example: |
| 56 | |
| 57 | // -- Layout -- |
| 58 | CUIRect View = ...; // parent UI rect initialized elsewhere |
| 59 | CUIRect Content; // rect for scrollable content |
| 60 | View.HSplitTop(500.0f, &Content, &View); // split maximum size of scrollable content |
| 61 | |
| 62 | // -- Initialization -- |
| 63 | static CScrollRegion s_ScrollRegion; |
| 64 | s_ScrollRegion.Begin(&Content); |
| 65 | // Content rect is now offset by the scroll offset |
| 66 | |
| 67 | // -- [Optional] Initialization with parameters -- |
| 68 | static CScrollRegion s_ScrollRegion; |
| 69 | CScrollRegionParams ScrollParams; |
| 70 | ScrollParams.m_ScrollUnit = 3 * LineHeight; |
| 71 | s_ScrollRegion.Begin(&Content, &ScrollParams); |
| 72 | // Content rect is now offset by the scroll offset |
| 73 | |
| 74 | // -- "Register" your content rects -- |
| 75 | CUIRect Rect; |
| 76 | Content.HSplitTop(SomeValue, &Rect, &Content); |
| 77 | s_ScrollRegion.AddRect(Rect); |
| 78 | |
| 79 | // -- [Optional] Knowing if a rect is clipped -- |
| 80 | s_ScrollRegion.RectClipped(Rect); |
| 81 | |
| 82 | // -- [Optional] Scroll to the last added rect -- |
| 83 | s_ScrollRegion.AddRect(Rect); |
| 84 | s_ScrollRegion.ScrollHere(Option); |
| 85 | |
| 86 | // -- [Convenience] Add rect and check for visibility at the same time -- |
| 87 | if(s_ScrollRegion.AddRect(Rect)) |
| 88 | { |
| 89 | // The rect is visible (not clipped) |
| 90 | } |
| 91 | |
| 92 | // -- [Convenience] Add rect and scroll to it if it's selected -- |
| 93 | if(s_ScrollRegion.AddRect(Rect, ScrollToSelection && IsSelected)) |
| 94 | { |
| 95 | // The rect is visible (not clipped) |
| 96 | } |
| 97 | |
| 98 | // -- End -- |
| 99 | s_ScrollRegion.End(); |
| 100 | */ |
| 101 | |
| 102 | // Instances of CScrollRegion must be static, as member addresses are used as UI item IDs |
| 103 | class CScrollRegion : private CUIElementBase |
| 104 | { |
| 105 | public: |
| 106 | enum EScrollRelative |
| 107 | { |
| 108 | SCROLLRELATIVE_UP = -1, |
| 109 | SCROLLRELATIVE_NONE = 0, |
| 110 | SCROLLRELATIVE_DOWN = 1, |
| 111 | }; |
| 112 | |
| 113 | private: |
| 114 | float m_ScrollY; |
| 115 | float m_ContentH; |
| 116 | float m_RequestScrollY; // [0, ContentHeight] |
| 117 | EScrollRelative m_ScrollDirection; |
| 118 | float m_ScrollSpeedMultiplier; |
| 119 | |
| 120 | float m_AnimTimeMax; |
| 121 | float m_AnimTime; |
| 122 | float m_AnimInitScrollY; |
| 123 | float m_AnimTargetScrollY; |
| 124 | |
| 125 | CUIRect m_ClipRect; |
| 126 | CUIRect m_RailRect; |
| 127 | CUIRect m_LastAddedRect; // saved for ScrollHere() |
| 128 | float m_SliderGrabPos; // where did user grab the slider |
| 129 | vec2 m_ContentScrollOff; |
| 130 | CScrollRegionParams m_Params; |
| 131 | |
| 132 | public: |
| 133 | enum EScrollOption |
| 134 | { |
| 135 | SCROLLHERE_KEEP_IN_VIEW = 0, |
| 136 | SCROLLHERE_TOP, |
| 137 | SCROLLHERE_BOTTOM, |
| 138 | }; |
| 139 | |
| 140 | CScrollRegion(); |
| 141 | void Reset(); |
| 142 | |
| 143 | void Begin(CUIRect *pClipRect, const CScrollRegionParams *pParams = nullptr); |
| 144 | void End(); |
| 145 | bool AddRect(const CUIRect &Rect, bool ShouldScrollHere = false); // returns true if the added rect is visible (not clipped) |
| 146 | void ScrollHere(EScrollOption Option = SCROLLHERE_KEEP_IN_VIEW); |
| 147 | void ScrollRelative(EScrollRelative Direction, float SpeedMultiplier = 1.0f); |
| 148 | void ScrollRelativeDirect(float ScrollAmount); |
| 149 | const CUIRect *ClipRect() const { return &m_ClipRect; } |
| 150 | void DoEdgeScrolling(); |
| 151 | bool RectClipped(const CUIRect &Rect) const; |
| 152 | bool ScrollbarShown() const; |
| 153 | bool Animating() const; |
| 154 | bool Active() const; |
| 155 | const CScrollRegionParams &Params() const { return m_Params; } |
| 156 | }; |
| 157 | |
| 158 | #endif |
| 159 | |