BLADERUNNER: Scrollable dropdown control for KIA (WIP)

Current purpose is for text language selection

Drop down list for language selection in KIA (early stage)
This commit is contained in:
Thanasis Antoniou 2020-05-27 00:36:28 +03:00
parent 1f62327969
commit 02d47a1d45
14 changed files with 1052 additions and 151 deletions

View File

@ -110,6 +110,7 @@ public:
static const int kArchiveCount = 12; // +2 to original value (10) to accommodate for SUBTITLES.MIX and one extra resource file, to allow for capability of loading all VQAx.MIX and the MODE.MIX file (debug purposes)
static const int kActorCount = 100;
static const int kActorVoiceOver = kActorCount - 1;
// Incremental number to keep track of significant revisions of the ScummVM bladerunner engine
// that could potentially introduce incompatibilities with old save files or require special actions to restore compatibility
// This is stored in game global variable "kVariableGameVersion"

View File

@ -273,6 +273,7 @@ MODULE_OBJS = \
ui/ui_input_box.o \
ui/ui_scroll_box.o \
ui/ui_slider.o \
ui/ui_dropdown.o \
ui/vk.o \
view.o \
vqa_decoder.o \

View File

@ -39,13 +39,16 @@ class TextResource;
class Subtitles {
friend class Debugger;
friend class KIASectionSettings;
//
// Subtitles could be in 6 possible languages are EN_ANY, DE_DEU, FR_FRA, IT_ITA, RU_RUS, ES_ESP
// with corresponding _vm->_languageCode values: "E", "G", "F", "I", "E", "S" (Russian version is built on top of English one)
static const uint kPreferedLine = 2; // Prefer drawing from this line (the bottom-most of available subtitle lines index is 0) by default
static const int kMarginBottom = 12; // In pixels. This is the bottom margin beneath the subtitles space
static const int kTextMaxWidth = 610; // In pixels
static const int kMaxTextResourceEntries = 27; // Support in-game subs (1) and all possible VQAs (26) with spoken dialogue or translatable text
static const uint kPreferedLine = 2; // Prefer drawing from this line (the bottom-most of available subtitle lines index is 0) by default
static const int kMarginBottom = 12; // In pixels. This is the bottom margin beneath the subtitles space
static const int kTextMaxWidth = 610; // In pixels
static const int kMaxTextResourceEntries = 27; // Support in-game subs (1) and all possible VQAs (26) with spoken dialogue or translatable text
static const int kMaxLanguageSelectionNum = 1024; // Max allowed number of languages to select from (should be available in the MIX file)
static const char *SUBTITLES_FILENAME_PREFIXES[kMaxTextResourceEntries];
static const char *SUBTITLES_FONT_FILENAME_EXTERNAL;
static const char *SUBTITLES_VERSION_TRENAME;

View File

@ -40,14 +40,31 @@
#include "bladerunner/ui/ui_container.h"
#include "bladerunner/ui/ui_image_picker.h"
#include "bladerunner/ui/ui_slider.h"
#include "bladerunner/ui/ui_scroll_box.h"
#include "bladerunner/ui/ui_dropdown.h"
#include "audio/mixer.h"
#include "common/keyboard.h"
#include "common/debug.h"
namespace BladeRunner {
const char *KIASectionSettings::kLeary = "LEARY";
const Color256 KIASectionSettings::kColors[] = {
{ 0, 0, 0 }, // Black - unpressed (framing rectange)
{ 16, 8, 8 },
{ 32, 24, 8 },
{ 56, 32, 16 },
{ 72, 48, 16 },
{ 88, 56, 24 }, // Mouse-over (framing rectange)
{ 104, 72, 32 },
{ 128, 80, 40 },
{ 136, 96, 48 },
{ 152, 112, 56 },
{ 168, 128, 72 } // Pressed (framing rectange)
};
KIASectionSettings::KIASectionSettings(BladeRunnerEngine *vm)
: KIASectionBase(vm) {
@ -64,17 +81,44 @@ KIASectionSettings::KIASectionSettings(BladeRunnerEngine *vm)
_soundEffectVolume = new UISlider(_vm, sliderCallback, this, Common::Rect(180, 185, 460, 195), _vm->_mixer->kMaxMixerVolume, 0);
_speechVolume = new UISlider(_vm, sliderCallback, this, Common::Rect(180, 210, 460, 220), _vm->_mixer->kMaxMixerVolume, 0);
#endif
_subtitlesEnable = nullptr;
if (_vm->_language == Common::RU_RUS) {
// expanded click-bounding box x-axis
_directorsCut = new UICheckBox(_vm, checkBoxCallback, this, Common::Rect(180, 364, 436, 374), 0, false);
// moved to new line
_subtitlesEnable = new UICheckBox(_vm, checkBoxCallback, this, Common::Rect(276, 376, 345, 386), 0, false);
if (_vm->_subtitles->isSystemActive()) {
// moved to new line
_subtitlesEnable = new UICheckBox(_vm, checkBoxCallback, this, Common::Rect(276, 376, 345, 386), 0, false);
}
} else {
_directorsCut = new UICheckBox(_vm, checkBoxCallback, this, Common::Rect(180, 364, 270, 374), 0, false);
// moved further to the right to avoid overlap with 'Designer's Cut' in some language versions (ESP)
_subtitlesEnable = new UICheckBox(_vm, checkBoxCallback, this, Common::Rect(311, 364, 380, 374), 0, false);
if (_vm->_subtitles->isSystemActive()) {
// moved further to the right to avoid overlap with 'Designer's Cut' in some language versions (ESP)
_subtitlesEnable = new UICheckBox(_vm, checkBoxCallback, this, Common::Rect(311, 364, 380, 374), 0, false);
}
}
_selectedTextLanguageStr = "";
_selectedTextLanguageId = -1;
_textLanguageDropdown = nullptr;
if (_vm->_subtitles->isSystemActive()) {
// TODO initialize with default values or ConfMan() in open()?
_selectedTextLanguageStr = "";
_selectedTextLanguageId = -1;
// Put at height on top of Music setting
// This avoids interference and handling the case of when BLADERUNNER_ORIGINAL_SETTINGS is set
// (in which case the middle part of the KIA screen is filled with controls)
_textLanguageDropdown = new UIDropDown(_vm,
dropdownSelectedCallback,
dropdownCancelledCallback,
dropdownClickedTopFrameCallback,
this,
"",
0,
136,
Subtitles::kMaxLanguageSelectionNum);
}
_playerAgendaSelector = new UIImagePicker(_vm, 5);
_uiContainer->add(_musicVolume);
@ -87,8 +131,17 @@ KIASectionSettings::KIASectionSettings(BladeRunnerEngine *vm)
_uiContainer->add(_directorsCut);
if (_vm->_subtitles->isSystemActive()) {
_uiContainer->add(_subtitlesEnable);
// Note: Keep _languageSelectorScrollBox last added to _uiContainer
// in order to be able to set it as the only active object
// when the language selection dropdown is shown.
_uiContainer->add(_textLanguageDropdown);
}
_state = kStateNormal;
_mouseX = 0;
_mouseY = 0;
_learyPos = 0;
}
@ -102,27 +155,39 @@ KIASectionSettings::~KIASectionSettings() {
delete _gammaCorrection;
#endif
delete _directorsCut;
delete _subtitlesEnable;
if (_vm->_subtitles->isSystemActive()) {
delete _textLanguageDropdown;
delete _subtitlesEnable;
}
delete _playerAgendaSelector;
}
void KIASectionSettings::open() {
_state = kStateNormal;
_playerAgendaSelector->resetImages();
_playerAgendaSelector->defineImage(0, Common::Rect(180, 290, 227, 353), nullptr, nullptr, nullptr, _vm->_textOptions->getText(30));
_playerAgendaSelector->defineImage(1, Common::Rect(238, 290, 285, 353), nullptr, nullptr, nullptr, _vm->_textOptions->getText(31));
_playerAgendaSelector->defineImage(2, Common::Rect(296, 290, 343, 353), nullptr, nullptr, nullptr, _vm->_textOptions->getText(32));
_playerAgendaSelector->defineImage(3, Common::Rect(354, 290, 401, 353), nullptr, nullptr, nullptr, _vm->_textOptions->getText(33));
_playerAgendaSelector->defineImage(4, Common::Rect(412, 290, 459, 353), nullptr, nullptr, nullptr, _vm->_textOptions->getText(34));
_playerAgendaSelector->defineImage(kPlayerAgendaPolite, Common::Rect(180, 290, 227, 353), nullptr, nullptr, nullptr, _vm->_textOptions->getText(30));
_playerAgendaSelector->defineImage(kPlayerAgendaNormal, Common::Rect(238, 290, 285, 353), nullptr, nullptr, nullptr, _vm->_textOptions->getText(31));
_playerAgendaSelector->defineImage(kPlayerAgendaSurly, Common::Rect(296, 290, 343, 353), nullptr, nullptr, nullptr, _vm->_textOptions->getText(32));
_playerAgendaSelector->defineImage(kPlayerAgendaErratic, Common::Rect(354, 290, 401, 353), nullptr, nullptr, nullptr, _vm->_textOptions->getText(33));
_playerAgendaSelector->defineImage(kPlayerAgendaUserChoice, Common::Rect(412, 290, 459, 353), nullptr, nullptr, nullptr, _vm->_textOptions->getText(34));
initConversationChoices();
_playerAgendaSelector->activate(mouseInCallback, nullptr, nullptr, mouseUpCallback, this);
_directorsCut->enable();
_subtitlesEnable->enable();
if (_vm->_subtitles->isSystemActive()) {
_subtitlesEnable->enable();
_textLanguageDropdown->activate();
populateLanguageSelection();
}
}
void KIASectionSettings::close() {
_playerAgendaSelector->deactivate();
if (_vm->_subtitles->isSystemActive()) {
_textLanguageDropdown->deactivate();
}
}
void KIASectionSettings::draw(Graphics::Surface &surface) {
@ -140,7 +205,9 @@ void KIASectionSettings::draw(Graphics::Surface &surface) {
_directorsCut->setChecked(_vm->_gameFlags->query(kFlagDirectorsCut));
_subtitlesEnable->setChecked(_vm->isSubtitlesEnabled());
if (_vm->_subtitles->isSystemActive()) {
_subtitlesEnable->setChecked(_vm->isSubtitlesEnabled());
}
const char *textConversationChoices = _vm->_textOptions->getText(0);
const char *textMusic = _vm->_textOptions->getText(2);
@ -167,7 +234,6 @@ void KIASectionSettings::draw(Graphics::Surface &surface) {
int posDark = 178 - _vm->_mainFont->getStringWidth(textDark);
#endif
_uiContainer->draw(surface);
_playerAgendaSelector->draw(surface);
_vm->_mainFont->drawString(&surface, textConversationChoices, posConversationChoices, 280, surface.w, surface.format.RGBToColor(232, 208, 136));
@ -202,38 +268,47 @@ void KIASectionSettings::draw(Graphics::Surface &surface) {
if (_vm->_subtitles->isSystemActive()) {
// Allow this to be loading as an extra text item in the resource for text options
const char *subtitlesTranslation = nullptr;
const char *subtitlesTranslation = nullptr;
const char *languageSelectTranslation = nullptr;
switch (_vm->_language) {
case Common::EN_ANY:
default:
subtitlesTranslation = "Subtitles";
break;
case Common::DE_DEU:
subtitlesTranslation = "Untertitel";
break;
case Common::FR_FRA:
subtitlesTranslation = "Sous-titres";
break;
case Common::IT_ITA:
subtitlesTranslation = "Sottotitoli";
break;
case Common::ES_ESP:
// the spanish text must have accented í
subtitlesTranslation = "Subt\xa1tulos";
break;
case Common::RU_RUS:
// субтитры
if (_vm->_russianCP1251) {
// Patched translation by Siberian Studio is using Windows-1251 encoding
subtitlesTranslation = "\xf1\xf3\xe1\xf2\xe8\xf2\xf0\xfb";
} else {
// Original release uses custom encoding
subtitlesTranslation = "CE,NBNHS";
}
break;
case Common::EN_ANY:
default:
subtitlesTranslation = "Subtitles";
languageSelectTranslation = "Text Language:";
break;
case Common::DE_DEU:
subtitlesTranslation = "Untertitel";
languageSelectTranslation = "Text Language:"; // TODO DEUTCH translation
break;
case Common::FR_FRA:
subtitlesTranslation = "Sous-titres";
languageSelectTranslation = "Text Language:"; // TODO FRENCH translation
break;
case Common::IT_ITA:
subtitlesTranslation = "Sottotitoli";
languageSelectTranslation = "Text Language:"; // TODO ITALIAN translation
break;
case Common::ES_ESP:
// the spanish text must have accented í
subtitlesTranslation = "Subt\xa1tulos";
languageSelectTranslation = "Text Language:"; // TODO SPANISH translation
break;
case Common::RU_RUS:
// субтитры
if (_vm->_russianCP1251) {
// Patched translation by Siberian Studio is using Windows-1251 encoding
subtitlesTranslation = "\xf1\xf3\xe1\xf2\xe8\xf2\xf0\xfb";
languageSelectTranslation = "Text Language:"; // TODO RUSSIAN translation + proper characters for encoding
} else {
// Original release uses custom encoding
subtitlesTranslation = "CE,NBNHS";
languageSelectTranslation = "Text Language:"; // TODO RUSSIAN translation + proper characters for this encoding
}
break;
}
// +1 to the max of original index of textOptions which is 41
// +2 to the max of original index of textOptions which is 41
const char *textSubtitles = strcmp(_vm->_textOptions->getText(42), "") == 0 ? subtitlesTranslation : _vm->_textOptions->getText(42);
const char *textLanguageSelect = strcmp(_vm->_textOptions->getText(43), "") == 0 ? languageSelectTranslation : _vm->_textOptions->getText(43);
if (_vm->_language == Common::RU_RUS) {
// special case for Russian version, put the option in a new line to avoid overlap
@ -242,36 +317,48 @@ void KIASectionSettings::draw(Graphics::Surface &surface) {
// moved further to the right to avoid overlap with 'Designer's Cut' in some language versions (ESP)
_vm->_mainFont->drawString(&surface, textSubtitles, 323, 365, surface.w, surface.format.RGBToColor(232, 208, 136));
}
// Vertical Align with "Soft" label
_textLanguageDropdown->setControlLeft(posSoft);
_textLanguageDropdown->setLabelStr(textLanguageSelect);
}
// Draw uiContainer contained objects after drawing the text on the section for music, sound, speech etc.
_uiContainer->draw(surface);
_playerAgendaSelector->drawTooltip(surface, _mouseX, _mouseY);
}
void KIASectionSettings::handleKeyDown(const Common::KeyState &kbd) {
if (toupper(kbd.ascii) != kLeary[_learyPos]) {
_learyPos = 0;
}
if (toupper(kbd.ascii) == kLeary[_learyPos]) {
++_learyPos;
if (!kLeary[_learyPos]) {
_vm->_settings->setLearyMode(!_vm->_settings->getLearyMode());
if (_state == kStateNormal) {
if (toupper(kbd.ascii) != kLeary[_learyPos]) {
_learyPos = 0;
initConversationChoices();
}
if (toupper(kbd.ascii) == kLeary[_learyPos]) {
++_learyPos;
if (!kLeary[_learyPos]) {
_vm->_settings->setLearyMode(!_vm->_settings->getLearyMode());
_learyPos = 0;
initConversationChoices();
}
}
}
}
void KIASectionSettings::handleMouseMove(int mouseX, int mouseY) {
_uiContainer->handleMouseMove(mouseX, mouseY);
_mouseX = mouseX;
_mouseY = mouseY;
_playerAgendaSelector->handleMouseAction(mouseX, mouseY, false, false, false);
}
void KIASectionSettings::handleMouseDown(bool mainButton) {
if (mainButton) {
_uiContainer->handleMouseDown(false);
_playerAgendaSelector->handleMouseAction(_mouseX, _mouseY, true, false, false);
}
}
@ -279,10 +366,17 @@ void KIASectionSettings::handleMouseDown(bool mainButton) {
void KIASectionSettings::handleMouseUp(bool mainButton) {
if (mainButton) {
_uiContainer->handleMouseUp(false);
_playerAgendaSelector->handleMouseAction(_mouseX, _mouseY, false, true, false);
}
}
void KIASectionSettings::handleMouseScroll(int direction) {
if (_vm->_subtitles->isSystemActive() && _state == kStateLanguageSelect) {
_uiContainer->handleMouseScroll(direction);
}
}
void KIASectionSettings::sliderCallback(void *callbackData, void *source) {
KIASectionSettings *self = (KIASectionSettings *)callbackData;
@ -333,8 +427,7 @@ void KIASectionSettings::checkBoxCallback(void *callbackData, void *source) {
} else {
self->_vm->_gameFlags->reset(kFlagDirectorsCut);
}
}
else if (source == self->_subtitlesEnable) {
} else if (self->_vm->_subtitles->isSystemActive() && source == self->_subtitlesEnable) {
self->_vm->setSubtitlesEnabled(self->_subtitlesEnable->_isChecked);
}
}
@ -351,29 +444,29 @@ void KIASectionSettings::mouseUpCallback(int buttonId, void *callbackData) {
void KIASectionSettings::onButtonPressed(int buttonId) {
switch (buttonId) {
case 0:
case kPlayerAgendaPolite:
_vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(kSfxELECBP1), 90, -30, -30, 50, 0);
_vm->_settings->setPlayerAgenda(0);
_vm->_settings->setPlayerAgenda(kPlayerAgendaPolite);
initConversationChoices();
break;
case 1:
case kPlayerAgendaNormal:
_vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(kSfxELECBP1), 90, -15, -15, 50, 0);
_vm->_settings->setPlayerAgenda(1);
_vm->_settings->setPlayerAgenda(kPlayerAgendaNormal);
initConversationChoices();
break;
case 2:
case kPlayerAgendaSurly:
_vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(kSfxELECBP1), 90, 0, 0, 50, 0);
_vm->_settings->setPlayerAgenda(2);
_vm->_settings->setPlayerAgenda(kPlayerAgendaSurly);
initConversationChoices();
break;
case 3:
case kPlayerAgendaErratic:
_vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(kSfxELECBP1), 90, 15, 15, 50, 0);
_vm->_settings->setPlayerAgenda(3);
_vm->_settings->setPlayerAgenda(kPlayerAgendaErratic);
initConversationChoices();
break;
case 4:
case kPlayerAgendaUserChoice:
_vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(kSfxELECBP1), 90, 30, 30, 50, 0);
_vm->_settings->setPlayerAgenda(4);
_vm->_settings->setPlayerAgenda(kPlayerAgendaUserChoice);
initConversationChoices();
break;
default:
@ -385,7 +478,7 @@ void KIASectionSettings::initConversationChoices() {
for (int i = 0; i < 5; ++i) {
const Shape *shape = nullptr;
if (_vm->_settings->getPlayerAgenda() == i) {
if (i == 4) {
if (i == kPlayerAgendaUserChoice) {
shape = _vm->_kia->_shapes->get(122);
} else if (_vm->_settings->getLearyMode()) {
shape = _vm->_kia->_shapes->get(106 + i);
@ -393,7 +486,7 @@ void KIASectionSettings::initConversationChoices() {
shape = _vm->_kia->_shapes->get(114 + i);
}
} else {
if (i == 4) {
if (i == kPlayerAgendaUserChoice) {
shape = _vm->_kia->_shapes->get(123);
} else if (_vm->_settings->getLearyMode()) {
shape = _vm->_kia->_shapes->get(110 + i);
@ -408,4 +501,62 @@ void KIASectionSettings::initConversationChoices() {
}
}
void KIASectionSettings::populateLanguageSelection() {
_textLanguageDropdown->clearLines();
_textLanguageDropdown->addLine("English v7 [ENG] (SCUMMVM)", 1);
_textLanguageDropdown->addLine("French v7 [FRA] (Kwama57)", 2);
_textLanguageDropdown->addLine("Spanish v7 [ESP] (Victor G. Fraile & GeekOb)", 3);
_textLanguageDropdown->addLine("Greek v1 [ENG] (Praetorian)", 4);
_textLanguageDropdown->addLine("Hebrew v1 [ENG] (Rzil)", 5);
_textLanguageDropdown->addLine("Chinese v0 [ENG] (*)", 6);
_textLanguageDropdown->addLine("Russian v1 [ENG] (*)", 7);
_textLanguageDropdown->addLine("Italian v0 [ITA] (*)", 8);
_textLanguageDropdown->addLine("Deutsch v0 [DEU] (*)", 9);
}
void KIASectionSettings::changeState(State state) {
_state = state;
if (state != kStateNormal) {
_vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(kSfxSPNBEEP7), 90, 0, 0, 50, 0);
}
}
void KIASectionSettings::dropdownSelectedCallback(void *callbackData, void *source, int lineData, int mouseButton) {
KIASectionSettings *self = (KIASectionSettings *)callbackData;
if (source == self->_textLanguageDropdown && lineData >= 0) {
self->_selectedTextLanguageStr = self->_textLanguageDropdown->getLineSelectedStr();
self->_selectedTextLanguageId = lineData;
self->showTextSelectionDropdown(false);
}
}
void KIASectionSettings::dropdownCancelledCallback(void *callbackData, void *source) {
KIASectionSettings *self = (KIASectionSettings *)callbackData;
if (source == self->_textLanguageDropdown) {
self->showTextSelectionDropdown(false);
}
}
void KIASectionSettings::dropdownClickedTopFrameCallback(void *callbackData, void *source) {
KIASectionSettings *self = (KIASectionSettings *)callbackData;
if (source == self->_textLanguageDropdown) {
self->showTextSelectionDropdown(!self->_textLanguageDropdown->isDropDownMenuExpanded());
}
}
void KIASectionSettings::showTextSelectionDropdown(bool showToggle) {
if (showToggle) {
// scrollable area will be expanded
populateLanguageSelection();
_uiContainer->setHandleSpecificNumOfTopLayers(1);
changeState(kStateLanguageSelect);
} else {
// scrollable area will be collapsed
_uiContainer->setHandleSpecificNumOfTopLayers(0);
changeState(kStateNormal);
}
}
} // End of namespace BladeRunner

View File

@ -24,9 +24,11 @@
#define BLADERUNNER_KIA_SECTION_SETTINGS_H
#include "bladerunner/bladerunner.h" // for BLADERUNNER_ORIGINAL_SETTINGS macro
#include "bladerunner/color.h"
#include "bladerunner/ui/kia_section_base.h"
#include "common/config-manager.h"
#include "common/rect.h"
namespace BladeRunner {
@ -35,9 +37,17 @@ class UIContainer;
class UICheckBox;
class UIImagePicker;
class UISlider;
class UIScrollBox;
class UIDropDown;
class KIASectionSettings : public KIASectionBase {
enum State {
kStateNormal = 0,
kStateLanguageSelect = 1
};
static const char *kLeary;
static const Color256 kColors[];
UIContainer *_uiContainer;
UISlider *_musicVolume;
@ -49,6 +59,18 @@ class KIASectionSettings : public KIASectionBase {
#endif
UICheckBox *_directorsCut;
UICheckBox *_subtitlesEnable;
Common::String _selectedTextLanguageStr;
int _selectedTextLanguageId;
UIDropDown *_textLanguageDropdown;
// UIScrollBox *_languageSelectorScrollBox;
// UIImagePicker *_languageDropdownBtn;
// Common::Rect _languageSelectorFrameRect;
// int _languageSelectorFrameRectColor;
// bool _languageSelectorFrameRectHasFocus;
// int _languageSelectorScrollBoxMaxLineWidth;
UIImagePicker *_playerAgendaSelector;
int _mouseX;
@ -56,6 +78,8 @@ class KIASectionSettings : public KIASectionBase {
int _learyPos;
State _state;
public:
KIASectionSettings(BladeRunnerEngine *vm);
~KIASectionSettings() override;
@ -69,16 +93,25 @@ public:
void handleMouseMove(int mouseX, int mouseY) override;
void handleMouseDown(bool mainButton) override;
void handleMouseUp(bool mainButton) override;
void handleMouseScroll(int direction) override;
void showTextSelectionDropdown(bool showToggle);
private:
static void sliderCallback(void *callbackData, void *source);
static void checkBoxCallback(void *callbackData, void *source);
static void mouseInCallback(int buttonId, void *callbackData);
static void mouseUpCallback(int buttonId, void *callbackData);
static void dropdownSelectedCallback(void *callbackData, void *source, int lineData, int mouseButton);
static void dropdownCancelledCallback(void *callbackData, void *source);
static void dropdownClickedTopFrameCallback(void *callbackData, void *source);
void onButtonPressed(int buttonId) override;
void initConversationChoices();
void populateLanguageSelection();
void changeState(State state);
};
} // End of namespace BladeRunner

View File

@ -33,38 +33,99 @@ void UIContainer::draw(Graphics::Surface &surface) {
}
void UIContainer::handleMouseMove(int mouseX, int mouseY) {
for (Common::Array<UIComponent*>::iterator component = _components.begin(); component != _components.end(); ++component) {
(*component)->handleMouseMove(mouseX, mouseY);
if (_handleSpecificNumOfTopLayers <= 0) {
for (Common::Array<UIComponent*>::iterator component = _components.begin(); component != _components.end(); ++component) {
(*component)->handleMouseMove(mouseX, mouseY);
}
} else {
int countOfTopLayersToHandle = _handleSpecificNumOfTopLayers;
Common::Array<UIComponent*>::iterator component = _components.end();
do {
--component;
--countOfTopLayersToHandle;
(*component)->handleMouseMove(mouseX, mouseY);
} while (component != _components.begin() && countOfTopLayersToHandle != 0);
}
}
void UIContainer::handleMouseDown(bool alternateButton) {
for (Common::Array<UIComponent*>::iterator component = _components.begin(); component != _components.end(); ++component) {
(*component)->handleMouseDown(alternateButton);
if (_handleSpecificNumOfTopLayers <= 0) {
for (Common::Array<UIComponent*>::iterator component = _components.begin(); component != _components.end(); ++component) {
(*component)->handleMouseDown(alternateButton);
}
} else {
int countOfTopLayersToHandle = _handleSpecificNumOfTopLayers;
Common::Array<UIComponent*>::iterator component = _components.end();
do {
--component;
--countOfTopLayersToHandle;
(*component)->handleMouseDown(alternateButton);
} while (component != _components.begin() && countOfTopLayersToHandle != 0);
}
}
void UIContainer::handleMouseUp(bool alternateButton) {
for (Common::Array<UIComponent*>::iterator component = _components.begin(); component != _components.end(); ++component) {
(*component)->handleMouseUp(alternateButton);
if (_handleSpecificNumOfTopLayers <= 0) {
for (Common::Array<UIComponent*>::iterator component = _components.begin(); component != _components.end(); ++component) {
(*component)->handleMouseUp(alternateButton);
}
} else {
int countOfTopLayersToHandle = _handleSpecificNumOfTopLayers;
Common::Array<UIComponent*>::iterator component = _components.end();
do {
--component;
--countOfTopLayersToHandle;
(*component)->handleMouseUp(alternateButton);
} while (component != _components.begin() && countOfTopLayersToHandle != 0);
}
}
void UIContainer::handleMouseScroll(int direction) {
for (Common::Array<UIComponent*>::iterator component = _components.begin(); component != _components.end(); ++component) {
(*component)->handleMouseScroll(direction);
if (_handleSpecificNumOfTopLayers <= 0) {
for (Common::Array<UIComponent*>::iterator component = _components.begin(); component != _components.end(); ++component) {
(*component)->handleMouseScroll(direction);
}
} else {
int countOfTopLayersToHandle = _handleSpecificNumOfTopLayers;
Common::Array<UIComponent*>::iterator component = _components.end();
do {
--component;
--countOfTopLayersToHandle;
(*component)->handleMouseScroll(direction);
} while (component != _components.begin() && countOfTopLayersToHandle != 0);
}
}
void UIContainer::handleKeyUp(const Common::KeyState &kbd) {
for (Common::Array<UIComponent*>::iterator component = _components.begin(); component != _components.end(); ++component) {
(*component)->handleKeyUp(kbd);
if (_handleSpecificNumOfTopLayers <= 0) {
for (Common::Array<UIComponent*>::iterator component = _components.begin(); component != _components.end(); ++component) {
(*component)->handleKeyUp(kbd);
}
} else {
int countOfTopLayersToHandle = _handleSpecificNumOfTopLayers;
Common::Array<UIComponent*>::iterator component = _components.end();
do {
--component;
--countOfTopLayersToHandle;
(*component)->handleKeyUp(kbd);
} while (component != _components.begin() && countOfTopLayersToHandle != 0);
}
}
void UIContainer::handleKeyDown(const Common::KeyState &kbd) {
for (Common::Array<UIComponent*>::iterator component = _components.begin(); component != _components.end(); ++component) {
(*component)->handleKeyDown(kbd);
if (_handleSpecificNumOfTopLayers <= 0) {
for (Common::Array<UIComponent*>::iterator component = _components.begin(); component != _components.end(); ++component) {
(*component)->handleKeyDown(kbd);
}
} else {
int countOfTopLayersToHandle = _handleSpecificNumOfTopLayers;
Common::Array<UIComponent*>::iterator component = _components.end();
do {
--component;
--countOfTopLayersToHandle;
(*component)->handleKeyDown(kbd);
} while (component != _components.begin() && countOfTopLayersToHandle != 0);
}
}
@ -74,6 +135,12 @@ void UIContainer::add(UIComponent *component) {
void UIContainer::clear() {
_components.clear();
_handleSpecificNumOfTopLayers = 0;
}
void UIContainer::setHandleSpecificNumOfTopLayers(int count) {
_handleSpecificNumOfTopLayers = count;
}
} // End of namespace BladeRunner

View File

@ -34,8 +34,12 @@ class UIComponent;
class UIContainer : public UIComponent {
Common::Array<UIComponent*> _components;
int _handleSpecificNumOfTopLayers;
public:
UIContainer(BladeRunnerEngine *vm) : UIComponent(vm) {}
UIContainer(BladeRunnerEngine *vm) : UIComponent(vm) {
_handleSpecificNumOfTopLayers = 0;
}
void draw(Graphics::Surface &surface) override;
@ -48,6 +52,8 @@ public:
void add(UIComponent *component);
void clear();
void setHandleSpecificNumOfTopLayers(int count);
};

View File

@ -0,0 +1,352 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "bladerunner/ui/ui_dropdown.h"
#include "bladerunner/audio_player.h"
#include "bladerunner/bladerunner.h"
#include "bladerunner/ui/kia.h"
#include "bladerunner/ui/ui_image_picker.h"
#include "bladerunner/ui/ui_scroll_box.h"
#include "bladerunner/subtitles.h"
#include "bladerunner/font.h"
#include "bladerunner/game_info.h"
#include "bladerunner/shape.h"
#include "bladerunner/game_constants.h"
#include "common/debug.h"
namespace BladeRunner {
const Color256 UIDropDown::kColors[] = {
{ 0, 0, 0 }, // Black - unpressed (framing rectange)
{ 16, 8, 8 },
{ 32, 24, 8 },
{ 56, 32, 16 },
{ 72, 48, 16 },
{ 88, 56, 24 }, // Mouse-over (framing rectange)
{ 104, 72, 32 },
{ 128, 80, 40 },
{ 136, 96, 48 },
{ 152, 112, 56 },
{ 168, 128, 72 } // Pressed (framing rectange)
};
UIDropDown::UIDropDown(BladeRunnerEngine *vm,
UIDropDownLineSelectedCallback *ddlLineSelectedCallback,
UIDropDownGenericCallback *ddlCancelledCallback,
UIDropDownGenericCallback *ddlTopFrameClickCallback,
void *callbackData,
Common::String labelStr,
int controlLeftX,
int controlTopY,
int scrollBoxMaxLineCount) : UIComponent(vm) {
_isVisible = false;
_labelStr = labelStr;
_controlLeftX = MAX(controlLeftX, 0);
// TODO The id should be used to eg. grab info about the selected subtitles from an engine's subtitles object
_lineSelectedId = -1;
_lineSelectorFrameRectColor = 0;
_lineSelectorFrameRectHasFocus = false;
// A framing (outlining) rectangle to highlight the selected option field on top of the scrollbox
controlTopY = CLIP(controlTopY, 0, 600);
_lineSelectorFrameRect = Common::Rect(0, controlTopY, 0, controlTopY + kDropDownButtonShapeHeight);
// TODO This eventually should be set to a default probably by the outside caller class(kia_section_settings)
// Current explicit assignment only serves as placeholder / proof of concept
_lineSelectedStr = "English (SCUMMVM) v7 [ENG]";
_lineSelectorScrollBox = new UIScrollBox(_vm, scrollBoxLineSelectCallback, this, scrollBoxMaxLineCount, 2, false, Common::Rect(0, 0, 0, 55 + kFrameRectPaddingPx), Common::Rect(0, 0, 0, 55));
_lineSelectorScrollBoxMaxLineWidth = 0;
_lineDropdownBtn = new UIImagePicker(_vm, 2);
_ddlLineSelectedCallback = ddlLineSelectedCallback;
_ddlCancelledCallback = ddlCancelledCallback;
_ddlTopFrameClickCallback = ddlTopFrameClickCallback;
_callbackData = callbackData;
_mouseX = 0;
_mouseY = 0;
}
UIDropDown::~UIDropDown() {
delete _lineSelectorScrollBox;
delete _lineDropdownBtn;
}
void UIDropDown::activate() {
_lineDropdownBtn->resetImages();
// Actual button shape
// defineImage actually increases internally the bottom and right bounds for the rect to be inclusive (for the contains() method)
_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);
// Clickable Selected/Active Line Description area
_lineDropdownBtn->defineImage(1, Common::Rect(0, _lineSelectorFrameRect.top, kDropDownButtonShapeWidth - 1, _lineSelectorFrameRect.bottom - 1), nullptr, nullptr, nullptr, nullptr);
_lineDropdownBtn->activate(nullptr, nullptr, mouseDownLDBCallback, nullptr, this);
_lineSelectorScrollBox->setBoxTop(_lineSelectorFrameRect.bottom);
_lineSelectorScrollBox->setScrollbarTop(_lineSelectorFrameRect.bottom);
_lineSelectorScrollBox->hide(); // show upon click on field or dropdown button
show();
}
void UIDropDown::deactivate() {
_isVisible = false;
_lineDropdownBtn->deactivate();
_lineSelectorScrollBox->hide();
}
void UIDropDown::draw(Graphics::Surface &surface) {
if (!_isVisible) {
return;
}
int posStartOfSelectedLineDesc = _controlLeftX + _vm->_mainFont->getStringWidth(_labelStr) + _vm->_mainFont->getCharWidth(' ');
_vm->_mainFont->drawString(&surface, _labelStr, _controlLeftX, _lineSelectorFrameRect.top, surface.w, surface.format.RGBToColor(232, 208, 136));
_vm->_mainFont->drawString(&surface, _lineSelectedStr,
posStartOfSelectedLineDesc,
_lineSelectorFrameRect.top, surface.w, surface.format.RGBToColor(240, 232, 192));
// TODO add a clipping for description field here
int posEndOfSelectedLineDesc = posStartOfSelectedLineDesc + _vm->_mainFont->getStringWidth(_lineSelectedStr) + _vm->_mainFont->getCharWidth(' ');
_lineDropdownBtn->setImageLeft(0, posEndOfSelectedLineDesc );
_lineDropdownBtn->setImageLeft(1, posStartOfSelectedLineDesc - kFrameRectPaddingPx);
_lineDropdownBtn->setImageWidth(1, posEndOfSelectedLineDesc + kFrameRectPaddingPx - posStartOfSelectedLineDesc);
_lineDropdownBtn->draw(surface);
// _lineDropdownBtn->drawTooltip(surface, _mouseX, _mouseY);
_lineSelectorFrameRect.moveTo(posStartOfSelectedLineDesc - kFrameRectPaddingPx, _lineSelectorFrameRect.top);
_lineSelectorFrameRect.setWidth(posEndOfSelectedLineDesc + kDropDownButtonShapeWidth + kFrameRectPaddingPx - posStartOfSelectedLineDesc);
_lineSelectorScrollBox->draw(surface);
int lineSelectorFrameRectTargetColor;
if (_lineSelectorScrollBox->isVisible()) {
lineSelectorFrameRectTargetColor = 10;
} else if (_lineSelectorFrameRectHasFocus) {
lineSelectorFrameRectTargetColor = 5;
} else {
lineSelectorFrameRectTargetColor = 0;
}
// Ensures animated transition of the frame's (outlining rectangle's) color to the new one
if (_lineSelectorFrameRectColor < lineSelectorFrameRectTargetColor) {
++_lineSelectorFrameRectColor;
}
// Ensures animated transition of the frame's (outlining rectangle's) color to the new one
if (_lineSelectorFrameRectColor > lineSelectorFrameRectTargetColor) {
--_lineSelectorFrameRectColor;
}
surface.frameRect(_lineSelectorFrameRect,
surface.format.RGBToColor(kColors[_lineSelectorFrameRectColor].r,
kColors[_lineSelectorFrameRectColor].g,
kColors[_lineSelectorFrameRectColor].b));
}
void UIDropDown::show() {
_isVisible = true;
}
void UIDropDown::hide() {
_isVisible = false;
}
bool UIDropDown::isVisible() {
return _isVisible;
}
bool UIDropDown::isDropDownMenuExpanded() {
return _lineSelectorScrollBox->isVisible();
}
void UIDropDown::clearLines() {
_lineSelectorScrollBox->clearLines();
_lineSelectorScrollBoxMaxLineWidth = 0;
}
void UIDropDown::addLine(const Common::String &text, int lineData) {
_lineSelectorScrollBox->addLine(text, lineData, 0x08);
_lineSelectorScrollBoxMaxLineWidth = MAX(_vm->_mainFont->getStringWidth(text), _lineSelectorScrollBoxMaxLineWidth);
}
void UIDropDown::addLine(const char *text, int lineData) {
_lineSelectorScrollBox->addLine(text, lineData, 0x08);
_lineSelectorScrollBoxMaxLineWidth = MAX(_vm->_mainFont->getStringWidth(text), _lineSelectorScrollBoxMaxLineWidth);
}
void UIDropDown::sortLines() {
_lineSelectorScrollBox->sortLines();
}
void UIDropDown::handleMouseMove(int mouseX, int mouseY) {
if (!_isVisible) {
return;
}
_mouseX = mouseX;
_mouseY = mouseY;
// contains() does not include right or bottom boundary "line"
if (_lineSelectorFrameRect.contains(mouseX, mouseY)) {
if (!_lineSelectorFrameRectHasFocus && !_lineSelectorScrollBox->isVisible()) {
_vm->_audioPlayer->playAud(_vm->_gameInfo->getSfxTrack(kSfxTEXT3), 100, 0, 0, 50, 0);
}
_lineSelectorFrameRectHasFocus = true;
} else {
_lineSelectorFrameRectHasFocus = false;
}
_lineSelectorScrollBox->handleMouseMove(mouseX, mouseY);
_lineDropdownBtn->handleMouseAction(mouseX, mouseY, false, false, false);
}
void UIDropDown::handleMouseDown(bool alternateButton) {
if (!_isVisible) {
return;
}
if (!alternateButton) {
_lineSelectorScrollBox->handleMouseDown(false);
_lineDropdownBtn->handleMouseAction(_mouseX, _mouseY, true, false, false);
if (!_lineSelectorFrameRectHasFocus
&& _lineSelectorScrollBox->isVisible()
&& !_lineSelectorScrollBox->hasFocus()
) {
_ddlCancelledCallback(_callbackData, this);
showSelectionDropdown(false);
}
}
}
void UIDropDown::handleMouseScroll(int direction) {
if (!_isVisible) {
return;
}
if (_lineSelectorScrollBox->isVisible()) {
_lineSelectorScrollBox->handleMouseScroll(direction);
}
}
void UIDropDown::handleMouseUp(bool alternateButton) {
if (!_isVisible) {
return;
}
if (!alternateButton) {
_lineSelectorScrollBox->handleMouseUp(false);
_lineDropdownBtn->handleMouseAction(_mouseX, _mouseY, false, true, false);
}
}
void UIDropDown::scrollBoxLineSelectCallback(void *callbackData, void *source, int lineData, int mouseButton) {
UIDropDown *self = (UIDropDown *)callbackData;
if (source == self->_lineSelectorScrollBox && lineData >= 0) {
Common::String selectedLangDescStr = self->_lineSelectorScrollBox->getLineText(lineData);
self->_lineSelectedId = lineData;
self->_lineSelectedStr = selectedLangDescStr;
self->_vm->_audioPlayer->playAud(self->_vm->_gameInfo->getSfxTrack(kSfxELECBP1), 90, 0, 0, 50, 0);
self->_ddlLineSelectedCallback(self->_callbackData, self, lineData, mouseButton);
self->showSelectionDropdown(false);
//debug("text selected: %s", selectedLangDescStr.c_str());
}
}
// Callback from _lineDropdownBtn items
void UIDropDown::mouseDownLDBCallback(int buttonId, void *callbackData) {
UIDropDown *self = (UIDropDown *)callbackData;
self->onButtonPressed(buttonId);
}
void UIDropDown::onButtonPressed(int buttonId) {
switch (buttonId) {
case 0:
// Pressed DDL dropdown button (0)
// fall through
case 1:
// if (buttonId == 1) {
// // Pressed DDL clickable area (1)
// debug("Pressed DDL clickable area (1)");
// }
_ddlTopFrameClickCallback(_callbackData, this);
showSelectionDropdown(!_lineSelectorScrollBox->isVisible());
break;
default:
return;
}
}
void UIDropDown::showSelectionDropdown(bool showToggle) {
int prevDropdownBtnLeft = _lineDropdownBtn->getImageLeft(0);
if (showToggle) {
_lineSelectorScrollBox->setBoxTop(_lineSelectorFrameRect.bottom);
_lineSelectorScrollBox->setBoxLeft(_lineDropdownBtn->getImageLeft(1));
// TODO width should be retrieved from the maximum width of a language description in SUBTITLES.MIX (or a max width to clip to)
_lineSelectorScrollBox->setBoxWidth(MAX(_lineDropdownBtn->getImageWidth(1), _lineSelectorScrollBoxMaxLineWidth + _vm->_mainFont->getCharWidth(' ')));
if (_lineDropdownBtn->getImageLeft(0) < kFurthestLeftForScrollBar) {
// CLIP expects the first boundary argument to be the min of the two.
_lineSelectorScrollBox->setScrollbarLeft(CLIP( _lineSelectorScrollBox->getBoxLeft() + _lineSelectorScrollBox->getBoxWidth(), _lineDropdownBtn->getImageLeft(0), kFurthestLeftForScrollBar));
} else {
_lineSelectorScrollBox->setScrollbarLeft(MAX( _lineSelectorScrollBox->getBoxLeft() + _lineSelectorScrollBox->getBoxWidth(), kFurthestLeftForScrollBar));
}
_lineSelectorScrollBox->setScrollbarTop(_lineSelectorFrameRect.bottom);
_lineSelectorScrollBox->setScrollbarWidth(kDropDownButtonShapeWidth);
_lineSelectorScrollBox->show();
// change dropdown button icon too
_lineDropdownBtn->resetActiveImage(0);
_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);
_lineSelectorFrameRectColor = 10;
} else {
// hide scrollable area
_lineSelectorScrollBox->hide();
// change dropdown button icon too
_lineDropdownBtn->resetActiveImage(0);
_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);
_lineSelectorFrameRectColor = 0;
}
}
void UIDropDown::setLabelStr(Common::String newLabel) {
_labelStr = newLabel;
}
void UIDropDown::setControlLeft(int controlLeftX) {
_controlLeftX = controlLeftX;
}
Common::String UIDropDown::getLineSelectedStr() {
return _lineSelectedStr;
}
}

View File

@ -0,0 +1,124 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef BLADERUNNER_UI_DROPDOWN_H
#define BLADERUNNER_UI_DROPDOWN_H
#include "bladerunner/color.h"
#include "bladerunner/ui/ui_component.h"
#include "common/array.h"
#include "common/rect.h"
#include "common/str.h"
namespace BladeRunner {
class BladeRunnerEngine;
class UIImagePicker;
class UIScrollBox;
typedef void UIDropDownLineSelectedCallback(void *callbackData, void *source, int data, int mouseButton);
typedef void UIDropDownGenericCallback(void *callbackData, void *source);
class UIDropDown : public UIComponent {
static const int kDropDownButtonShapeWidth = 15;
static const int kDropDownButtonShapeHeight = 10;
static const uint8 kFrameRectPaddingPx = 2;
static const int kFurthestLeftForScrollBar = 495;
static const Color256 kColors[];
int _controlLeftX;
Common::String _labelStr;
bool _isVisible;
int _lineSelectedId;
Common::String _lineSelectedStr;
UIScrollBox *_lineSelectorScrollBox;
UIImagePicker *_lineDropdownBtn;
int _lineDropdownBtnTopY;
int _lineDropdownBtnHeight;
Common::Rect _lineSelectorFrameRect;
int _lineSelectorFrameRectColor;
bool _lineSelectorFrameRectHasFocus;
int _lineSelectorScrollBoxMaxLineWidth;
UIDropDownLineSelectedCallback *_ddlLineSelectedCallback;
UIDropDownGenericCallback *_ddlCancelledCallback;
UIDropDownGenericCallback *_ddlTopFrameClickCallback;
void *_callbackData;
int _mouseX;
int _mouseY;
public:
UIDropDown(BladeRunnerEngine *vm, UIDropDownLineSelectedCallback *ddlLineSelectedCallback,
UIDropDownGenericCallback *ddlCancelledCallback,
UIDropDownGenericCallback *ddlTopFrameClickCallback,
void *callbackData,
Common::String labelStr,
int controlLeftX,
int controlTopY,
int scrollBoxMaxLineCount);
~UIDropDown() override;
void draw(Graphics::Surface &surface) override;
void handleMouseMove(int mouseX, int mouseY) override;
void handleMouseDown(bool alternateButton) override;
void handleMouseUp(bool alternateButton) override;
void handleMouseScroll(int direction) override;
void show();
void hide();
bool isVisible();
bool isDropDownMenuExpanded();
void activate();
void deactivate();
void clearLines();
void addLine(const Common::String &text, int lineData);
void addLine(const char *text, int lineData);
void sortLines();
void setLabelStr(Common::String newLabel);
void setControlLeft(int controlLeftX);
Common::String getLineSelectedStr();
private:
static void mouseDownLDBCallback(int buttonId, void *callbackData);
static void scrollBoxLineSelectCallback(void *callbackData, void *source, int lineData, int mouseButton);
void onButtonPressed(int buttonId);
void showSelectionDropdown(bool showToggle);
};
} // End of namespace BladeRunner
#endif

View File

@ -103,6 +103,18 @@ bool UIImagePicker::setImageLeft(int i, int left) {
return true;
}
bool UIImagePicker::setImageWidth(int i, int16 width) {
if (i < 0 || i >= _imageCount || !_images[i].active) {
return false;
}
Image &img = _images[i];
img.rect.setWidth(width);
return true;
}
bool UIImagePicker::setImageShapeUp(int i, const Shape *shapeUp) {
if (i < 0 || i >= _imageCount || !_images[i].active) {
return false;
@ -147,6 +159,36 @@ bool UIImagePicker::setImageTooltip(int i, const char *tooltip) {
return true;
}
int UIImagePicker::getImageTop(int i) {
if (i < 0 || i >= _imageCount || !_images[i].active) {
return false;
}
Image &img = _images[i];
return img.rect.top;
}
int UIImagePicker::getImageLeft(int i) {
if (i < 0 || i >= _imageCount || !_images[i].active) {
return false;
}
Image &img = _images[i];
return img.rect.left;
}
int UIImagePicker::getImageWidth(int i) {
if (i < 0 || i >= _imageCount || !_images[i].active) {
return false;
}
Image &img = _images[i];
return img.rect.width();
}
bool UIImagePicker::resetActiveImage(int i) {
if (i < 0 || i >= _imageCount || !_images[i].active) {
return false;

View File

@ -73,11 +73,16 @@ public:
bool setImageTop(int i, int top);
bool setImageLeft(int i, int left);
bool setImageWidth(int i, int16 width);
bool setImageShapeUp(int i, const Shape *shapeUp);
bool setImageShapeHovered(int i, const Shape *shapeHovered);
bool setImageShapeDown(int i, const Shape *shapeDown);
bool setImageTooltip(int i, const char *tooltip);
int getImageTop(int i);
int getImageLeft(int i);
int getImageWidth(int i);
bool resetActiveImage(int i);
void activate(UIImagePickerCallback *mouseInCallback,

View File

@ -80,7 +80,15 @@ const Color256 UIScrollBox::kTextColors4[] = {
{ 248, 224, 144 }
};
UIScrollBox::UIScrollBox(BladeRunnerEngine *vm, UIScrollBoxCallback *lineSelectedCallback, void *callbackData, int maxLineCount, int style, bool center, Common::Rect rect, Common::Rect scrollBarRect) : UIComponent(vm) {
UIScrollBox::UIScrollBox(BladeRunnerEngine *vm,
UIScrollBoxClickedCallback *lineSelectedCallback,
void *callbackData,
int maxLineCount,
int style,
bool center,
Common::Rect rect,
Common::Rect scrollBarRect) : UIComponent(vm) {
_selectedLineState = 0;
_scrollUpButtonState = 0;
_scrollDownButtonState = 0;
@ -101,7 +109,7 @@ UIScrollBox::UIScrollBox(BladeRunnerEngine *vm, UIScrollBoxCallback *lineSelecte
_callbackData = callbackData;
_isVisible = false;
_style = style;
_style = style; // 0, 1 or (new) 2. "2" is similar to "1" but with solid background for main area and scroll bar
_center = center;
_timeLastScroll = _vm->_time->currentSystem();
_timeLastCheckbox = _vm->_time->currentSystem();
@ -171,10 +179,52 @@ void UIScrollBox::hide() {
_isVisible = false;
}
bool UIScrollBox::isVisible() {
return _isVisible;
}
bool UIScrollBox::hasFocus() {
return _mouseOver;
}
void UIScrollBox::setBoxTop(int top) {
_rect.moveTo(_rect.left, top);
_rect.bottom = _rect.top + kLineHeight * _maxLinesVisible - 1;
}
void UIScrollBox::setBoxLeft(int left) {
_rect.moveTo(left, _rect.top);
}
void UIScrollBox::setBoxWidth(uint16 width) {
_rect.setWidth(width);
}
int UIScrollBox::getBoxLeft() {
return _rect.left;
}
uint16 UIScrollBox::getBoxWidth() {
return _rect.width();
}
void UIScrollBox::setScrollbarTop(int top) {
_scrollBarRect.moveTo(_scrollBarRect.left, top);
}
void UIScrollBox::setScrollbarLeft(int left) {
_scrollBarRect.moveTo(left, _scrollBarRect.top);
}
void UIScrollBox::setScrollbarWidth(uint16 width) {
_scrollBarRect.setWidth(width);
_scrollBarRect.right += 15; // right side was not used, but it's useful for determining if the control is selected
}
void UIScrollBox::clearLines() {
_lineCount = 0;
_firstLineVisible = 0;
}
void UIScrollBox::addLine(const Common::String &text, int lineData, int flags) {
@ -378,7 +428,26 @@ int UIScrollBox::getSelectedLineData() {
return -1;
}
Common::String UIScrollBox::getLineText(int lineData) {
if (hasLine(lineData)) {
return _lines[_hoveredLine]->text;
}
return "";
}
int UIScrollBox::getMaxLinesVisible() {
return _maxLinesVisible;
}
int UIScrollBox::getLineCount() {
return _lineCount;
}
void UIScrollBox::draw(Graphics::Surface &surface) {
if (!_isVisible) {
return;
}
uint32 timeNow = _vm->_time->currentSystem();
// update scrolling
@ -536,12 +605,22 @@ void UIScrollBox::draw(Graphics::Surface &surface) {
if (_lines[i]->flags & 0x08) { // has background rectangle
int colorBackground = 0;
if (_style) {
if (_style == 2) {
colorBackground = surface.format.RGBToColor(kTextBackgroundColors[colorIndex].r / 8, kTextBackgroundColors[colorIndex].g / 8, kTextBackgroundColors[colorIndex].b / 8);
} else if (_style > 0) {
colorBackground = surface.format.RGBToColor(kTextBackgroundColors[colorIndex].r, kTextBackgroundColors[colorIndex].g, kTextBackgroundColors[colorIndex].b);
} else {
colorBackground = surface.format.RGBToColor(80, 56, 32);
}
surface.fillRect(Common::Rect(x, y, _rect.right + 1, y1 + 1), colorBackground);
if (_style == 2) {
// New: style = 2 (original unused)
// original behavior -- No padding between the colored background of lines, simulate solid background (gradient)
surface.fillRect(Common::Rect(CLIP(x - 1, 0, 639), y, _rect.right + 1, y + kLineHeight), colorBackground);
} else {
// original behavior -- there is padding between the colored background of lines
surface.fillRect(Common::Rect(x, y, _rect.right + 1, y1 + 1), colorBackground);
}
}
if (_center) {
@ -557,68 +636,79 @@ void UIScrollBox::draw(Graphics::Surface &surface) {
} while (i < lastLineVisible);
}
// draw scroll up button
int scrollUpButtonShapeId = 0;
if (_scrollUpButtonState) {
if (_scrollUpButtonState == 2) {
if (_scrollUpButtonHover) {
scrollUpButtonShapeId = 72;
if (_style == 2 && getLineCount() >= getMaxLinesVisible()) {
// New: style = 2 (original unused)
// Solid background color for scrollbar
int scrollBarFillColor = surface.format.RGBToColor(k3DFrameColors[0].r / 2, k3DFrameColors[0].g / 2, k3DFrameColors[0].b / 2);
surface.fillRect(Common::Rect(_scrollBarRect.left, _scrollBarRect.top, CLIP(_scrollBarRect.left + 15, 0, 639), _scrollBarRect.bottom), scrollBarFillColor);
}
if (_style != 2
|| (_style == 2 && getLineCount() >= getMaxLinesVisible())
) {
// draw scroll up button
int scrollUpButtonShapeId = 0;
if (_scrollUpButtonState) {
if (_scrollUpButtonState == 2) {
if (_scrollUpButtonHover) {
scrollUpButtonShapeId = 72;
} else {
scrollUpButtonShapeId = 71;
}
} else {
scrollUpButtonShapeId = 71;
scrollUpButtonShapeId = 70;
}
} else if (_scrollUpButtonHover) {
scrollUpButtonShapeId = 71;
} else {
scrollUpButtonShapeId = 70;
}
} else if (_scrollUpButtonHover) {
scrollUpButtonShapeId = 71;
} else {
scrollUpButtonShapeId = 70;
}
_vm->_kia->_shapes->get(scrollUpButtonShapeId)->draw(surface, _scrollBarRect.left, _scrollBarRect.top);
_vm->_kia->_shapes->get(scrollUpButtonShapeId)->draw(surface, _scrollBarRect.left, _scrollBarRect.top);
// draw scroll down button
int scrollDownButtonShapeId = 0;
if (_scrollDownButtonState) {
if (_scrollDownButtonState == 2) {
if (_scrollDownButtonHover) {
scrollDownButtonShapeId = 75;
// draw scroll down button
int scrollDownButtonShapeId = 0;
if (_scrollDownButtonState) {
if (_scrollDownButtonState == 2) {
if (_scrollDownButtonHover) {
scrollDownButtonShapeId = 75;
} else {
scrollDownButtonShapeId = 74;
}
} else {
scrollDownButtonShapeId = 74;
scrollDownButtonShapeId = 73;
}
} else if (_scrollDownButtonHover) {
scrollDownButtonShapeId = 74;
} else {
scrollDownButtonShapeId = 73;
}
} else if (_scrollDownButtonHover) {
scrollDownButtonShapeId = 74;
} else {
scrollDownButtonShapeId = 73;
}
_vm->_kia->_shapes->get(scrollDownButtonShapeId)->draw(surface, _scrollBarRect.left, _scrollBarRect.bottom - 7);
_vm->_kia->_shapes->get(scrollDownButtonShapeId)->draw(surface, _scrollBarRect.left, _scrollBarRect.bottom - 7);
int scrollAreaSize = _scrollBarRect.bottom - (_scrollBarRect.top + 15);
int scrollBarHeight = 0;
if (_lineCount <= _maxLinesVisible) {
scrollBarHeight = _scrollBarRect.bottom - (_scrollBarRect.top + 15);
} else {
scrollBarHeight = _maxLinesVisible * scrollAreaSize / _lineCount;
}
scrollBarHeight = MAX(scrollBarHeight, 16);
int scrollAreaSize = _scrollBarRect.bottom - (_scrollBarRect.top + 15);
int scrollBarHeight = 0;
if (_lineCount <= _maxLinesVisible) {
scrollBarHeight = _scrollBarRect.bottom - (_scrollBarRect.top + 15);
} else {
scrollBarHeight = _maxLinesVisible * scrollAreaSize / _lineCount;
}
scrollBarHeight = MAX(scrollBarHeight, 16);
int v56 = 0;
if (_lineCount <= _maxLinesVisible) {
v56 = 0;
} else {
v56 = _firstLineVisible * (scrollAreaSize - scrollBarHeight) / (_lineCount - _maxLinesVisible);
}
int v56 = 0;
if (_lineCount <= _maxLinesVisible) {
v56 = 0;
} else {
v56 = _firstLineVisible * (scrollAreaSize - scrollBarHeight) / (_lineCount - _maxLinesVisible);
}
int v58 = v56 + _scrollBarRect.top + 8;
int v58 = v56 + _scrollBarRect.top + 8;
if (_scrollBarState == 2) {
draw3DFrame(surface, Common::Rect(_scrollBarRect.left, v58, _scrollBarRect.left + 15, v58 + scrollBarHeight), 1, 1);
} else if (!_scrollBarState && _scrollBarHover) {
draw3DFrame(surface, Common::Rect(_scrollBarRect.left, v56 + _scrollBarRect.top + 8, _scrollBarRect.left + 15, v58 + scrollBarHeight), 0, 1);
} else {
draw3DFrame(surface, Common::Rect(_scrollBarRect.left, v58, _scrollBarRect.left + 15, v58 + scrollBarHeight), 0, 0);
if (_scrollBarState == 2) {
draw3DFrame(surface, Common::Rect(_scrollBarRect.left, v58, _scrollBarRect.left + 15, v58 + scrollBarHeight), 1, 1);
} else if (!_scrollBarState && _scrollBarHover) {
draw3DFrame(surface, Common::Rect(_scrollBarRect.left, v56 + _scrollBarRect.top + 8, _scrollBarRect.left + 15, v58 + scrollBarHeight), 0, 1);
} else {
draw3DFrame(surface, Common::Rect(_scrollBarRect.left, v58, _scrollBarRect.left + 15, v58 + scrollBarHeight), 0, 0);
}
}
}

View File

@ -32,7 +32,7 @@
namespace BladeRunner {
typedef void UIScrollBoxCallback(void *callbackData, void *source, int lineData, int mouseButton);
typedef void UIScrollBoxClickedCallback(void *callbackData, void *source, int lineData, int mouseButton);
class UIScrollBox : public UIComponent {
static const int kLineHeight = 10;
@ -68,8 +68,8 @@ class UIScrollBox : public UIComponent {
bool _mouseButton;
UIScrollBoxCallback *_lineSelectedCallback;
void *_callbackData;
UIScrollBoxClickedCallback *_lineSelectedCallback;
void *_callbackData;
bool _isVisible;
int _style;
@ -93,7 +93,15 @@ class UIScrollBox : public UIComponent {
bool _mouseOver;
public:
UIScrollBox(BladeRunnerEngine *vm, UIScrollBoxCallback *lineSelectedCallback, void *callbackData, int maxLineCount, int style, bool center, Common::Rect rect,Common::Rect scrollBarRect);
UIScrollBox(BladeRunnerEngine *vm,
UIScrollBoxClickedCallback *lineSelectedCallback,
void *callbackData,
int maxLineCount,
int style,
bool center,
Common::Rect rect,
Common::Rect scrollBarRect);
~UIScrollBox() override;
void draw(Graphics::Surface &surface) override;
@ -105,6 +113,19 @@ public:
void show();
void hide();
bool isVisible();
bool hasFocus();
void setBoxTop(int top);
void setBoxLeft(int left);
void setBoxWidth(uint16 width);
void setScrollbarTop(int top);
void setScrollbarLeft(int left);
void setScrollbarWidth(uint16 width);
int getBoxLeft();
uint16 getBoxWidth();
void clearLines();
void addLine(const Common::String &text, int lineData, int flags);
@ -112,6 +133,9 @@ public:
void sortLines();
int getSelectedLineData();
Common::String getLineText(int lineData);
int getMaxLinesVisible();
int getLineCount();
void checkAll();
void uncheckAll();

View File

@ -30,19 +30,19 @@
namespace BladeRunner {
const Color256 UISlider::kColors[] = {
{ 0, 0, 0 },
{ 16, 8, 8 },
{ 32, 24, 8 },
{ 56, 32, 16 },
{ 72, 48, 16 },
{ 88, 56, 24 },
{ 104, 72, 32 },
{ 128, 80, 40 },
{ 136, 96, 48 },
{ 152, 112, 56 },
{ 168, 128, 72 },
{ 184, 144, 88 },
{ 200, 160, 96 },
{ 0, 0, 0 }, // Black - unpressed (framing rectange)
{ 16, 8, 8 },
{ 32, 24, 8 },
{ 56, 32, 16 },
{ 72, 48, 16 },
{ 88, 56, 24 }, // Mouse-over (framing rectange)
{ 104, 72, 32 },
{ 128, 80, 40 },
{ 136, 96, 48 },
{ 152, 112, 56 },
{ 168, 128, 72 }, // Pressed (framing rectange)
{ 184, 144, 88 },
{ 200, 160, 96 },
{ 216, 184, 112 },
{ 232, 200, 128 },
{ 240, 224, 144 }
@ -79,10 +79,12 @@ void UISlider::draw(Graphics::Surface &surface) {
frameColor = 0;
}
// Ensures animated transition of the frame's (outlining rectangle's) color to the new one
if (_currentFrameColor < frameColor) {
++_currentFrameColor;
}
// Ensures animated transition of the frame's (outlining rectangle's) color to the new one
if (_currentFrameColor > frameColor) {
--_currentFrameColor;
}