1 /*
2 This file is part of Warzone 2100.
3 Copyright (C) 2020 Warzone 2100 Project
4
5 Warzone 2100 is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 Warzone 2100 is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with Warzone 2100; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19 /**
20 * @file
21 * Functions for scrollable list.
22 */
23
24 #include "scrollablelist.h"
25 #include "lib/framework/input.h"
26 #include "lib/ivis_opengl/pieblitfunc.h"
27
28 static const auto SCROLLBAR_WIDTH = 15;
29
initialize()30 void ScrollableListWidget::initialize()
31 {
32 attach(scrollBar = ScrollBarWidget::make());
33 attach(listView = std::make_shared<ClipRectWidget>());
34 scrollBar->show(false);
35 backgroundColor.rgba = 0;
36 }
37
geometryChanged()38 void ScrollableListWidget::geometryChanged()
39 {
40 scrollBar->setGeometry(width() - SCROLLBAR_WIDTH, 0, SCROLLBAR_WIDTH, height());
41 scrollBar->setViewSize(calculateListViewHeight());
42 layoutDirty = true;
43 }
44
run(W_CONTEXT * psContext)45 void ScrollableListWidget::run(W_CONTEXT *psContext)
46 {
47 updateLayout();
48 listView->setTopOffset(snapOffset ? snappedOffset() : scrollBar->position());
49 }
50
51 /**
52 * Snap offset to first visible child.
53 *
54 * This wouldn't be necessary if it were possible to clip the rendering.
55 */
snappedOffset()56 uint32_t ScrollableListWidget::snappedOffset()
57 {
58 for (auto child : listView->children())
59 {
60 if (child->y() >= scrollBar->position())
61 {
62 return child->y();
63 }
64 }
65
66 return 0;
67 }
68
addItem(const std::shared_ptr<WIDGET> & item)69 void ScrollableListWidget::addItem(const std::shared_ptr<WIDGET> &item)
70 {
71 listView->attach(item);
72 layoutDirty = true;
73 }
74
clear()75 void ScrollableListWidget::clear()
76 {
77 listView->removeAllChildren();
78 layoutDirty = true;
79 updateLayout();
80 listView->setTopOffset(0);
81 }
82
updateLayout()83 void ScrollableListWidget::updateLayout()
84 {
85 if (!layoutDirty) {
86 return;
87 }
88 layoutDirty = false;
89
90 auto listViewWidthWithoutScrollBar = calculateListViewWidth();
91 auto listViewWidthWithScrollBar = listViewWidthWithoutScrollBar - scrollBar->width();
92 auto listViewHeight = calculateListViewHeight();
93
94 resizeChildren(listViewWidthWithScrollBar);
95
96 scrollBar->show(scrollableHeight > listViewHeight);
97
98 if (scrollBar->visible())
99 {
100 listView->setGeometry(padding.left, padding.top, listViewWidthWithScrollBar, listViewHeight);
101 } else {
102 resizeChildren(listViewWidthWithoutScrollBar);
103 listView->setGeometry(padding.left, padding.top, listViewWidthWithoutScrollBar, listViewHeight);
104 }
105
106 scrollBar->setScrollableSize(scrollableHeight);
107 }
108
resizeChildren(uint32_t width)109 void ScrollableListWidget::resizeChildren(uint32_t width)
110 {
111 scrollableHeight = 0;
112 auto nextOffset = 0;
113 for (auto child : listView->children())
114 {
115 child->setGeometry(0, nextOffset, width, child->height());
116 scrollableHeight = nextOffset + child->height();
117 nextOffset = scrollableHeight + itemSpacing;
118 }
119 }
120
calculateListViewHeight() const121 uint32_t ScrollableListWidget::calculateListViewHeight() const
122 {
123 return height() - padding.top - padding.bottom;
124 }
125
calculateListViewWidth() const126 uint32_t ScrollableListWidget::calculateListViewWidth() const
127 {
128 return width() - padding.left - padding.right;
129 }
130
processClickRecursive(W_CONTEXT * psContext,WIDGET_KEY key,bool wasPressed)131 bool ScrollableListWidget::processClickRecursive(W_CONTEXT *psContext, WIDGET_KEY key, bool wasPressed)
132 {
133 scrollBar->incrementPosition(-getMouseWheelSpeed().y * 20);
134 return WIDGET::processClickRecursive(psContext, key, wasPressed);
135 }
136
enableScroll()137 void ScrollableListWidget::enableScroll()
138 {
139 scrollBar->enable();
140 }
141
disableScroll()142 void ScrollableListWidget::disableScroll()
143 {
144 scrollBar->disable();
145 }
146
setStickToBottom(bool value)147 void ScrollableListWidget::setStickToBottom(bool value)
148 {
149 scrollBar->setStickToBottom(value);
150 }
151
setPadding(Padding const & rect)152 void ScrollableListWidget::setPadding(Padding const &rect)
153 {
154 padding = rect;
155 layoutDirty = true;
156 }
157
setBackgroundColor(PIELIGHT const & color)158 void ScrollableListWidget::setBackgroundColor(PIELIGHT const &color)
159 {
160 backgroundColor = color;
161 }
162
setSnapOffset(bool value)163 void ScrollableListWidget::setSnapOffset(bool value)
164 {
165 snapOffset = value;
166 }
167
setItemSpacing(uint32_t value)168 void ScrollableListWidget::setItemSpacing(uint32_t value)
169 {
170 itemSpacing = value;
171 }
172
display(int xOffset,int yOffset)173 void ScrollableListWidget::display(int xOffset, int yOffset)
174 {
175 if (backgroundColor.rgba != 0)
176 {
177 int x0 = x() + xOffset;
178 int y0 = y() + yOffset;
179 pie_UniTransBoxFill(x0, y0, x0 + width(), y0 + height(), backgroundColor);
180 }
181 }
182
displayRecursive(WidgetGraphicsContext const & context)183 void ScrollableListWidget::displayRecursive(WidgetGraphicsContext const& context)
184 {
185 updateLayout();
186 WIDGET::displayRecursive(context);
187 }
188
getScrollbarWidth() const189 int ScrollableListWidget::getScrollbarWidth() const
190 {
191 return SCROLLBAR_WIDTH;
192 }
193
getScrollPosition() const194 uint16_t ScrollableListWidget::getScrollPosition() const
195 {
196 return scrollBar->position();
197 }
198
setScrollPosition(uint16_t newPosition)199 void ScrollableListWidget::setScrollPosition(uint16_t newPosition)
200 {
201 updateLayout();
202 scrollBar->setPosition(newPosition);
203 listView->setTopOffset(snapOffset ? snappedOffset() : scrollBar->position());
204 }
205