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 Begin(CUIRect *pClipRect, vec2 *pOutOffset, const CScrollRegionParams *pParams = nullptr); |
132 | void End(); |
133 | bool AddRect(const CUIRect &Rect, bool ShouldScrollHere = false); // returns true if the added rect is visible (not clipped) |
134 | void ScrollHere(EScrollOption Option = SCROLLHERE_KEEP_IN_VIEW); |
135 | void ScrollRelative(EScrollRelative Direction, float SpeedMultiplier = 1.0f); |
136 | const CUIRect *ClipRect() const { return &m_ClipRect; } |
137 | void DoEdgeScrolling(); |
138 | bool RectClipped(const CUIRect &Rect) const; |
139 | bool ScrollbarShown() const; |
140 | bool Animating() const; |
141 | bool Active() const; |
142 | const CScrollRegionParams &Params() const { return m_Params; } |
143 | }; |
144 | |
145 | #endif |
146 | |