[Scummvm-git-logs] scummvm master -> 02d47a1d45e5f92e914416c5b77b294429ca704d

antoniou79 a.antoniou79 at gmail.com
Wed Jun 10 09:44:19 UTC 2020


This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
02d47a1d45 BLADERUNNER: Scrollable dropdown control for KIA (WIP)


Commit: 02d47a1d45e5f92e914416c5b77b294429ca704d
    https://github.com/scummvm/scummvm/commit/02d47a1d45e5f92e914416c5b77b294429ca704d
Author: Thanasis Antoniou (a.antoniou79 at gmail.com)
Date: 2020-06-10T12:33:05+03:00

Commit Message:
BLADERUNNER: Scrollable dropdown control for KIA (WIP)

Current purpose is for text language selection

Drop down list for language selection in KIA (early stage)

Changed paths:
  A engines/bladerunner/ui/ui_dropdown.cpp
  A engines/bladerunner/ui/ui_dropdown.h
    engines/bladerunner/bladerunner.h
    engines/bladerunner/module.mk
    engines/bladerunner/subtitles.h
    engines/bladerunner/ui/kia_section_settings.cpp
    engines/bladerunner/ui/kia_section_settings.h
    engines/bladerunner/ui/ui_container.cpp
    engines/bladerunner/ui/ui_container.h
    engines/bladerunner/ui/ui_image_picker.cpp
    engines/bladerunner/ui/ui_image_picker.h
    engines/bladerunner/ui/ui_scroll_box.cpp
    engines/bladerunner/ui/ui_scroll_box.h
    engines/bladerunner/ui/ui_slider.cpp


diff --git a/engines/bladerunner/bladerunner.h b/engines/bladerunner/bladerunner.h
index d70b8e1a6b..8631d4e920 100644
--- a/engines/bladerunner/bladerunner.h
+++ b/engines/bladerunner/bladerunner.h
@@ -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"
diff --git a/engines/bladerunner/module.mk b/engines/bladerunner/module.mk
index f1d935b606..984de0aa27 100644
--- a/engines/bladerunner/module.mk
+++ b/engines/bladerunner/module.mk
@@ -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 \
diff --git a/engines/bladerunner/subtitles.h b/engines/bladerunner/subtitles.h
index 713e45efb4..44ed1ea4c1 100644
--- a/engines/bladerunner/subtitles.h
+++ b/engines/bladerunner/subtitles.h
@@ -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;
diff --git a/engines/bladerunner/ui/kia_section_settings.cpp b/engines/bladerunner/ui/kia_section_settings.cpp
index 001579acaa..5d1e3c2fb0 100644
--- a/engines/bladerunner/ui/kia_section_settings.cpp
+++ b/engines/bladerunner/ui/kia_section_settings.cpp
@@ -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
diff --git a/engines/bladerunner/ui/kia_section_settings.h b/engines/bladerunner/ui/kia_section_settings.h
index b6ca8a7eb9..430a3b7972 100644
--- a/engines/bladerunner/ui/kia_section_settings.h
+++ b/engines/bladerunner/ui/kia_section_settings.h
@@ -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
diff --git a/engines/bladerunner/ui/ui_container.cpp b/engines/bladerunner/ui/ui_container.cpp
index 6b9feb105a..eeff854b04 100644
--- a/engines/bladerunner/ui/ui_container.cpp
+++ b/engines/bladerunner/ui/ui_container.cpp
@@ -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
diff --git a/engines/bladerunner/ui/ui_container.h b/engines/bladerunner/ui/ui_container.h
index 8d794513dd..214d55ca99 100644
--- a/engines/bladerunner/ui/ui_container.h
+++ b/engines/bladerunner/ui/ui_container.h
@@ -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);
 };
 
 
diff --git a/engines/bladerunner/ui/ui_dropdown.cpp b/engines/bladerunner/ui/ui_dropdown.cpp
new file mode 100644
index 0000000000..fe2c00de7d
--- /dev/null
+++ b/engines/bladerunner/ui/ui_dropdown.cpp
@@ -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;
+}
+
+}
diff --git a/engines/bladerunner/ui/ui_dropdown.h b/engines/bladerunner/ui/ui_dropdown.h
new file mode 100644
index 0000000000..9a89ebee91
--- /dev/null
+++ b/engines/bladerunner/ui/ui_dropdown.h
@@ -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
diff --git a/engines/bladerunner/ui/ui_image_picker.cpp b/engines/bladerunner/ui/ui_image_picker.cpp
index 7cdd50a62c..d6c01301f1 100644
--- a/engines/bladerunner/ui/ui_image_picker.cpp
+++ b/engines/bladerunner/ui/ui_image_picker.cpp
@@ -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;
diff --git a/engines/bladerunner/ui/ui_image_picker.h b/engines/bladerunner/ui/ui_image_picker.h
index 29d59ba68c..5d7bf5ddad 100644
--- a/engines/bladerunner/ui/ui_image_picker.h
+++ b/engines/bladerunner/ui/ui_image_picker.h
@@ -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,
diff --git a/engines/bladerunner/ui/ui_scroll_box.cpp b/engines/bladerunner/ui/ui_scroll_box.cpp
index b3b861e991..96ff857bf8 100644
--- a/engines/bladerunner/ui/ui_scroll_box.cpp
+++ b/engines/bladerunner/ui/ui_scroll_box.cpp
@@ -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);
-
-	// draw scroll down button
-	int scrollDownButtonShapeId = 0;
-	if (_scrollDownButtonState) {
-		if (_scrollDownButtonState == 2) {
-			if (_scrollDownButtonHover) {
-				scrollDownButtonShapeId = 75;
+		_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;
+				} 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);
+		}
 	}
 }
 
diff --git a/engines/bladerunner/ui/ui_scroll_box.h b/engines/bladerunner/ui/ui_scroll_box.h
index 1e2d0e5de9..bd32762b99 100644
--- a/engines/bladerunner/ui/ui_scroll_box.h
+++ b/engines/bladerunner/ui/ui_scroll_box.h
@@ -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();
diff --git a/engines/bladerunner/ui/ui_slider.cpp b/engines/bladerunner/ui/ui_slider.cpp
index e8fc73568c..a3eb18d6ec 100644
--- a/engines/bladerunner/ui/ui_slider.cpp
+++ b/engines/bladerunner/ui/ui_slider.cpp
@@ -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;
 	}




More information about the Scummvm-git-logs mailing list