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