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 dropdown.
22 */
23
24 #include "dropdown.h"
25 #include "label.h"
26 #include "widgbase.h"
27 #include "form.h"
28 #include "lib/ivis_opengl/pieblitfunc.h"
29 #include "lib/framework/input.h"
30
31 class DropdownItemWrapper;
32
33 class DropdownOverlay: public WIDGET
34 {
35 public:
DropdownOverlay(std::function<void ()> onClickedFunc)36 DropdownOverlay(std::function<void ()> onClickedFunc): onClickedFunc(onClickedFunc)
37 {
38 setCalcLayout(LAMBDA_CALCLAYOUT_SIMPLE({
39 psWidget->setGeometry(0, 0, screenWidth - 1, screenHeight - 1);
40 }));
41 }
42
clicked(W_CONTEXT *,WIDGET_KEY=WKEY_PRIMARY)43 void clicked(W_CONTEXT *, WIDGET_KEY = WKEY_PRIMARY) override
44 {
45 onClickedFunc();
46 }
47
48 std::function<void ()> onClickedFunc;
49 };
50
initialize(const std::shared_ptr<WIDGET> & newItem,DropdownOnSelectHandler newOnSelect)51 void DropdownItemWrapper::initialize(const std::shared_ptr<WIDGET> &newItem, DropdownOnSelectHandler newOnSelect)
52 {
53 item = newItem;
54 setGeometry(item->geometry());
55 attach(item);
56 onSelect = newOnSelect;
57 }
58
processClickRecursive(W_CONTEXT * psContext,WIDGET_KEY key,bool wasPressed)59 bool DropdownItemWrapper::processClickRecursive(W_CONTEXT *psContext, WIDGET_KEY key, bool wasPressed)
60 {
61 auto result = WIDGET::processClickRecursive(psContext, key, wasPressed);
62
63 if (key != WKEY_NONE && wasPressed && item->isMouseOverWidget())
64 {
65 onSelect(std::static_pointer_cast<DropdownItemWrapper>(shared_from_this()));
66 }
67
68 return result;
69 }
70
geometryChanged()71 void DropdownItemWrapper::geometryChanged()
72 {
73 item->setGeometry(
74 padding.left,
75 padding.top,
76 width() - padding.left - padding.right,
77 height() - padding.top - padding.bottom
78 );
79 }
80
display(int xOffset,int yOffset)81 void DropdownItemWrapper::display(int xOffset, int yOffset)
82 {
83 if (selected)
84 {
85 auto x0 = xOffset + x();
86 auto y0 = yOffset + y();
87 pie_UniTransBoxFill(x0, y0, x0 + width(), y0 + height(), WZCOL_MENU_SCORE_BUILT);
88 }
89 }
90
DropdownWidget()91 DropdownWidget::DropdownWidget()
92 {
93 itemsList = ScrollableListWidget::make();
94 itemsList->setBackgroundColor(WZCOL_MENU_BACKGROUND);
95 setListHeight(100);
96 }
97
run(W_CONTEXT * psContext)98 void DropdownWidget::run(W_CONTEXT *psContext)
99 {
100 if (overlayScreen)
101 {
102 itemsList->setGeometry(screenPosX(), screenPosY() + height(), width(), itemsList->height());
103
104 if (keyPressed(KEY_ESC))
105 {
106 inputLoseFocus(); // clear the input buffer.
107 close();
108 }
109 }
110 }
111
geometryChanged()112 void DropdownWidget::geometryChanged()
113 {
114 for(auto& item : items)
115 {
116 item->setGeometry(0, 0, width(), height());
117 }
118 }
119
clicked(W_CONTEXT * psContext,WIDGET_KEY key)120 void DropdownWidget::clicked(W_CONTEXT *psContext, WIDGET_KEY key)
121 {
122 open();
123 }
124
open()125 void DropdownWidget::open()
126 {
127 if (overlayScreen != nullptr) { return; }
128 std::weak_ptr<DropdownWidget> pWeakThis(std::dynamic_pointer_cast<DropdownWidget>(shared_from_this()));
129 widgScheduleTask([pWeakThis]() {
130 if (auto dropdownWidget = pWeakThis.lock())
131 {
132 dropdownWidget->overlayScreen = W_SCREEN::make();
133 widgRegisterOverlayScreenOnTopOfScreen(dropdownWidget->overlayScreen, dropdownWidget->screenPointer.lock());
134 dropdownWidget->itemsList->setGeometry(dropdownWidget->screenPosX(), dropdownWidget->screenPosY() + dropdownWidget->height(), dropdownWidget->width(), dropdownWidget->itemsList->height());
135 dropdownWidget->overlayScreen->psForm->attach(dropdownWidget->itemsList);
136 dropdownWidget->overlayScreen->psForm->attach(std::make_shared<DropdownOverlay>([pWeakThis]() { if (auto dropdownWidget = pWeakThis.lock()) { dropdownWidget->close(); } }));
137 }
138 });
139 }
140
close()141 void DropdownWidget::close()
142 {
143 std::weak_ptr<DropdownWidget> pWeakThis(std::dynamic_pointer_cast<DropdownWidget>(shared_from_this()));
144 widgScheduleTask([pWeakThis]() {
145 if (auto dropdownWidget = pWeakThis.lock())
146 {
147 widgRemoveOverlayScreen(dropdownWidget->overlayScreen);
148 dropdownWidget->overlayScreen->psForm->detach(dropdownWidget->itemsList);
149 dropdownWidget->overlayScreen = nullptr;
150 }
151 });
152 }
153
display(int xOffset,int yOffset)154 void DropdownWidget::display(int xOffset, int yOffset)
155 {
156 auto x0 = xOffset + x();
157 auto y0 = yOffset + y();
158
159 if (overlayScreen)
160 {
161 pie_UniTransBoxFill(x0, y0, x0 + width(), y0 + height(), WZCOL_MENU_SCORE_BUILT);
162 }
163
164 if (selectedItem)
165 {
166 WidgetGraphicsContext context;
167 context = context.translatedBy(x0, y0);
168 selectedItem->getItem()->displayRecursive(context);
169 }
170 }
171
addItem(const std::shared_ptr<WIDGET> & item)172 void DropdownWidget::addItem(const std::shared_ptr<WIDGET> &item)
173 {
174 auto itemOnSelect = [this](std::shared_ptr<DropdownItemWrapper> selected) {
175 if (!overlayScreen) {
176 open();
177 return;
178 }
179
180 select(selected);
181 close();
182 };
183
184 auto wrapper = DropdownItemWrapper::make(item, itemOnSelect);
185 wrapper->setPadding(itemPadding);
186 wrapper->setGeometry(0, 0, width(), height());
187
188 items.push_back(wrapper);
189 itemsList->addItem(wrapper);
190 }
191
processClickRecursive(W_CONTEXT * psContext,WIDGET_KEY key,bool wasPressed)192 bool DropdownWidget::processClickRecursive(W_CONTEXT *psContext, WIDGET_KEY key, bool wasPressed)
193 {
194 if (!overlayScreen && selectedItem)
195 {
196 W_CONTEXT shiftedContext(psContext);
197 auto deltaX = x() - selectedItem->x();
198 auto deltaY = y() - selectedItem->y();
199 shiftedContext.mx = psContext->mx - deltaX;
200 shiftedContext.my = psContext->my - deltaY;
201 shiftedContext.xOffset = psContext->xOffset + deltaX;
202 shiftedContext.yOffset = psContext->yOffset + deltaY;
203 selectedItem->processClickRecursive(&shiftedContext, key, wasPressed);
204 }
205
206 return WIDGET::processClickRecursive(psContext, key, wasPressed);
207 }
208