| 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: |
| 56 | -- Initialization -- |
| 57 | static CScrollRegion s_ScrollRegion; |
| 58 | vec2 ScrollOffset(0, 0); |
| 59 | s_ScrollRegion.Begin(&ScrollRegionRect, &ScrollOffset); |
| 60 | Content = ScrollRegionRect; |
| 61 | Content.y += ScrollOffset.y; |
| 62 | |
| 63 | -- "Register" your content rects -- |
| 64 | CUIRect Rect; |
| 65 | Content.HSplitTop(SomeValue, &Rect, &Content); |
| 66 | s_ScrollRegion.AddRect(Rect); |
| 67 | |
| 68 | -- [Optional] Knowing if a rect is clipped -- |
| 69 | s_ScrollRegion.RectClipped(Rect); |
| 70 | |
| 71 | -- [Optional] Scroll to a rect (to the last added rect)-- |
| 72 | ... |
| 73 | s_ScrollRegion.AddRect(Rect); |
| 74 | s_ScrollRegion.ScrollHere(Option); |
| 75 | |
| 76 | -- [Convenience] Add rect and check for visibility at the same time |
| 77 | if(s_ScrollRegion.AddRect(Rect)) |
| 78 | // The rect is visible (not clipped) |
| 79 | |
| 80 | -- [Convenience] Add rect and scroll to it if it's selected |
| 81 | if(s_ScrollRegion.AddRect(Rect, ScrollToSelection && IsSelected)) |
| 82 | // The rect is visible (not clipped) |
| 83 | |
| 84 | -- End -- |
| 85 | s_ScrollRegion.End(); |
| 86 | */ |
| 87 | |
| 88 | // Instances of CScrollRegion must be static, as member addresses are used as UI item IDs |
| 89 | class CScrollRegion : private CUIElementBase |
| 90 | { |
| 91 | public: |
| 92 | // TODO: Properly fix whatever is causing the 1-pixel discrepancy in scrolling rect height and remove this magic value. |
| 93 | // Currently this must be added when calculating the required height of a UI rect for a scroll region to get a perfect fit. |
| 94 | static constexpr float HEIGHT_MAGIC_FIX = 1.0f; |
| 95 | |
| 96 | enum EScrollRelative |
| 97 | { |
| 98 | SCROLLRELATIVE_UP = -1, |
| 99 | SCROLLRELATIVE_NONE = 0, |
| 100 | SCROLLRELATIVE_DOWN = 1, |
| 101 | }; |
| 102 | |
| 103 | private: |
| 104 | float m_ScrollY; |
| 105 | float m_ContentH; |
| 106 | float m_RequestScrollY; // [0, ContentHeight] |
| 107 | EScrollRelative m_ScrollDirection; |
| 108 | float m_ScrollSpeedMultiplier; |
| 109 | |
| 110 | float m_AnimTimeMax; |
| 111 | float m_AnimTime; |
| 112 | float m_AnimInitScrollY; |
| 113 | float m_AnimTargetScrollY; |
| 114 | |
| 115 | CUIRect m_ClipRect; |
| 116 | CUIRect m_RailRect; |
| 117 | CUIRect m_LastAddedRect; // saved for ScrollHere() |
| 118 | float m_SliderGrabPos; // where did user grab the slider |
| 119 | vec2 m_ContentScrollOff; |
| 120 | CScrollRegionParams m_Params; |
| 121 | |
| 122 | public: |
| 123 | enum EScrollOption |
| 124 | { |
| 125 | SCROLLHERE_KEEP_IN_VIEW = 0, |
| 126 | SCROLLHERE_TOP, |
| 127 | SCROLLHERE_BOTTOM, |
| 128 | }; |
| 129 | |
| 130 | CScrollRegion(); |
| 131 | void Reset(); |
| 132 | |
| 133 | void Begin(CUIRect *pClipRect, vec2 *pOutOffset, const CScrollRegionParams *pParams = nullptr); |
| 134 | void End(); |
| 135 | bool AddRect(const CUIRect &Rect, bool ShouldScrollHere = false); // returns true if the added rect is visible (not clipped) |
| 136 | void ScrollHere(EScrollOption Option = SCROLLHERE_KEEP_IN_VIEW); |
| 137 | void ScrollRelative(EScrollRelative Direction, float SpeedMultiplier = 1.0f); |
| 138 | void ScrollRelativeDirect(float ScrollAmount); |
| 139 | const CUIRect *ClipRect() const { return &m_ClipRect; } |
| 140 | void DoEdgeScrolling(); |
| 141 | bool RectClipped(const CUIRect &Rect) const; |
| 142 | bool ScrollbarShown() const; |
| 143 | bool Animating() const; |
| 144 | bool Active() const; |
| 145 | const CScrollRegionParams &Params() const { return m_Params; } |
| 146 | }; |
| 147 | |
| 148 | #endif |
| 149 | |