1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "bladerunner/ui/ui_dropdown.h"
24
25 #include "bladerunner/audio_player.h"
26 #include "bladerunner/bladerunner.h"
27 #include "bladerunner/ui/kia.h"
28 #include "bladerunner/ui/ui_image_picker.h"
29 #include "bladerunner/ui/ui_scroll_box.h"
30 #include "bladerunner/subtitles.h"
31 #include "bladerunner/font.h"
32 #include "bladerunner/game_info.h"
33 #include "bladerunner/shape.h"
34 #include "bladerunner/game_constants.h"
35
36 #include "common/debug.h"
37
38 namespace BladeRunner {
39
40 const Color256 UIDropDown::kColors[] = {
41 { 0, 0, 0 }, // Black - unpressed (framing rectange)
42 { 16, 8, 8 },
43 { 32, 24, 8 },
44 { 56, 32, 16 },
45 { 72, 48, 16 },
46 { 88, 56, 24 }, // Mouse-over (framing rectange)
47 { 104, 72, 32 },
48 { 128, 80, 40 },
49 { 136, 96, 48 },
50 { 152, 112, 56 },
51 { 168, 128, 72 } // Pressed (framing rectange)
52 };
53
UIDropDown(BladeRunnerEngine * vm,UIDropDownLineSelectedCallback * ddlLineSelectedCallback,UIDropDownGenericCallback * ddlCancelledCallback,UIDropDownGenericCallback * ddlTopFrameClickCallback,void * callbackData,Common::String labelStr,int controlLeftX,int controlTopY,int scrollBoxMaxLineCount)54 UIDropDown::UIDropDown(BladeRunnerEngine *vm,
55 UIDropDownLineSelectedCallback *ddlLineSelectedCallback,
56 UIDropDownGenericCallback *ddlCancelledCallback,
57 UIDropDownGenericCallback *ddlTopFrameClickCallback,
58 void *callbackData,
59 Common::String labelStr,
60 int controlLeftX,
61 int controlTopY,
62 int scrollBoxMaxLineCount) : UIComponent(vm) {
63
64 _isVisible = false;
65
66 _labelStr = labelStr;
67 _controlLeftX = MAX(controlLeftX, 0);
68 // TODO The id should be used to eg. grab info about the selected subtitles from an engine's subtitles object
69 _lineSelectedId = -1;
70
71 _lineSelectorFrameRectColor = 0;
72 _lineSelectorFrameRectHasFocus = false;
73 // A framing (outlining) rectangle to highlight the selected option field on top of the scrollbox
74 controlTopY = CLIP(controlTopY, 0, 600);
75 _lineSelectorFrameRect = Common::Rect(0, controlTopY, 0, controlTopY + kDropDownButtonShapeHeight);
76
77 // TODO This eventually should be set to a default probably by the outside caller class(kia_section_settings)
78 // Current explicit assignment only serves as placeholder / proof of concept
79 _lineSelectedStr = "English (SCUMMVM) v7 [ENG]";
80 _lineSelectorScrollBox = new UIScrollBox(_vm, scrollBoxLineSelectCallback, this, scrollBoxMaxLineCount, 2, false, Common::Rect(0, 0, 0, 55 + kFrameRectPaddingPx), Common::Rect(0, 0, 0, 55));
81 _lineSelectorScrollBoxMaxLineWidth = 0;
82
83 _lineDropdownBtn = new UIImagePicker(_vm, 2);
84
85 _ddlLineSelectedCallback = ddlLineSelectedCallback;
86 _ddlCancelledCallback = ddlCancelledCallback;
87 _ddlTopFrameClickCallback = ddlTopFrameClickCallback;
88 _callbackData = callbackData;
89
90 _mouseX = 0;
91 _mouseY = 0;
92 }
93
~UIDropDown()94 UIDropDown::~UIDropDown() {
95 delete _lineSelectorScrollBox;
96 delete _lineDropdownBtn;
97 }
98
activate()99 void UIDropDown::activate() {
100 _lineDropdownBtn->resetImages();
101 // Actual button shape
102 // defineImage actually increases internally the bottom and right bounds for the rect to be inclusive (for the contains() method)
103 _lineDropdownBtn->defineImage(0, Common::Rect(0, _lineSelectorFrameRect.top + 1, kDropDownButtonShapeWidth - 1, _lineSelectorFrameRect.bottom - 1), _vm->_kia->_shapes->get(73), _vm->_kia->_shapes->get(74), _vm->_kia->_shapes->get(75), nullptr);
104 // Clickable Selected/Active Line Description area
105 _lineDropdownBtn->defineImage(1, Common::Rect(0, _lineSelectorFrameRect.top, kDropDownButtonShapeWidth - 1, _lineSelectorFrameRect.bottom - 1), nullptr, nullptr, nullptr, nullptr);
106 _lineDropdownBtn->activate(nullptr, nullptr, mouseDownLDBCallback, nullptr, this);
107
108 _lineSelectorScrollBox->setBoxTop(_lineSelectorFrameRect.bottom);
109 _lineSelectorScrollBox->setScrollbarTop(_lineSelectorFrameRect.bottom);
110
111 _lineSelectorScrollBox->hide(); // show upon click on field or dropdown button
112 show();
113 }
114
115
deactivate()116 void UIDropDown::deactivate() {
117 _isVisible = false;
118
119 _lineDropdownBtn->deactivate();
120 _lineSelectorScrollBox->hide();
121 }
122
draw(Graphics::Surface & surface)123 void UIDropDown::draw(Graphics::Surface &surface) {
124 if (!_isVisible) {
125 return;
126 }
127
128 int posStartOfSelectedLineDesc = _controlLeftX + _vm->_mainFont->getStringWidth(_labelStr) + _vm->_mainFont->getCharWidth(' ');
129 _vm->_mainFont->drawString(&surface, _labelStr, _controlLeftX, _lineSelectorFrameRect.top, surface.w, surface.format.RGBToColor(232, 208, 136));
130 _vm->_mainFont->drawString(&surface, _lineSelectedStr,
131 posStartOfSelectedLineDesc,
132 _lineSelectorFrameRect.top, surface.w, surface.format.RGBToColor(240, 232, 192));
133
134 // TODO add a clipping for description field here
135 int posEndOfSelectedLineDesc = posStartOfSelectedLineDesc + _vm->_mainFont->getStringWidth(_lineSelectedStr) + _vm->_mainFont->getCharWidth(' ');
136
137 _lineDropdownBtn->setImageLeft(0, posEndOfSelectedLineDesc );
138
139 _lineDropdownBtn->setImageLeft(1, posStartOfSelectedLineDesc - kFrameRectPaddingPx);
140 _lineDropdownBtn->setImageWidth(1, posEndOfSelectedLineDesc + kFrameRectPaddingPx - posStartOfSelectedLineDesc);
141
142 _lineDropdownBtn->draw(surface);
143 // _lineDropdownBtn->drawTooltip(surface, _mouseX, _mouseY);
144
145 _lineSelectorFrameRect.moveTo(posStartOfSelectedLineDesc - kFrameRectPaddingPx, _lineSelectorFrameRect.top);
146 _lineSelectorFrameRect.setWidth(posEndOfSelectedLineDesc + kDropDownButtonShapeWidth + kFrameRectPaddingPx - posStartOfSelectedLineDesc);
147
148 _lineSelectorScrollBox->draw(surface);
149
150 int lineSelectorFrameRectTargetColor;
151 if (_lineSelectorScrollBox->isVisible()) {
152 lineSelectorFrameRectTargetColor = 10;
153 } else if (_lineSelectorFrameRectHasFocus) {
154 lineSelectorFrameRectTargetColor = 5;
155 } else {
156 lineSelectorFrameRectTargetColor = 0;
157 }
158
159 // Ensures animated transition of the frame's (outlining rectangle's) color to the new one
160 if (_lineSelectorFrameRectColor < lineSelectorFrameRectTargetColor) {
161 ++_lineSelectorFrameRectColor;
162 }
163
164 // Ensures animated transition of the frame's (outlining rectangle's) color to the new one
165 if (_lineSelectorFrameRectColor > lineSelectorFrameRectTargetColor) {
166 --_lineSelectorFrameRectColor;
167 }
168 surface.frameRect(_lineSelectorFrameRect,
169 surface.format.RGBToColor(kColors[_lineSelectorFrameRectColor].r,
170 kColors[_lineSelectorFrameRectColor].g,
171 kColors[_lineSelectorFrameRectColor].b));
172 }
173
show()174 void UIDropDown::show() {
175 _isVisible = true;
176 }
177
hide()178 void UIDropDown::hide() {
179 _isVisible = false;
180 }
181
isVisible()182 bool UIDropDown::isVisible() {
183 return _isVisible;
184 }
185
isDropDownMenuExpanded()186 bool UIDropDown::isDropDownMenuExpanded() {
187 return _lineSelectorScrollBox->isVisible();
188 }
189
clearLines()190 void UIDropDown::clearLines() {
191 _lineSelectorScrollBox->clearLines();
192 _lineSelectorScrollBoxMaxLineWidth = 0;
193 }
194
addLine(const Common::String & text,int lineData)195 void UIDropDown::addLine(const Common::String &text, int lineData) {
196 _lineSelectorScrollBox->addLine(text, lineData, 0x08);
197 _lineSelectorScrollBoxMaxLineWidth = MAX(_vm->_mainFont->getStringWidth(text), _lineSelectorScrollBoxMaxLineWidth);
198 }
199
addLine(const char * text,int lineData)200 void UIDropDown::addLine(const char *text, int lineData) {
201 _lineSelectorScrollBox->addLine(text, lineData, 0x08);
202 _lineSelectorScrollBoxMaxLineWidth = MAX(_vm->_mainFont->getStringWidth(text), _lineSelectorScrollBoxMaxLineWidth);
203 }
204
sortLines()205 void UIDropDown::sortLines() {
206 _lineSelectorScrollBox->sortLines();
207 }
208
handleMouseMove(int mouseX,int mouseY)209 void UIDropDown::handleMouseMove(int mouseX, int mouseY) {
210 if (!_isVisible) {
211 return;
212 }
213
214 _mouseX = mouseX;
215 _mouseY = mouseY;
216
217 // contains() does not include right or bottom boundary "line"
218 if (_lineSelectorFrameRect.contains(mouseX, mouseY)) {
219 if (!_lineSelectorFrameRectHasFocus && !_lineSelectorScrollBox->isVisible()) {
220 _vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(kSfxTEXT3), 100, 0, 0, 50, 0);
221 }
222 _lineSelectorFrameRectHasFocus = true;
223 } else {
224 _lineSelectorFrameRectHasFocus = false;
225 }
226
227 _lineSelectorScrollBox->handleMouseMove(mouseX, mouseY);
228 _lineDropdownBtn->handleMouseAction(mouseX, mouseY, false, false, false);
229 }
230
handleMouseDown(bool alternateButton)231 void UIDropDown::handleMouseDown(bool alternateButton) {
232 if (!_isVisible) {
233 return;
234 }
235
236 if (!alternateButton) {
237 _lineSelectorScrollBox->handleMouseDown(false);
238 _lineDropdownBtn->handleMouseAction(_mouseX, _mouseY, true, false, false);
239 if (!_lineSelectorFrameRectHasFocus
240 && _lineSelectorScrollBox->isVisible()
241 && !_lineSelectorScrollBox->hasFocus()
242 ) {
243 _ddlCancelledCallback(_callbackData, this);
244 showSelectionDropdown(false);
245 }
246 }
247 }
248
handleMouseScroll(int direction)249 void UIDropDown::handleMouseScroll(int direction) {
250 if (!_isVisible) {
251 return;
252 }
253
254 if (_lineSelectorScrollBox->isVisible()) {
255 _lineSelectorScrollBox->handleMouseScroll(direction);
256 }
257 }
258
handleMouseUp(bool alternateButton)259 void UIDropDown::handleMouseUp(bool alternateButton) {
260 if (!_isVisible) {
261 return;
262 }
263
264 if (!alternateButton) {
265 _lineSelectorScrollBox->handleMouseUp(false);
266 _lineDropdownBtn->handleMouseAction(_mouseX, _mouseY, false, true, false);
267 }
268 }
269
scrollBoxLineSelectCallback(void * callbackData,void * source,int lineData,int mouseButton)270 void UIDropDown::scrollBoxLineSelectCallback(void *callbackData, void *source, int lineData, int mouseButton) {
271 UIDropDown *self = (UIDropDown *)callbackData;
272
273 if (source == self->_lineSelectorScrollBox && lineData >= 0) {
274 Common::String selectedLangDescStr = self->_lineSelectorScrollBox->getLineText(lineData);
275 self->_lineSelectedId = lineData;
276 self->_lineSelectedStr = selectedLangDescStr;
277 self->_vm->_audioPlayer->playAud(self->_vm->_gameInfo->getSfxTrack(kSfxELECBP1), 90, 0, 0, 50, 0);
278 self->_ddlLineSelectedCallback(self->_callbackData, self, lineData, mouseButton);
279 self->showSelectionDropdown(false);
280 //debug("text selected: %s", selectedLangDescStr.c_str());
281 }
282 }
283
284 // Callback from _lineDropdownBtn items
mouseDownLDBCallback(int buttonId,void * callbackData)285 void UIDropDown::mouseDownLDBCallback(int buttonId, void *callbackData) {
286 UIDropDown *self = (UIDropDown *)callbackData;
287 self->onButtonPressed(buttonId);
288 }
289
onButtonPressed(int buttonId)290 void UIDropDown::onButtonPressed(int buttonId) {
291 switch (buttonId) {
292 case 0:
293 // Pressed DDL dropdown button (0)
294 // fall through
295 case 1:
296 // if (buttonId == 1) {
297 // // Pressed DDL clickable area (1)
298 // debug("Pressed DDL clickable area (1)");
299 // }
300 _ddlTopFrameClickCallback(_callbackData, this);
301 showSelectionDropdown(!_lineSelectorScrollBox->isVisible());
302 break;
303 default:
304 return;
305 }
306 }
307
showSelectionDropdown(bool showToggle)308 void UIDropDown::showSelectionDropdown(bool showToggle) {
309 int prevDropdownBtnLeft = _lineDropdownBtn->getImageLeft(0);
310 if (showToggle) {
311 _lineSelectorScrollBox->setBoxTop(_lineSelectorFrameRect.bottom);
312 _lineSelectorScrollBox->setBoxLeft(_lineDropdownBtn->getImageLeft(1));
313 // TODO width should be retrieved from the maximum width of a language description in SUBTITLES.MIX (or a max width to clip to)
314 _lineSelectorScrollBox->setBoxWidth(MAX(_lineDropdownBtn->getImageWidth(1), _lineSelectorScrollBoxMaxLineWidth + _vm->_mainFont->getCharWidth(' ')));
315
316 if (_lineDropdownBtn->getImageLeft(0) < kFurthestLeftForScrollBar) {
317 // CLIP expects the first boundary argument to be the min of the two.
318 _lineSelectorScrollBox->setScrollbarLeft(CLIP( _lineSelectorScrollBox->getBoxLeft() + _lineSelectorScrollBox->getBoxWidth(), _lineDropdownBtn->getImageLeft(0), kFurthestLeftForScrollBar));
319 } else {
320 _lineSelectorScrollBox->setScrollbarLeft(MAX( _lineSelectorScrollBox->getBoxLeft() + _lineSelectorScrollBox->getBoxWidth(), kFurthestLeftForScrollBar));
321 }
322
323 _lineSelectorScrollBox->setScrollbarTop(_lineSelectorFrameRect.bottom);
324 _lineSelectorScrollBox->setScrollbarWidth(kDropDownButtonShapeWidth);
325 _lineSelectorScrollBox->show();
326 // change dropdown button icon too
327 _lineDropdownBtn->resetActiveImage(0);
328 _lineDropdownBtn->defineImage(0, Common::Rect(prevDropdownBtnLeft, _lineSelectorFrameRect.top + 1, prevDropdownBtnLeft + kDropDownButtonShapeWidth - 1, _lineSelectorFrameRect.bottom - 1), _vm->_kia->_shapes->get(70), _vm->_kia->_shapes->get(71), _vm->_kia->_shapes->get(72), nullptr);
329 _lineSelectorFrameRectColor = 10;
330 } else {
331 // hide scrollable area
332 _lineSelectorScrollBox->hide();
333 // change dropdown button icon too
334 _lineDropdownBtn->resetActiveImage(0);
335 _lineDropdownBtn->defineImage(0, Common::Rect(prevDropdownBtnLeft, _lineSelectorFrameRect.top + 1, prevDropdownBtnLeft + kDropDownButtonShapeWidth - 1, _lineSelectorFrameRect.bottom - 1), _vm->_kia->_shapes->get(73), _vm->_kia->_shapes->get(74), _vm->_kia->_shapes->get(75), nullptr);
336 _lineSelectorFrameRectColor = 0;
337 }
338 }
339
setLabelStr(Common::String newLabel)340 void UIDropDown::setLabelStr(Common::String newLabel) {
341 _labelStr = newLabel;
342 }
343
setControlLeft(int controlLeftX)344 void UIDropDown::setControlLeft(int controlLeftX) {
345 _controlLeftX = controlLeftX;
346 }
347
getLineSelectedStr()348 Common::String UIDropDown::getLineSelectedStr() {
349 return _lineSelectedStr;
350 }
351
352 }
353