[Scummvm-git-logs] scummvm master -> 41f2ef9745307ddbaf8b7746e0179d1448bb200f

bgK bastien.bouclet at gmail.com
Sun Nov 24 13:06:30 UTC 2019


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

Summary:
2c812a6b7a GUI: Add DropdownButtonWidget and use it in the launcher for mass add
41f2ef9745 GUI: Use the item at the click position in PopUpDialog


Commit: 2c812a6b7a0b24b9012379118867fb4f64f32c14
    https://github.com/scummvm/scummvm/commit/2c812a6b7a0b24b9012379118867fb4f64f32c14
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2019-11-24T14:06:25+01:00

Commit Message:
GUI: Add DropdownButtonWidget and use it in the launcher for mass add

DropdownButtonWidget is a button split in two parts vertically. Clicking
the left part triggers a default action. Clicking the right part shows a
list of other actions the user can choose from.

Using this widget on the launcher lets 'Mass add' be a secondary action
of the 'Add' button, removing the necessity of pressing the shift key to
access the feature.

Changed paths:
    graphics/VectorRenderer.cpp
    graphics/VectorRenderer.h
    graphics/VectorRendererSpec.cpp
    gui/ThemeEngine.cpp
    gui/ThemeEngine.h
    gui/ThemeParser.cpp
    gui/ThemeParser.h
    gui/launcher.cpp
    gui/launcher.h
    gui/options.cpp
    gui/themes/default.inc
    gui/themes/scummclassic.zip
    gui/themes/scummclassic/classic_gfx.stx
    gui/themes/scummclassic/classic_layout.stx
    gui/themes/scummclassic/classic_layout_lowres.stx
    gui/themes/scummmodern.zip
    gui/themes/scummmodern/scummmodern_gfx.stx
    gui/themes/scummmodern/scummmodern_layout.stx
    gui/themes/scummmodern/scummmodern_layout_lowres.stx
    gui/themes/scummremastered.zip
    gui/themes/scummremastered/remastered_gfx.stx
    gui/themes/scummremastered/remastered_layout.stx
    gui/themes/scummremastered/remastered_layout_lowres.stx
    gui/widget.cpp
    gui/widget.h
    gui/widgets/popup.cpp
    gui/widgets/popup.h


diff --git a/graphics/VectorRenderer.cpp b/graphics/VectorRenderer.cpp
index e226e45..bcab4bf 100644
--- a/graphics/VectorRenderer.cpp
+++ b/graphics/VectorRenderer.cpp
@@ -52,13 +52,48 @@ void VectorRenderer::drawStep(const Common::Rect &area, const Common::Rect &clip
 	setGradientFactor(step.factor);
 	setStrokeWidth(step.stroke);
 	setFillMode((FillMode)step.fillMode);
-	setClippingRect(clip);
+	setClippingRect(applyStepClippingRect(area, clip, step));
 
 	_dynamicData = extra;
 
 	(this->*(step.drawingCall))(area, step);
 }
 
+Common::Rect VectorRenderer::applyStepClippingRect(const Common::Rect &area, const Common::Rect &clip, const DrawStep &step) {
+	if (step.clip == Common::Rect()) {
+		return clip;
+	}
+
+	Common::Rect finalClip = clip;
+	if (step.clip.left > 0) {
+		finalClip.left = area.left + step.clip.left;
+	} else if (step.clip.left < 0) {
+		finalClip.left = area.right + step.clip.left;
+	}
+
+	if (step.clip.top > 0) {
+		finalClip.top = area.top + step.clip.top;
+	} else if (step.clip.top < 0) {
+		finalClip.top = area.bottom + step.clip.top;
+	}
+
+	if (step.clip.right > 0) {
+		finalClip.right = area.left + step.clip.right;
+	} else if (step.clip.right < 0) {
+		finalClip.right = area.right + step.clip.right;
+	}
+
+	if (step.clip.bottom > 0) {
+		finalClip.bottom = area.top + step.clip.bottom;
+	} else if (step.clip.bottom < 0) {
+		finalClip.bottom = area.bottom + step.clip.bottom;
+	}
+
+	finalClip.clip(clip);
+
+	return finalClip;
+}
+
 int VectorRenderer::stepGetRadius(const DrawStep &step, const Common::Rect &area) {
 	int radius = 0;
 
@@ -102,7 +137,7 @@ void VectorRenderer::stepGetPositions(const DrawStep &step, const Common::Rect &
 		}
 	} else {
 		in_x = area.left + step.padding.left;
-		in_w = area.width();
+		in_w = area.width() - step.padding.left - step.padding.right;
 	}
 
 	if (!step.autoHeight) {
@@ -133,7 +168,7 @@ void VectorRenderer::stepGetPositions(const DrawStep &step, const Common::Rect &
 		}
 	} else {
 		in_y = area.top + step.padding.top;
-		in_h = area.height();
+		in_h = area.height() - step.padding.top - step.padding.bottom;
 	}
 
 	if (step.scale != (1 << 16) && step.scale != 0) {
diff --git a/graphics/VectorRenderer.h b/graphics/VectorRenderer.h
index 9be5546..28a987a 100644
--- a/graphics/VectorRenderer.h
+++ b/graphics/VectorRenderer.h
@@ -64,6 +64,7 @@ struct DrawStep {
 						  negative values mean counting from the opposite direction */
 
 	Common::Rect padding;
+	Common::Rect clip; /**< Clipping rect restriction */
 
 	enum VectorAlignment {
 		kVectorAlignManual,
@@ -392,6 +393,11 @@ public:
 	int stepGetRadius(const DrawStep &step, const Common::Rect &area);
 
 	/**
+	 * Restrict a draw call clipping rect with a step specific clipping rect
+	 */
+	Common::Rect applyStepClippingRect(const Common::Rect &area, const Common::Rect &clip, const DrawStep &step);
+
+	/**
 	 * DrawStep callback functions for each drawing feature
 	 */
 	void drawCallback_CIRCLE(const Common::Rect &area, const DrawStep &step) {
@@ -412,7 +418,7 @@ public:
 	void drawCallback_LINE(const Common::Rect &area, const DrawStep &step) {
 		uint16 x, y, w, h;
 		stepGetPositions(step, area, x, y, w, h);
-		drawLine(x, y, x + w, y + w);
+		drawLine(x, y, x + w, y + h);
 	}
 
 	void drawCallback_ROUNDSQ(const Common::Rect &area, const DrawStep &step) {
diff --git a/graphics/VectorRendererSpec.cpp b/graphics/VectorRendererSpec.cpp
index 457e9ff..a1d8ba3 100644
--- a/graphics/VectorRendererSpec.cpp
+++ b/graphics/VectorRendererSpec.cpp
@@ -670,9 +670,9 @@ gradientFillClip(PixelType *ptr, int width, int x, int y, int realX, int realY)
 	if (grad == 0 ||
 		_gradCache[curGrad] == _gradCache[curGrad + 1] || // no color change
 		stripSize < 2) { // the stip is small
-		colorFill<PixelType>(ptr, ptr + width, _gradCache[curGrad]);
+		colorFillClip<PixelType>(ptr, ptr + width, _gradCache[curGrad], realX, realY, _clippingArea);
 	} else if (grad == 3 && ox) {
-		colorFill<PixelType>(ptr, ptr + width, _gradCache[curGrad + 1]);
+		colorFillClip<PixelType>(ptr, ptr + width, _gradCache[curGrad + 1], realX, realY, _clippingArea);
 	} else {
 		for (int j = x; j < x + width; j++, ptr++) {
 			if (realX + j - x < _clippingArea.left || realX + j - x >= _clippingArea.right) continue;
diff --git a/gui/ThemeEngine.cpp b/gui/ThemeEngine.cpp
index e27ee7a..2c08970 100644
--- a/gui/ThemeEngine.cpp
+++ b/gui/ThemeEngine.cpp
@@ -126,9 +126,16 @@ static const DrawDataInfo kDrawDataDefaults[] = {
 	{kDDWidgetBackgroundSlider,     "widget_slider",    kDrawLayerBackground,   kDDNone},
 
 	{kDDButtonIdle,                 "button_idle",      kDrawLayerBackground,   kDDNone},
-	{kDDButtonHover,                "button_hover",     kDrawLayerForeground,  kDDButtonIdle},
+	{kDDButtonHover,                "button_hover",     kDrawLayerForeground,   kDDButtonIdle},
 	{kDDButtonDisabled,             "button_disabled",  kDrawLayerBackground,   kDDNone},
-	{kDDButtonPressed,              "button_pressed",   kDrawLayerForeground,  kDDButtonIdle},
+	{kDDButtonPressed,              "button_pressed",   kDrawLayerForeground,   kDDButtonIdle},
+
+	{kDDDropDownButtonIdle,         "dropdown_button_idle",          kDrawLayerBackground, kDDNone},
+	{kDDDropDownButtonHoverLeft,    "dropdown_button_hover_left",    kDrawLayerForeground, kDDDropDownButtonIdle},
+	{kDDDropDownButtonHoverRight,   "dropdown_button_hover_right",   kDrawLayerForeground, kDDDropDownButtonIdle},
+	{kDDDropDownButtonDisabled,     "dropdown_button_disabled",      kDrawLayerForeground, kDDNone},
+	{kDDDropDownButtonPressedLeft,  "dropdown_button_pressed_left",  kDrawLayerForeground, kDDDropDownButtonIdle},
+	{kDDDropDownButtonPressedRight, "dropdown_button_pressed_right", kDrawLayerForeground, kDDDropDownButtonIdle},
 
 	{kDDSliderFull,                 "slider_full",      kDrawLayerForeground,  kDDNone},
 	{kDDSliderHover,                "slider_hover",     kDrawLayerForeground,  kDDNone},
@@ -935,6 +942,35 @@ void ThemeEngine::drawButton(const Common::Rect &r, const Common::String &str, W
 	           _widgets[dd]->_textAlignV);
 }
 
+void ThemeEngine::drawDropDownButton(const Common::Rect &r, uint32 dropdownWidth, const Common::String &str,
+                                     ThemeEngine::WidgetStateInfo buttonState, bool inButton, bool inDropdown) {
+	if (!ready())
+		return;
+
+	DrawData dd;
+	if (buttonState == kStateHighlight && inButton)
+		dd = kDDDropDownButtonHoverLeft;
+	else if (buttonState == kStateHighlight && inDropdown)
+		dd = kDDDropDownButtonHoverRight;
+	else if (buttonState == kStateDisabled)
+		dd = kDDDropDownButtonDisabled;
+	else if (buttonState == kStatePressed && inButton)
+		dd = kDDDropDownButtonPressedLeft;
+	else if (buttonState == kStatePressed && inDropdown)
+		dd = kDDDropDownButtonPressedRight;
+	else
+		dd = kDDDropDownButtonIdle;
+
+	drawDD(dd, r);
+
+	// Center the text in the button without using the area of the drop down button
+	Common::Rect textRect = r;
+	textRect.left  = r.left  + dropdownWidth;
+	textRect.right = r.right - dropdownWidth;
+	drawDDText(getTextData(dd), getTextColor(dd), textRect, str, false, true, _widgets[dd]->_textAlignH,
+	           _widgets[dd]->_textAlignV);
+}
+
 void ThemeEngine::drawLineSeparator(const Common::Rect &r) {
 	if (!ready())
 		return;
diff --git a/gui/ThemeEngine.h b/gui/ThemeEngine.h
index 1c35b1e..827ec19 100644
--- a/gui/ThemeEngine.h
+++ b/gui/ThemeEngine.h
@@ -80,6 +80,13 @@ enum DrawData {
 	kDDButtonDisabled,
 	kDDButtonPressed,
 
+	kDDDropDownButtonIdle,
+	kDDDropDownButtonHoverLeft,
+	kDDDropDownButtonHoverRight,
+	kDDDropDownButtonDisabled,
+	kDDDropDownButtonPressedLeft,
+	kDDDropDownButtonPressedRight,
+
 	kDDSliderFull,
 	kDDSliderHover,
 	kDDSliderDisabled,
@@ -400,6 +407,9 @@ public:
 	void drawButton(const Common::Rect &r, const Common::String &str, WidgetStateInfo state = kStateEnabled,
 	                uint16 hints = 0);
 
+	void drawDropDownButton(const Common::Rect &r, uint32 dropdownWidth, const Common::String &str,
+	                        WidgetStateInfo buttonState, bool inButton, bool inDropdown);
+
 	void drawSurface(const Common::Point &p, const Graphics::Surface &surface, bool themeTrans = false);
 
 	void drawSlider(const Common::Rect &r, int width, WidgetStateInfo state = kStateEnabled);
diff --git a/gui/ThemeParser.cpp b/gui/ThemeParser.cpp
index a52e359..f92676a 100644
--- a/gui/ThemeParser.cpp
+++ b/gui/ThemeParser.cpp
@@ -629,6 +629,17 @@ bool ThemeParser::parseDrawStep(ParserNode *stepNode, Graphics::DrawStep *drawst
 		}
 	}
 
+	if (stepNode->values.contains("clip")) {
+		val = stepNode->values["clip"];
+		int cl, ct, cr, cb;
+		if (parseIntegerKey(val, 4, &cl, &ct, &cr, &cb)) {
+			drawstep->clip.left = cl;
+			drawstep->clip.top = ct;
+			drawstep->clip.right = cr;
+			drawstep->clip.bottom = cb;
+		}
+	}
+
 #undef PARSER_ASSIGN_INT
 #undef PARSER_ASSIGN_RGB
 
diff --git a/gui/ThemeParser.h b/gui/ThemeParser.h
index 1557314..f55a24a 100644
--- a/gui/ThemeParser.h
+++ b/gui/ThemeParser.h
@@ -147,6 +147,7 @@ protected:
 					XML_PROP(orientation, false)
 					XML_PROP(file, false)
 					XML_PROP(autoscale, false)
+					XML_PROP(clip, false)
 				KEY_END()
 
 				XML_KEY(text)
diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index 84512ea..15866c0 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -66,9 +66,11 @@ enum {
 	kAboutCmd = 'ABOU',
 	kOptionsCmd = 'OPTN',
 	kAddGameCmd = 'ADDG',
+	kMassAddGameCmd = 'MADD',
 	kEditGameCmd = 'EDTG',
 	kRemoveGameCmd = 'REMG',
 	kLoadGameCmd = 'LOAD',
+	kRecordGameCmd = 'RECG',
 	kQuitCmd = 'QUIT',
 	kSearchCmd = 'SRCH',
 	kListSearchCmd = 'LSSR',
@@ -145,20 +147,30 @@ void LauncherDialog::build() {
 	_startButton =
 		new ButtonWidget(this, "Launcher.StartButton", _("~S~tart"), _("Start selected game"), kStartCmd);
 
-	_loadButton =
-		new ButtonWidget(this, "Launcher.LoadGameButton", _("~L~oad..."), _("Load saved game for selected game"), kLoadGameCmd);
+	DropdownButtonWidget *loadButton =
+	        new DropdownButtonWidget(this, "Launcher.LoadGameButton", _("~L~oad..."), _("Load saved game for selected game"), kLoadGameCmd);
+#ifdef ENABLE_EVENTRECORDER
+	loadButton->appendEntry(_s("Record..."), kRecordGameCmd);
+#endif
+	_loadButton = loadButton;
 
 	// Above the lowest button rows: two more buttons (directly below the list box)
 	if (g_system->getOverlayWidth() > 320) {
-		_addButton =
-			new ButtonWidget(this, "Launcher.AddGameButton", _("~A~dd Game..."), _("Hold Shift for Mass Add"), kAddGameCmd);
+		DropdownButtonWidget *addButton =
+			new DropdownButtonWidget(this, "Launcher.AddGameButton", _("~A~dd Game..."), _("Add games to the list"), kAddGameCmd);
+		addButton->appendEntry(_s("Mass Add..."), kMassAddGameCmd);
+		_addButton = addButton;
+
 		_editButton =
 			new ButtonWidget(this, "Launcher.EditGameButton", _("~E~dit Game..."), _("Change game options"), kEditGameCmd);
 		_removeButton =
 			new ButtonWidget(this, "Launcher.RemoveGameButton", _("~R~emove Game"), _("Remove game from the list. The game data files stay intact"), kRemoveGameCmd);
 	} else {
-		_addButton =
-		new ButtonWidget(this, "Launcher.AddGameButton", _c("~A~dd Game...", "lowres"), _("Hold Shift for Mass Add"), kAddGameCmd);
+		DropdownButtonWidget *addButton =
+			new DropdownButtonWidget(this, "Launcher.AddGameButton", _c("~A~dd Game...", "lowres"), _("Add games to the list"), kAddGameCmd);
+		addButton->appendEntry(_c("Mass Add...", "lowres"), kMassAddGameCmd);
+		_addButton = addButton;
+
 		_editButton =
 		new ButtonWidget(this, "Launcher.EditGameButton", _c("~E~dit Game...", "lowres"), _("Change game options"), kEditGameCmd);
 		_removeButton =
@@ -318,38 +330,6 @@ void LauncherDialog::updateListing() {
 }
 
 void LauncherDialog::addGame() {
-
-#ifndef DISABLE_MASS_ADD
-	const bool massAdd = checkModifier(Common::KBD_SHIFT);
-
-	if (massAdd) {
-		MessageDialog alert(_("Do you really want to run the mass game detector? "
-							  "This could potentially add a huge number of games."), _("Yes"), _("No"));
-		if (alert.runModal() == GUI::kMessageOK && _browser->runModal() > 0) {
-			MassAddDialog massAddDlg(_browser->getResult());
-
-			massAddDlg.runModal();
-
-			// Update the ListWidget and force a redraw
-
-			// If new target(s) were added, update the ListWidget and move
-			// the selection to to first newly detected game.
-			Common::String newTarget = massAddDlg.getFirstAddedTarget();
-			if (!newTarget.empty()) {
-				updateListing();
-				selectTarget(newTarget);
-			}
-
-			g_gui.scheduleTopDialogRedraw();
-		}
-
-		// We need to update the buttons here, so "Mass add" will revert to "Add game"
-		// without any additional event.
-		updateButtons();
-		return;
-	}
-#endif
-
 	// Allow user to add a new game to the list.
 	// 1) show a dir selection dialog which lets the user pick the directory
 	//    the game data resides in.
@@ -392,6 +372,28 @@ void LauncherDialog::addGame() {
 	} while (looping);
 }
 
+void LauncherDialog::massAddGame() {
+	MessageDialog alert(_("Do you really want to run the mass game detector? "
+						  "This could potentially add a huge number of games."), _("Yes"), _("No"));
+	if (alert.runModal() == GUI::kMessageOK && _browser->runModal() > 0) {
+		MassAddDialog massAddDlg(_browser->getResult());
+
+		massAddDlg.runModal();
+
+		// Update the ListWidget and force a redraw
+
+		// If new target(s) were added, update the ListWidget and move
+		// the selection to to first newly detected game.
+		Common::String newTarget = massAddDlg.getFirstAddedTarget();
+		if (!newTarget.empty()) {
+			updateListing();
+			selectTarget(newTarget);
+		}
+
+		g_gui.scheduleTopDialogRedraw();
+	}
+}
+
 void LauncherDialog::removeGame(int item) {
 	MessageDialog alert(_("Do you really want to remove this game configuration?"), _("Yes"), _("No"));
 
@@ -432,20 +434,6 @@ void LauncherDialog::editGame(int item) {
 	}
 }
 
-void LauncherDialog::loadGameButtonPressed(int item) {
-#ifdef ENABLE_EVENTRECORDER
-	const bool shiftPressed = checkModifier(Common::KBD_SHIFT);
-	if (shiftPressed) {
-		recordGame(item);
-	} else {
-		loadGame(item);
-	}
-	updateButtons();
-#else
-	loadGame(item);
-#endif
-}
-
 #ifdef ENABLE_EVENTRECORDER
 void LauncherDialog::recordGame(int item) {
 	RecorderDialog recorderDialog;
@@ -640,6 +628,9 @@ void LauncherDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 	case kAddGameCmd:
 		addGame();
 		break;
+	case kMassAddGameCmd:
+		massAddGame();
+		break;
 	case kRemoveGameCmd:
 		removeGame(item);
 		break;
@@ -647,8 +638,13 @@ void LauncherDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 		editGame(item);
 		break;
 	case kLoadGameCmd:
-		loadGameButtonPressed(item);
+		loadGame(item);
 		break;
+#ifdef ENABLE_EVENTRECORDER
+	case kRecordGameCmd:
+		recordGame(item);
+		break;
+#endif
 	case kOptionsCmd: {
 		GlobalOptionsDialog options(this);
 		options.runModal();
@@ -717,28 +713,8 @@ void LauncherDialog::updateButtons() {
 		_loadButton->setEnabled(en);
 		_loadButton->markAsDirty();
 	}
-	switchButtonsText(_addButton, "~A~dd Game...", _s("Mass Add..."));
-#ifdef ENABLE_EVENTRECORDER
-	switchButtonsText(_loadButton, "~L~oad...", _s("Record..."));
-#endif
 }
 
-// Update the label of the button depending on whether shift is pressed or not
-void LauncherDialog::switchButtonsText(ButtonWidget *button, const char *normalText, const char *shiftedText) {
-	const bool shiftPressed = checkModifier(Common::KBD_SHIFT);
-	const bool lowRes = g_system->getOverlayWidth() <= 320;
-
-	const char *newAddButtonLabel = shiftPressed
-		? (lowRes ? _c(shiftedText, "lowres") : _(shiftedText))
-		: (lowRes ? _c(normalText, "lowres") : _(normalText));
-
-	if (button->getLabel() != newAddButtonLabel)
-		button->setLabel(newAddButtonLabel);
-}
-
-
-
-
 void LauncherDialog::reflowLayout() {
 #ifndef DISABLE_FANCY_THEMES
 	if (g_gui.xmlEval()->getVar("Globals.ShowLauncherLogo") == 1 && g_gui.theme()->supportsImages()) {
diff --git a/gui/launcher.h b/gui/launcher.h
index 5bb386f..7009b99 100644
--- a/gui/launcher.h
+++ b/gui/launcher.h
@@ -82,7 +82,6 @@ protected:
 	void updateListing();
 
 	void updateButtons();
-	void switchButtonsText(ButtonWidget *button, const char *normalText, const char *shiftedText);
 
 	void build();
 	void clean();
@@ -94,6 +93,7 @@ protected:
 	 * Handle "Add game..." button.
 	 */
 	virtual void addGame();
+	void massAddGame();
 
 	/**
 	 * Handle "Remove game..." button.
@@ -106,11 +106,6 @@ protected:
 	void editGame(int item);
 
 	/**
-	 * Facade for "Load..."/"Record..." buttons.
-	 */
-	void loadGameButtonPressed(int item);
-
-	/**
 	 * Handle "Record..." button.
 	 */
 	void recordGame(int item);
diff --git a/gui/options.cpp b/gui/options.cpp
index a324ef6..161742d 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -2154,22 +2154,24 @@ void GlobalOptionsDialog::apply() {
 	}
 #ifdef USE_TTS
 	Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
-	if (newLang != oldLang) {
-		if (newLang == "C")
-			ttsMan->setLanguage("en");
-		else {
-			ttsMan->setLanguage(newLang);
+	if (ttsMan) {
+		if (newLang != oldLang) {
+			if (newLang == "C")
+				ttsMan->setLanguage("en");
+			else {
+				ttsMan->setLanguage(newLang);
+			}
+			_ttsVoiceSelectionPopUp->setSelectedTag(0);
 		}
-		_ttsVoiceSelectionPopUp->setSelectedTag(0);
+		int volume = (ConfMan.getInt("speech_volume", "scummvm") * 100) / 256;
+		if (ConfMan.hasKey("mute", "scummvm") && ConfMan.getBool("mute", "scummvm"))
+			volume = 0;
+		ttsMan->setVolume(volume);
+		ConfMan.setBool("tts_enabled", _ttsCheckbox->getState(), _domain);
+		int selectedVoice = _ttsVoiceSelectionPopUp->getSelectedTag();
+		ConfMan.setInt("tts_voice", selectedVoice, _domain);
+		ttsMan->setVoice(selectedVoice);
 	}
-	int volume = (ConfMan.getInt("speech_volume", "scummvm") * 100) / 256;
-	if (ConfMan.hasKey("mute", "scummvm") && ConfMan.getBool("mute", "scummvm"))
-		volume = 0;
-	ttsMan->setVolume(volume);
-	ConfMan.setBool("tts_enabled", _ttsCheckbox->getState(), _domain);
-	int selectedVoice = _ttsVoiceSelectionPopUp->getSelectedTag();
-	ConfMan.setInt("tts_voice", selectedVoice, _domain);
-	ttsMan->setVoice(selectedVoice);
 #endif
 
 	if (isRebuildNeeded) {
diff --git a/gui/themes/default.inc b/gui/themes/default.inc
index 3e09b49..9b6b102 100644
--- a/gui/themes/default.inc
+++ b/gui/themes/default.inc
@@ -17,6 +17,9 @@ const char *defaultXML1 = "<?xml version = '1.0'?>"
 "<color name='green2' "
 "rgb='0,255,0' "
 "/>"
+"<color name='white' "
+"rgb='255,255,255' "
+"/>"
 "</palette>"
 "<fonts>"
 "<font id='text_default' "
@@ -62,7 +65,7 @@ const char *defaultXML1 = "<?xml version = '1.0'?>"
 "color='lightgrey' "
 "/>"
 "<text_color id='color_alternative_inverted' "
-"color='255,255,255' "
+"color='white' "
 "/>"
 "<text_color id='color_alternative_hover' "
 "color='176,176,176' "
@@ -508,6 +511,302 @@ const char *defaultXML1 = "<?xml version = '1.0'?>"
 "fill='none' "
 "/>"
 "</drawdata>"
+"<drawdata id='dropdown_button_idle' cache='false' resolution='y>399'>"
+"<text font='text_button' "
+"text_color='color_button' "
+"vertical_align='center' "
+"horizontal_align='center' "
+"/>"
+"<drawstep func='bevelsq' "
+"bevel='2' "
+"fill='none' "
+"/>"
+"<drawstep func='triangle' "
+"fg_color='green' "
+"fill='foreground' "
+"width='8' "
+"height='6' "
+"xpos='right' "
+"ypos='center' "
+"padding='0,0,4,0' "
+"orientation='bottom' "
+"/>"
+"<drawstep func='line' "
+"fg_color='lightgrey' "
+"stroke='2' "
+"fill='foreground' "
+"width='0' "
+"height='auto' "
+"xpos='right' "
+"ypos='center' "
+"padding='0,0,17,1' "
+"/>"
+"</drawdata>"
+"<drawdata id='dropdown_button_idle' cache='false' resolution='y<400'>"
+"<text font='text_button' "
+"text_color='color_button' "
+"vertical_align='center' "
+"horizontal_align='center' "
+"/>"
+"<drawstep func='bevelsq' "
+"bevel='2' "
+"fill='none' "
+"/>"
+"<drawstep func='triangle' "
+"fg_color='green' "
+"fill='foreground' "
+"width='6' "
+"height='6' "
+"xpos='right' "
+"ypos='center' "
+"padding='0,0,0,0' "
+"orientation='bottom' "
+"/>"
+"</drawdata>"
+"<drawdata id='dropdown_button_hover_left' cache='false' resolution='y>399'>"
+"<text font='text_button' "
+"text_color='color_button_hover' "
+"vertical_align='center' "
+"horizontal_align='center' "
+"/>"
+"<drawstep func='bevelsq' "
+"bevel='2' "
+"fill='none' "
+"/>"
+"<drawstep func='triangle' "
+"fg_color='green' "
+"fill='foreground' "
+"width='8' "
+"height='6' "
+"xpos='right' "
+"ypos='center' "
+"padding='0,0,4,0' "
+"orientation='bottom' "
+"/>"
+"<drawstep func='line' "
+"fg_color='lightgrey' "
+"stroke='2' "
+"fill='foreground' "
+"width='0' "
+"height='auto' "
+"xpos='right' "
+"ypos='center' "
+"padding='0,0,17,1' "
+"/>"
+"</drawdata>"
+"<drawdata id='dropdown_button_hover_left' cache='false' resolution='y<400'>"
+"<text font='text_button' "
+"text_color='color_button_hover' "
+"vertical_align='center' "
+"horizontal_align='center' "
+"/>"
+"<drawstep func='bevelsq' "
+"bevel='2' "
+"fill='none' "
+"/>"
+"<drawstep func='triangle' "
+"fg_color='green' "
+"fill='foreground' "
+"width='6' "
+"height='6' "
+"xpos='right' "
+"ypos='center' "
+"padding='0,0,0,0' "
+"orientation='bottom' "
+"/>"
+"</drawdata>"
+"<drawdata id='dropdown_button_hover_right' cache='false' resolution='y>399'>"
+"<text font='text_button' "
+"text_color='color_button' "
+"vertical_align='center' "
+"horizontal_align='center' "
+"/>"
+"<drawstep func='bevelsq' "
+"bevel='2' "
+"fill='none' "
+"/>"
+"<drawstep func='triangle' "
+"fg_color='green2' "
+"fill='foreground' "
+"width='8' "
+"height='6' "
+"xpos='right' "
+"ypos='center' "
+"padding='0,0,4,0' "
+"orientation='bottom' "
+"/>"
+"<drawstep func='line' "
+"fg_color='lightgrey' "
+"stroke='2' "
+"fill='foreground' "
+"width='0' "
+"height='auto' "
+"xpos='right' "
+"ypos='center' "
+"padding='0,0,17,1' "
+"/>"
+"</drawdata>"
+"<drawdata id='dropdown_button_hover_right' cache='false' resolution='y<400'>"
+"<text font='text_button' "
+"text_color='color_button' "
+"vertical_align='center' "
+"horizontal_align='center' "
+"/>"
+"<drawstep func='bevelsq' "
+"bevel='2' "
+"fill='none' "
+"/>"
+"<drawstep func='triangle' "
+"fg_color='green2' "
+"fill='foreground' "
+"width='6' "
+"height='6' "
+"xpos='right' "
+"ypos='center' "
+"padding='0,0,0,0' "
+"orientation='bottom' "
+"/>"
+"</drawdata>"
+"<drawdata id='dropdown_button_disabled' cache='false' resolution='y>399'>"
+"<text font='text_button' "
+"text_color='color_button_disabled' "
+"vertical_align='center' "
+"horizontal_align='center' "
+"/>"
+"<drawstep func='bevelsq' "
+"bevel='2' "
+"fill='none' "
+"/>"
+"<drawstep func='triangle' "
+"fg_color='lightgrey' "
+"fill='foreground' "
+"width='8' "
+"height='6' "
+"xpos='right' "
+"ypos='center' "
+"padding='0,0,4,0' "
+"orientation='bottom' "
+"/>"
+"<drawstep func='line' "
+"fg_color='lightgrey' "
+"stroke='2' "
+"fill='foreground' "
+"width='0' "
+"height='auto' "
+"xpos='right' "
+"ypos='center' "
+"padding='0,0,17,1' "
+"/>"
+"</drawdata>"
+"<drawdata id='dropdown_button_disabled' cache='false' resolution='y<400'>"
+"<text font='text_button' "
+"text_color='color_button_disabled' "
+"vertical_align='center' "
+"horizontal_align='center' "
+"/>"
+"<drawstep func='bevelsq' "
+"bevel='2' "
+"fill='none' "
+"/>"
+"<drawstep func='triangle' "
+"fg_color='lightgrey' "
+"fill='foreground' "
+"width='6' "
+"height='6' "
+"xpos='right' "
+"ypos='center' "
+"padding='0,0,0,0' "
+"orientation='bottom' "
+"/>"
+"</drawdata>"
+"<drawdata id='dropdown_button_pressed_left' cache='false' resolution='y>399'>"
+"<text font='text_button' "
+"text_color='color_alternative_inverted' "
+"vertical_align='center' "
+"horizontal_align='center' "
+"/>"
+"<drawstep func='square' "
+"fill='foreground' "
+"fg_color='green' "
+"clip='0,0,-18,0' "
+"/>"
+"<drawstep func='triangle' "
+"fg_color='green' "
+"fill='foreground' "
+"width='8' "
+"height='6' "
+"xpos='right' "
+"ypos='center' "
+"padding='0,0,4,0' "
+"orientation='bottom' "
+"/>"
+"</drawdata>"
+"<drawdata id='dropdown_button_pressed_left' cache='false' resolution='y<400'>"
+"<text font='text_button' "
+"text_color='color_alternative_inverted' "
+"vertical_align='center' "
+"horizontal_align='center' "
+"/>"
+"<drawstep func='square' "
+"fill='foreground' "
+"fg_color='green' "
+"clip='0,0,-7,0' "
+"/>"
+"<drawstep func='triangle' "
+"fg_color='green' "
+"fill='foreground' "
+"width='6' "
+"height='6' "
+"xpos='right' "
+"ypos='center' "
+"padding='0,0,0,0' "
+"orientation='bottom' "
+"/>"
+"</drawdata>"
+"<drawdata id='dropdown_button_pressed_right' cache='false' resolution='y>399'>"
+"<text font='text_button' "
+"text_color='color_button' "
+"vertical_align='center' "
+"horizontal_align='center' "
+"/>"
+"<drawstep func='square' "
+"fill='foreground' "
+"fg_color='green' "
+"clip='-16,0,0,0' "
+"/>"
+"<drawstep func='triangle' "
+"fg_color='white' "
+"fill='foreground' "
+"width='8' "
+"height='6' "
+"xpos='right' "
+"ypos='center' "
+"padding='0,0,4,0' "
+"orientation='bottom' "
+"/>"
+"</drawdata>"
+"<drawdata id='dropdown_button_pressed_right' cache='false' resolution='y<400'>"
+"<text font='text_button' "
+"text_color='color_button' "
+"vertical_align='center' "
+"horizontal_align='center' "
+"/>"
+"<drawstep func='square' "
+"fill='foreground' "
+"fg_color='green' "
+"clip='-7,0,0,0' "
+"/>"
+"<drawstep func='triangle' "
+"fg_color='white' "
+"fill='foreground' "
+"width='6' "
+"height='6' "
+"xpos='right' "
+"ypos='center' "
+"padding='0,0,0,0' "
+"orientation='bottom' "
+"/>"
+"</drawdata>"
 "<drawdata id='checkbox_disabled' cache='false'>"
 "<text font='text_default' "
 "text_color='color_normal_disabled' "
@@ -637,6 +936,7 @@ const char *defaultXML1 = "<?xml version = '1.0'?>"
 "<def var='Tooltip.YDelta' value='16'/>"
 "<def var='Predictive.Button.Width' value='60' />"
 "<def var='Predictive.ShowDeletePic' value='0'/>"
+"<def var='DropdownButton.Width' value='17'/>"
 "<widget name='OptionsLabel' "
 "size='110,Globals.Line.Height' "
 "textalign='right' "
@@ -2339,6 +2639,8 @@ const char *defaultXML1 = "<?xml version = '1.0'?>"
 "</layout>"
 "</layout>"
 "</dialog>"
+"<dialog name='DropdownDialog' overlays='screen_center' shading='luminance'>"
+"</dialog>"
 "</layout_info>"
 ;
  const char *defaultXML4 = "<layout_info resolution='y<400'>"
@@ -2364,6 +2666,7 @@ const char *defaultXML1 = "<?xml version = '1.0'?>"
 "<def var='Predictive.Button.Width' value='45' />"
 "<def var='Predictive.Button.Height' value='15' />"
 "<def var='Predictive.ShowDeletePic' value='0'/>"
+"<def var='DropdownButton.Width' value='7'/>"
 "<widget name='Button' "
 "size='72,16' "
 "/>"
@@ -4034,6 +4337,8 @@ const char *defaultXML1 = "<?xml version = '1.0'?>"
 "</layout>"
 "</layout>"
 "</dialog>"
+"<dialog name='DropdownDialog' overlays='screen_center' shading='luminance'>"
+"</dialog>"
 "</layout_info>"
 ;
 const char *defaultXML[] = { defaultXML1, defaultXML2, defaultXML3, defaultXML4 };
diff --git a/gui/themes/scummclassic.zip b/gui/themes/scummclassic.zip
index b59e9b0..eb43825 100644
Binary files a/gui/themes/scummclassic.zip and b/gui/themes/scummclassic.zip differ
diff --git a/gui/themes/scummclassic/classic_gfx.stx b/gui/themes/scummclassic/classic_gfx.stx
index 8b72802..a5fa994 100644
--- a/gui/themes/scummclassic/classic_gfx.stx
+++ b/gui/themes/scummclassic/classic_gfx.stx
@@ -37,6 +37,9 @@
 		<color name = 'green2'
 				rgb = '0, 255, 0'
 		/>
+		<color name = 'white'
+				rgb = '255, 255, 255'
+		/>
 	</palette>
 
 	<fonts>
@@ -89,7 +92,7 @@
 		/>
 
 		<text_color	id = 'color_alternative_inverted'
-				color = '255, 255, 255'
+				color = 'white'
 		/>
 
 		<text_color	id = 'color_alternative_hover'
@@ -593,6 +596,308 @@
 		/>
 	</drawdata>
 
+	<drawdata id = 'dropdown_button_idle' cache = 'false' resolution = 'y>399'>
+		<text	font = 'text_button'
+				text_color = 'color_button'
+				vertical_align = 'center'
+				horizontal_align = 'center'
+		/>
+		<drawstep	func = 'bevelsq'
+					bevel = '2'
+					fill = 'none'
+		/>
+		<drawstep	func = 'triangle'
+					fg_color = 'green'
+					fill = 'foreground'
+					width = '8'
+					height = '6'
+					xpos = 'right'
+					ypos = 'center'
+					padding = '0,0,4,0'
+					orientation = 'bottom'
+		/>
+		<drawstep	func = 'line'
+					fg_color = 'lightgrey'
+					stroke = '2'
+					fill = 'foreground'
+					width = '0'
+					height = 'auto'
+					xpos = 'right'
+					ypos = 'center'
+					padding = '0,0,17,1'
+		/>
+	</drawdata>
+	<drawdata id = 'dropdown_button_idle' cache = 'false' resolution = 'y<400'>
+		<text	font = 'text_button'
+				text_color = 'color_button'
+				vertical_align = 'center'
+				horizontal_align = 'center'
+		/>
+		<drawstep	func = 'bevelsq'
+					bevel = '2'
+					fill = 'none'
+		/>
+		<drawstep	func = 'triangle'
+					fg_color = 'green'
+					fill = 'foreground'
+					width = '6'
+					height = '6'
+					xpos = 'right'
+					ypos = 'center'
+					padding = '0,0,0,0'
+					orientation = 'bottom'
+		/>
+	</drawdata>
+
+	<drawdata id = 'dropdown_button_hover_left' cache = 'false' resolution = 'y>399'>
+		<text	font = 'text_button'
+				text_color = 'color_button_hover'
+				vertical_align = 'center'
+				horizontal_align = 'center'
+		/>
+		<drawstep	func = 'bevelsq'
+					bevel = '2'
+					fill = 'none'
+		/>
+		<drawstep	func = 'triangle'
+					fg_color = 'green'
+					fill = 'foreground'
+					width = '8'
+					height = '6'
+					xpos = 'right'
+					ypos = 'center'
+					padding = '0,0,4,0'
+					orientation = 'bottom'
+		/>
+		<drawstep	func = 'line'
+					fg_color = 'lightgrey'
+					stroke = '2'
+					fill = 'foreground'
+					width = '0'
+					height = 'auto'
+					xpos = 'right'
+					ypos = 'center'
+					padding = '0,0,17,1'
+		/>
+	</drawdata>
+	<drawdata id = 'dropdown_button_hover_left' cache = 'false' resolution = 'y<400'>
+		<text	font = 'text_button'
+				text_color = 'color_button_hover'
+				vertical_align = 'center'
+				horizontal_align = 'center'
+		/>
+		<drawstep	func = 'bevelsq'
+					bevel = '2'
+					fill = 'none'
+		/>
+		<drawstep	func = 'triangle'
+					fg_color = 'green'
+					fill = 'foreground'
+					width = '6'
+					height = '6'
+					xpos = 'right'
+					ypos = 'center'
+					padding = '0,0,0,0'
+					orientation = 'bottom'
+		/>
+	</drawdata>
+
+	<drawdata id = 'dropdown_button_hover_right' cache = 'false' resolution = 'y>399'>
+		<text	font = 'text_button'
+				text_color = 'color_button'
+				vertical_align = 'center'
+				horizontal_align = 'center'
+		/>
+		<drawstep	func = 'bevelsq'
+					bevel = '2'
+					fill = 'none'
+		/>
+		<drawstep	func = 'triangle'
+					fg_color = 'green2'
+					fill = 'foreground'
+					width = '8'
+					height = '6'
+					xpos = 'right'
+					ypos = 'center'
+					padding = '0,0,4,0'
+					orientation = 'bottom'
+		/>
+		<drawstep	func = 'line'
+					fg_color = 'lightgrey'
+					stroke = '2'
+					fill = 'foreground'
+					width = '0'
+					height = 'auto'
+					xpos = 'right'
+					ypos = 'center'
+					padding = '0,0,17,1'
+		/>
+	</drawdata>
+	<drawdata id = 'dropdown_button_hover_right' cache = 'false' resolution = 'y<400'>
+		<text	font = 'text_button'
+				text_color = 'color_button'
+				vertical_align = 'center'
+				horizontal_align = 'center'
+		/>
+		<drawstep	func = 'bevelsq'
+					bevel = '2'
+					fill = 'none'
+		/>
+		<drawstep	func = 'triangle'
+					fg_color = 'green2'
+					fill = 'foreground'
+					width = '6'
+					height = '6'
+					xpos = 'right'
+					ypos = 'center'
+					padding = '0,0,0,0'
+					orientation = 'bottom'
+		/>
+	</drawdata>
+
+	<drawdata id = 'dropdown_button_disabled' cache = 'false' resolution = 'y>399'>
+		<text	font = 'text_button'
+				text_color = 'color_button_disabled'
+				vertical_align = 'center'
+				horizontal_align = 'center'
+		/>
+		<drawstep	func = 'bevelsq'
+					bevel = '2'
+					fill = 'none'
+		/>
+		<drawstep	func = 'triangle'
+					fg_color = 'lightgrey'
+					fill = 'foreground'
+					width = '8'
+					height = '6'
+					xpos = 'right'
+					ypos = 'center'
+					padding = '0,0,4,0'
+					orientation = 'bottom'
+		/>
+		<drawstep	func = 'line'
+					fg_color = 'lightgrey'
+					stroke = '2'
+					fill = 'foreground'
+					width = '0'
+					height = 'auto'
+					xpos = 'right'
+					ypos = 'center'
+					padding = '0,0,17,1'
+		/>
+	</drawdata>
+	<drawdata id = 'dropdown_button_disabled' cache = 'false' resolution = 'y<400'>
+		<text	font = 'text_button'
+				text_color = 'color_button_disabled'
+				vertical_align = 'center'
+				horizontal_align = 'center'
+		/>
+		<drawstep	func = 'bevelsq'
+					bevel = '2'
+					fill = 'none'
+		/>
+		<drawstep	func = 'triangle'
+					fg_color = 'lightgrey'
+					fill = 'foreground'
+					width = '6'
+					height = '6'
+					xpos = 'right'
+					ypos = 'center'
+					padding = '0,0,0,0'
+					orientation = 'bottom'
+		/>
+	</drawdata>
+
+	<drawdata id = 'dropdown_button_pressed_left' cache = 'false' resolution = 'y>399'>
+		<text	font = 'text_button'
+				text_color = 'color_alternative_inverted'
+				vertical_align = 'center'
+				horizontal_align = 'center'
+		/>
+		<drawstep	func = 'square'
+					fill = 'foreground'
+					fg_color = 'green'
+					clip = '0,0,-18,0'
+		/>
+		<drawstep	func = 'triangle'
+					fg_color = 'green'
+					fill = 'foreground'
+					width = '8'
+					height = '6'
+					xpos = 'right'
+					ypos = 'center'
+					padding = '0,0,4,0'
+					orientation = 'bottom'
+		/>
+	</drawdata>
+	<drawdata id = 'dropdown_button_pressed_left' cache = 'false' resolution = 'y<400'>
+		<text	font = 'text_button'
+				text_color = 'color_alternative_inverted'
+				vertical_align = 'center'
+				horizontal_align = 'center'
+		/>
+		<drawstep	func = 'square'
+					fill = 'foreground'
+					fg_color = 'green'
+					clip = '0,0,-7,0'
+		/>
+		<drawstep	func = 'triangle'
+					fg_color = 'green'
+					fill = 'foreground'
+					width = '6'
+					height = '6'
+					xpos = 'right'
+					ypos = 'center'
+					padding = '0,0,0,0'
+					orientation = 'bottom'
+		/>
+	</drawdata>
+
+	<drawdata id = 'dropdown_button_pressed_right' cache = 'false' resolution = 'y>399'>
+		<text	font = 'text_button'
+				text_color = 'color_button'
+				vertical_align = 'center'
+				horizontal_align = 'center'
+		/>
+		<drawstep	func = 'square'
+					fill = 'foreground'
+					fg_color = 'green'
+					clip = '-16,0,0,0'
+		/>
+		<drawstep	func = 'triangle'
+					fg_color = 'white'
+					fill = 'foreground'
+					width = '8'
+					height = '6'
+					xpos = 'right'
+					ypos = 'center'
+					padding = '0,0,4,0'
+					orientation = 'bottom'
+		/>
+	</drawdata>
+	<drawdata id = 'dropdown_button_pressed_right' cache = 'false' resolution = 'y<400'>
+		<text	font = 'text_button'
+				text_color = 'color_button'
+				vertical_align = 'center'
+				horizontal_align = 'center'
+		/>
+		<drawstep	func = 'square'
+					fill = 'foreground'
+					fg_color = 'green'
+					clip = '-7,0,0,0'
+		/>
+		<drawstep	func = 'triangle'
+					fg_color = 'white'
+					fill = 'foreground'
+					width = '6'
+					height = '6'
+					xpos = 'right'
+					ypos = 'center'
+					padding = '0,0,0,0'
+					orientation = 'bottom'
+		/>
+	</drawdata>
+
 	<drawdata id = 'checkbox_disabled' cache = 'false'>
 		<text	font = 'text_default'
 				text_color = 'color_normal_disabled'
diff --git a/gui/themes/scummclassic/classic_layout.stx b/gui/themes/scummclassic/classic_layout.stx
index 84bd056..66fab1d 100644
--- a/gui/themes/scummclassic/classic_layout.stx
+++ b/gui/themes/scummclassic/classic_layout.stx
@@ -51,6 +51,8 @@
 		<def var = 'Predictive.Button.Width' value = '60' />
 		<def var = 'Predictive.ShowDeletePic' value = '0'/>
 
+		<def var = 'DropdownButton.Width' value = '17'/>
+
 		<widget name = 'OptionsLabel'
 				size = '110, Globals.Line.Height'
 				textalign = 'right'
@@ -1807,4 +1809,6 @@
 			</layout>
 		</layout>
 	</dialog>
+	<dialog name = 'DropdownDialog' overlays = 'screen_center' shading = 'luminance'>
+	</dialog>
 </layout_info>
diff --git a/gui/themes/scummclassic/classic_layout_lowres.stx b/gui/themes/scummclassic/classic_layout_lowres.stx
index e1d90ff..e65e3d2 100644
--- a/gui/themes/scummclassic/classic_layout_lowres.stx
+++ b/gui/themes/scummclassic/classic_layout_lowres.stx
@@ -53,6 +53,8 @@
 		<def var = 'Predictive.Button.Height' value = '15' />
 		<def var = 'Predictive.ShowDeletePic' value = '0'/>
 
+		<def var = 'DropdownButton.Width' value = '7'/>
+
 		<widget name = 'Button'
 				size = '72, 16'
 		/>
@@ -1775,4 +1777,6 @@
 			</layout>
 		</layout>
 	</dialog>
+	<dialog name = 'DropdownDialog' overlays = 'screen_center' shading = 'luminance'>
+	</dialog>
 </layout_info>
diff --git a/gui/themes/scummmodern.zip b/gui/themes/scummmodern.zip
index 7506446..f7a6900 100644
Binary files a/gui/themes/scummmodern.zip and b/gui/themes/scummmodern.zip differ
diff --git a/gui/themes/scummmodern/scummmodern_gfx.stx b/gui/themes/scummmodern/scummmodern_gfx.stx
index 0e62f36..c449c1a 100644
--- a/gui/themes/scummmodern/scummmodern_gfx.stx
+++ b/gui/themes/scummmodern/scummmodern_gfx.stx
@@ -86,6 +86,9 @@
 		<color name = 'darkgray'
 				rgb = '176, 168, 144'
 		/>
+		<color name = 'darkgray2'
+				rgb = '192, 192, 192'
+		/>
 		<color name = 'lightgray'
 				rgb = '210, 200, 170'
 		/>
@@ -208,7 +211,7 @@
 		/>
 
 		<text_color	id = 'color_alternative_disabled'
-				color = '192, 192, 192'
+				color = 'darkgray2'
 		/>
 
 		<text_color	id = 'color_button'
@@ -216,7 +219,7 @@
 		/>
 
 		<text_color	id = 'color_button_disabled'
-				color = '192, 192, 192'
+				color = 'darkgray2'
 		/>
 	</fonts>
 
@@ -856,6 +859,361 @@
 		/>
 	</drawdata>
 
+	<!-- Dropdown button widget -->
+	<drawdata id = 'dropdown_button_idle' cache = 'false' resolution = 'y>399'>
+		<text	font = 'text_button'
+				text_color = 'color_button'
+				vertical_align = 'center'
+				horizontal_align = 'center'
+		/>
+		<drawstep	func = 'roundedsq'
+					radius = '5'
+					stroke = '1'
+					fill = 'gradient'
+					shadow = '0'
+					fg_color = 'darkredborder'
+					gradient_start = 'brightred'
+					gradient_end = 'darkred'
+					bevel = '1'
+					bevel_color = 'brightredborder'
+		/>
+		<drawstep	func = 'triangle'
+					fg_color = 'white'
+					fill = 'foreground'
+					width = '8'
+					height = '6'
+					xpos = 'right'
+					ypos = 'center'
+					padding = '0,0,2,0'
+					orientation = 'bottom'
+		/>
+	</drawdata>
+	<drawdata id = 'dropdown_button_idle' cache = 'false' resolution = 'y<400'>
+		<text	font = 'text_button'
+				text_color = 'color_button'
+				vertical_align = 'center'
+				horizontal_align = 'center'
+		/>
+		<drawstep	func = 'roundedsq'
+					radius = '5'
+					stroke = '1'
+					fill = 'gradient'
+					shadow = '0'
+					fg_color = 'darkredborder'
+					gradient_start = 'brightred'
+					gradient_end = 'darkred'
+					bevel = '1'
+					bevel_color = 'brightredborder'
+		/>
+		<drawstep	func = 'triangle'
+					fg_color = 'white'
+					fill = 'foreground'
+					width = '6'
+					height = '6'
+					xpos = 'right'
+					ypos = 'center'
+					padding = '0,0,0,0'
+					orientation = 'bottom'
+		/>
+	</drawdata>
+
+	<drawdata id = 'dropdown_button_hover_left' cache = 'false' resolution = 'y>399'>
+		<text	font = 'text_button'
+				text_color = 'color_button'
+				vertical_align = 'center'
+				horizontal_align = 'center'
+		/>
+		<drawstep	func = 'roundedsq'
+					radius = '5'
+					stroke = '1'
+					fill = 'gradient'
+					shadow = '0'
+					fg_color = 'darkredborder'
+					gradient_start = 'brightpink'
+					gradient_end = 'darkpink'
+					bevel = '1'
+					bevel_color = 'brightredborder'
+					clip = '0,0,-13,0'
+		/>
+		<drawstep	func = 'triangle'
+					fg_color = 'white'
+					fill = 'foreground'
+					width = '8'
+					height = '6'
+					xpos = 'right'
+					ypos = 'center'
+					padding = '0,0,2,0'
+					orientation = 'bottom'
+		/>
+	</drawdata>
+	<drawdata id = 'dropdown_button_hover_left' cache = 'false' resolution = 'y<400'>
+		<text	font = 'text_button'
+				text_color = 'color_button'
+				vertical_align = 'center'
+				horizontal_align = 'center'
+		/>
+		<drawstep	func = 'roundedsq'
+					radius = '5'
+					stroke = '1'
+					fill = 'gradient'
+					shadow = '0'
+					fg_color = 'darkredborder'
+					gradient_start = 'brightpink'
+					gradient_end = 'darkpink'
+					bevel = '1'
+					bevel_color = 'brightredborder'
+					clip = '0,0,-7,0'
+		/>
+		<drawstep	func = 'triangle'
+					fg_color = 'white'
+					fill = 'foreground'
+					width = '6'
+					height = '6'
+					xpos = 'right'
+					ypos = 'center'
+					padding = '0,0,0,0'
+					orientation = 'bottom'
+		/>
+	</drawdata>
+
+	<drawdata id = 'dropdown_button_hover_right' cache = 'false' resolution = 'y>399'>
+		<text	font = 'text_button'
+				text_color = 'color_button'
+				vertical_align = 'center'
+				horizontal_align = 'center'
+		/>
+		<drawstep	func = 'roundedsq'
+					radius = '5'
+					stroke = '1'
+					fill = 'gradient'
+					shadow = '0'
+					fg_color = 'darkredborder'
+					gradient_start = 'brightpink'
+					gradient_end = 'darkpink'
+					bevel = '1'
+					bevel_color = 'brightredborder'
+					clip = '-13,0,0,0'
+		/>
+		<drawstep	func = 'triangle'
+					fg_color = 'white'
+					fill = 'foreground'
+					width = '8'
+					height = '6'
+					xpos = 'right'
+					ypos = 'center'
+					padding = '0,0,2,0'
+					orientation = 'bottom'
+		/>
+	</drawdata>
+	<drawdata id = 'dropdown_button_hover_right' cache = 'false' resolution = 'y<400'>
+		<text	font = 'text_button'
+				text_color = 'color_button'
+				vertical_align = 'center'
+				horizontal_align = 'center'
+		/>
+		<drawstep	func = 'roundedsq'
+					radius = '5'
+					stroke = '1'
+					fill = 'gradient'
+					shadow = '0'
+					fg_color = 'darkredborder'
+					gradient_start = 'brightpink'
+					gradient_end = 'darkpink'
+					bevel = '1'
+					bevel_color = 'brightredborder'
+					clip = '-7,0,0,0'
+		/>
+		<drawstep	func = 'triangle'
+					fg_color = 'white'
+					fill = 'foreground'
+					width = '6'
+					height = '6'
+					xpos = 'right'
+					ypos = 'center'
+					padding = '0,0,0,0'
+					orientation = 'bottom'
+		/>
+	</drawdata>
+
+	<drawdata id = 'dropdown_button_disabled' cache = 'false' resolution = 'y>399'>
+		<text	font = 'text_button'
+				text_color = 'color_button_disabled'
+				vertical_align = 'center'
+				horizontal_align = 'center'
+		/>
+		<drawstep	func = 'roundedsq'
+					radius = '5'
+					stroke = '1'
+					fill = 'gradient'
+					shadow = '0'
+					fg_color = 'shadowcolor'
+					gradient_start = 'darkenedbrightred'
+					gradient_end = 'darkeneddarkred'
+					bevel = '1'
+					bevel_color = 'darkgray'
+		/>
+		<drawstep	func = 'triangle'
+					fg_color = 'darkgray2'
+					fill = 'foreground'
+					width = '8'
+					height = '6'
+					xpos = 'right'
+					ypos = 'center'
+					padding = '0,0,2,0'
+					orientation = 'bottom'
+		/>
+	</drawdata>
+	<drawdata id = 'dropdown_button_disabled' cache = 'false' resolution = 'y<400'>
+		<text	font = 'text_button'
+				text_color = 'color_button_disabled'
+				vertical_align = 'center'
+				horizontal_align = 'center'
+		/>
+		<drawstep	func = 'roundedsq'
+					radius = '5'
+					stroke = '1'
+					fill = 'gradient'
+					shadow = '0'
+					fg_color = 'shadowcolor'
+					gradient_start = 'darkenedbrightred'
+					gradient_end = 'darkeneddarkred'
+					bevel = '1'
+					bevel_color = 'darkgray'
+		/>
+		<drawstep	func = 'triangle'
+					fg_color = 'darkgray2'
+					fill = 'foreground'
+					width = '6'
+					height = '6'
+					xpos = 'right'
+					ypos = 'center'
+					padding = '0,0,0,0'
+					orientation = 'bottom'
+		/>
+	</drawdata>
+
+	<drawdata id = 'dropdown_button_pressed_left' cache = 'false' resolution = 'y>399'>
+		<text	font = 'text_button'
+				text_color = 'color_button'
+				vertical_align = 'center'
+				horizontal_align = 'center'
+		/>
+		<drawstep	func = 'roundedsq'
+					radius = '5'
+					stroke = '1'
+					fill = 'foreground'
+					shadow = '0'
+					factor = '0'
+					fg_color = '120, 40, 16'
+					gradient_start = '255, 0, 0'
+					gradient_end = '255, 0, 0'
+					bevel = '1'
+					bevel_color = 'black'
+					clip = '0,0,-13,0'
+		/>
+		<drawstep	func = 'triangle'
+					fg_color = 'white'
+					fill = 'foreground'
+					width = '8'
+					height = '6'
+					xpos = 'right'
+					ypos = 'center'
+					padding = '0,0,2,0'
+					orientation = 'bottom'
+		/>
+	</drawdata>
+	<drawdata id = 'dropdown_button_pressed_left' cache = 'false' resolution = 'y<400'>
+		<text	font = 'text_button'
+				text_color = 'color_button'
+				vertical_align = 'center'
+				horizontal_align = 'center'
+		/>
+		<drawstep	func = 'roundedsq'
+					radius = '5'
+					stroke = '1'
+					fill = 'foreground'
+					shadow = '0'
+					factor = '0'
+					fg_color = '120, 40, 16'
+					gradient_start = '255, 0, 0'
+					gradient_end = '255, 0, 0'
+					bevel = '1'
+					bevel_color = 'black'
+					clip = '0,0,-7,0'
+		/>
+		<drawstep	func = 'triangle'
+					fg_color = 'white'
+					fill = 'foreground'
+					width = '6'
+					height = '6'
+					xpos = 'right'
+					ypos = 'center'
+					padding = '0,0,0,0'
+					orientation = 'bottom'
+		/>
+	</drawdata>
+
+	<drawdata id = 'dropdown_button_pressed_right' cache = 'false' resolution = 'y>399'>
+		<text	font = 'text_button'
+				text_color = 'color_button'
+				vertical_align = 'center'
+				horizontal_align = 'center'
+		/>
+		<drawstep	func = 'roundedsq'
+					radius = '5'
+					stroke = '1'
+					fill = 'foreground'
+					shadow = '0'
+					factor = '0'
+					fg_color = '120, 40, 16'
+					gradient_start = '255, 0, 0'
+					gradient_end = '255, 0, 0'
+					bevel = '1'
+					bevel_color = 'black'
+					clip = '-13,0,0,0'
+		/>
+		<drawstep	func = 'triangle'
+					fg_color = 'white'
+					fill = 'foreground'
+					width = '8'
+					height = '6'
+					xpos = 'right'
+					ypos = 'center'
+					padding = '0,0,2,0'
+					orientation = 'bottom'
+		/>
+	</drawdata>
+	<drawdata id = 'dropdown_button_pressed_right' cache = 'false' resolution = 'y<400'>
+		<text	font = 'text_button'
+				text_color = 'color_button'
+				vertical_align = 'center'
+				horizontal_align = 'center'
+		/>
+		<drawstep	func = 'roundedsq'
+					radius = '5'
+					stroke = '1'
+					fill = 'foreground'
+					shadow = '0'
+					factor = '0'
+					fg_color = '120, 40, 16'
+					gradient_start = '255, 0, 0'
+					gradient_end = '255, 0, 0'
+					bevel = '1'
+					bevel_color = 'black'
+					clip = '-7,0,0,0'
+		/>
+		<drawstep	func = 'triangle'
+					fg_color = 'white'
+					fill = 'foreground'
+					width = '6'
+					height = '6'
+					xpos = 'right'
+					ypos = 'center'
+					padding = '0,0,0,0'
+					orientation = 'bottom'
+		/>
+	</drawdata>
+
 	<!-- Disabled checkbox -->
 	<drawdata id = 'checkbox_disabled' cache = 'false'>
 		<text	font = 'text_default'
diff --git a/gui/themes/scummmodern/scummmodern_layout.stx b/gui/themes/scummmodern/scummmodern_layout.stx
index a45dd3e..dd958ea 100644
--- a/gui/themes/scummmodern/scummmodern_layout.stx
+++ b/gui/themes/scummmodern/scummmodern_layout.stx
@@ -58,6 +58,8 @@
 		<def var = 'Predictive.Button.Width' value = '60' />
 		<def var = 'Predictive.ShowDeletePic' value = '1'/>
 
+		<def var = 'DropdownButton.Width' value = '13'/>
+
 		<widget name = 'OptionsLabel'
 				size = '115, Globals.Line.Height'
 				textalign = 'right'
@@ -75,6 +77,9 @@
 		<widget name = 'Button'
 				size = '108, 24'
 		/>
+		<widget name = 'LauncherButton'
+				size = '130, 24'
+		/>
 		<widget name = 'WideButton'
 				size = '216, 24'
 		/>
@@ -160,31 +165,31 @@
 				<widget name = 'GameList'/>
 				<layout type = 'vertical' padding = '10, 0, 0, 0'>
 					<widget name = 'StartButton'
-							type = 'Button'
+							type = 'LauncherButton'
 					/>
 					<widget name = 'LoadGameButton'
-							type = 'Button'
+							type = 'LauncherButton'
 					/>
 					<space size = '10' />
 					<widget name = 'AddGameButton'
-							type = 'Button'
+							type = 'LauncherButton'
 					/>
 					<widget name = 'EditGameButton'
-							type = 'Button'
+							type = 'LauncherButton'
 					/>
 					<widget name = 'RemoveGameButton'
-							type = 'Button'
+							type = 'LauncherButton'
 					/>
 					<space size = '10' />
 					<widget name = 'OptionsButton'
-							type = 'Button'
+							type = 'LauncherButton'
 					/>
 					<widget name = 'AboutButton'
-							type = 'Button'
+							type = 'LauncherButton'
 					/>
 					<space size = '10' />
 					<widget name = 'QuitButton'
-							type = 'Button'
+							type = 'LauncherButton'
 					/>
 				</layout>
 			</layout>
@@ -260,7 +265,7 @@
 			</layout>
 		</layout>
 	</dialog>
-	
+
 	<dialog name = 'GlobalOptions_Control' overlays = 'Dialog.GlobalOptions.TabWidget'>
 		<layout type = 'vertical' padding = '16, 16, 16, 16' spacing = '8'>
 			<widget name = 'grOnScreenCheckbox'
@@ -934,13 +939,12 @@
 		<layout type='vertical' padding='16,16,16,16' spacing='16'>
 			<widget name='TTSCheckbox'
 				type='Checkbox'
-				/>
+			/>
 			<widget name='TTSVoiceSelection'
 				type='PopUp'
-				/>
-			</layout>
-		</dialog>
-
+			/>
+		</layout>
+	</dialog>
 	<dialog name='KeysDialog' overlays='Dialog.GlobalOptions' shading='dim'>
 		<layout type='vertical' padding='8,8,8,8' center='true'>
 			<widget name='Action'
@@ -1821,5 +1825,6 @@
 			</layout>
 		</layout>
 	</dialog>
-
+	<dialog name = 'DropdownDialog' overlays = 'screen_center'>
+	</dialog>
 </layout_info>
diff --git a/gui/themes/scummmodern/scummmodern_layout_lowres.stx b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
index 20363bb..7324663 100644
--- a/gui/themes/scummmodern/scummmodern_layout_lowres.stx
+++ b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
@@ -43,6 +43,8 @@
 		<def var = 'Predictive.Button.Height' value = '15' />
 		<def var = 'Predictive.ShowDeletePic' value = '0'/>
 
+		<def var = 'DropdownButton.Width' value = '7'/>
+
 		<widget name = 'Button'
 				size = '72, 16'
 		/>
@@ -1793,4 +1795,6 @@
 			</layout>
 		</layout>
 	</dialog>
+	<dialog name = 'DropdownDialog' overlays = 'screen_center'>
+	</dialog>
 </layout_info>
diff --git a/gui/themes/scummremastered.zip b/gui/themes/scummremastered.zip
index b4f7c0e..b9b247c 100644
Binary files a/gui/themes/scummremastered.zip and b/gui/themes/scummremastered.zip differ
diff --git a/gui/themes/scummremastered/remastered_gfx.stx b/gui/themes/scummremastered/remastered_gfx.stx
index 76b722c..c18a8bf 100644
--- a/gui/themes/scummremastered/remastered_gfx.stx
+++ b/gui/themes/scummremastered/remastered_gfx.stx
@@ -36,13 +36,13 @@
 				rgb = '180, 39, 9'
 		/>
 
-    <color name = 'button_pressed'
+		<color name = 'button_pressed'
 				rgb = '190, 39, 9'
 		/>
 
-    <color name = 'button_disabled'
-        rgb = '96, 96, 96'
-    />
+		<color name = 'button_disabled'
+				rgb = '96, 96, 96'
+		/>
 
 		<!-- Dialog background -->
 		<color name = 'dialog_background'
@@ -57,7 +57,7 @@
 				rgb = '0, 204, 51'
 		/>
 
-    <color name = 'paleyellow'
+		<color name = 'paleyellow'
 				rgb = '247, 228, 166'
 		/>
 
@@ -73,6 +73,9 @@
 		<color name = 'darkgray'
 				rgb = '176, 168, 144'
 		/>
+		<color name = 'darkgray2'
+				rgb = '192, 192, 192'
+		/>
 		<color name = 'lightgray'
 				rgb = '210, 200, 170'
 		/>
@@ -81,8 +84,8 @@
 		/>
 
 
-    <!-- Older/Unused? -->
-    <color name = 'brightredborder'
+		<!-- Older/Unused? -->
+		<color name = 'brightredborder'
 				rgb = '238, 213, 207'
 		/>
 
@@ -128,7 +131,7 @@
 				scalable_file = 'FreeSansBold.ttf'
 		/>
 		<font	resolution = 'y<400'
-		   		id = 'text_default'
+				id = 'text_default'
 				file = 'clR6x12.bdf'
 				scalable_file = 'FreeSans.ttf'
 				point_size = '11'
@@ -138,7 +141,7 @@
 				scalable_file = 'FreeSansBold.ttf'
 		/>
 		<font	resolution = 'y<400'
-		   		id = 'text_button'
+				id = 'text_button'
 				file = 'clR6x12.bdf'
 				scalable_file = 'FreeSans.ttf'
 				point_size = '11'
@@ -148,7 +151,7 @@
 				scalable_file = 'FreeSans.ttf'
 		/>
 		<font	resolution = 'y<400'
-		   		id = 'text_normal'
+				id = 'text_normal'
 				file = 'clR6x12.bdf'
 				scalable_file = 'FreeSans.ttf'
 				point_size = '11'
@@ -209,7 +212,7 @@
 		/>
 
 		<text_color	id = 'color_alternative_disabled'
-				color = '192, 192, 192'
+				color = 'darkgray2'
 		/>
 
 		<text_color	id = 'color_button'
@@ -217,7 +220,7 @@
 		/>
 
 		<text_color	id = 'color_button_disabled'
-				color = '192, 192, 192'
+				color = 'darkgray2'
 		/>
 	</fonts>
 
@@ -287,7 +290,7 @@
 					stroke = '1'
 					radius = '10'
 					fill = 'gradient'
-          fg_color = 'blandyellow'
+					fg_color = 'blandyellow'
 					gradient_start = 'button_hover'
 					gradient_end = 'button_hover'
 		/>
@@ -298,7 +301,7 @@
 					stroke = '1'
 					radius = '10'
 					fill = 'gradient'
-          fg_color = 'blandyellow'
+					fg_color = 'blandyellow'
 					gradient_start = 'button_idle'
 					gradient_end = 'button_idle'
 		/>
@@ -807,7 +810,7 @@
 					fill = 'background'
 					shadow = '2'
 					fg_color = 'darkredborder'
-          bg_color = 'button_idle'
+					bg_color = 'button_idle'
 					bevel = '0'
 					bevel_color = 'brightredborder'
 		/>
@@ -826,7 +829,7 @@
 					fill = 'background'
 					shadow = '2'
 					fg_color = 'darkredborder'
-          bg_color = 'button_hover'
+					bg_color = 'button_hover'
 					bevel = '0'
 					bevel_color = 'brightredborder'
 		/>
@@ -852,6 +855,287 @@
 		/>
 	</drawdata>
 
+	<!-- Dropdown button widget -->
+	<drawdata id = 'dropdown_button_idle' cache = 'false' resolution = 'y>399'>
+		<text	font = 'text_button'
+				text_color = 'color_button'
+				vertical_align = 'center'
+				horizontal_align = 'center'
+		/>
+		<drawstep	func = 'roundedsq'
+					radius = '5'
+					fill = 'foreground'
+					shadow = '2'
+					fg_color = 'button_idle'
+		/>
+		<drawstep	func = 'triangle'
+					fg_color = 'white'
+					fill = 'foreground'
+					width = '8'
+					height = '6'
+					xpos = 'right'
+					ypos = 'center'
+					padding = '0,0,2,0'
+					orientation = 'bottom'
+		/>
+	</drawdata>
+	<drawdata id = 'dropdown_button_idle' cache = 'false' resolution = 'y<400'>
+		<text	font = 'text_button'
+				text_color = 'color_button'
+				vertical_align = 'center'
+				horizontal_align = 'center'
+		/>
+		<drawstep	func = 'roundedsq'
+					radius = '5'
+					fill = 'foreground'
+					shadow = '2'
+					fg_color = 'button_idle'
+		/>
+		<drawstep	func = 'triangle'
+					fg_color = 'white'
+					fill = 'foreground'
+					width = '6'
+					height = '6'
+					xpos = 'right'
+					ypos = 'center'
+					padding = '0,0,0,0'
+					orientation = 'bottom'
+		/>
+	</drawdata>
+
+	<drawdata id = 'dropdown_button_hover_left' cache = 'false' resolution = 'y>399'>
+		<text	font = 'text_button'
+				text_color = 'color_button'
+				vertical_align = 'center'
+				horizontal_align = 'center'
+		/>
+		<drawstep	func = 'roundedsq'
+					radius = '5'
+					fill = 'foreground'
+					fg_color = 'button_hover'
+					clip = '0,0,-13,0'
+		/>
+		<drawstep	func = 'triangle'
+					fg_color = 'white'
+					fill = 'foreground'
+					width = '8'
+					height = '6'
+					xpos = 'right'
+					ypos = 'center'
+					padding = '0,0,2,0'
+					orientation = 'bottom'
+		/>
+	</drawdata>
+	<drawdata id = 'dropdown_button_hover_left' cache = 'false' resolution = 'y<400'>
+		<text	font = 'text_button'
+				text_color = 'color_button'
+				vertical_align = 'center'
+				horizontal_align = 'center'
+		/>
+		<drawstep	func = 'roundedsq'
+					radius = '5'
+					fill = 'foreground'
+					fg_color = 'button_hover'
+					clip = '0,0,-7,0'
+		/>
+		<drawstep	func = 'triangle'
+					fg_color = 'white'
+					fill = 'foreground'
+					width = '6'
+					height = '6'
+					xpos = 'right'
+					ypos = 'center'
+					padding = '0,0,0,0'
+					orientation = 'bottom'
+		/>
+	</drawdata>
+
+	<drawdata id = 'dropdown_button_hover_right' cache = 'false' resolution = 'y>399'>
+		<text	font = 'text_button'
+				text_color = 'color_button'
+				vertical_align = 'center'
+				horizontal_align = 'center'
+		/>
+		<drawstep	func = 'roundedsq'
+					radius = '5'
+					fill = 'foreground'
+					fg_color = 'button_hover'
+					clip = '-13,0,0,0'
+		/>
+		<drawstep	func = 'triangle'
+					fg_color = 'white'
+					fill = 'foreground'
+					width = '8'
+					height = '6'
+					xpos = 'right'
+					ypos = 'center'
+					padding = '0,0,2,0'
+					orientation = 'bottom'
+		/>
+	</drawdata>
+	<drawdata id = 'dropdown_button_hover_right' cache = 'false' resolution = 'y<400'>
+		<text	font = 'text_button'
+				text_color = 'color_button'
+				vertical_align = 'center'
+				horizontal_align = 'center'
+		/>
+		<drawstep	func = 'roundedsq'
+					radius = '5'
+					fill = 'foreground'
+					fg_color = 'button_hover'
+					clip = '-7,0,0,0'
+		/>
+		<drawstep	func = 'triangle'
+					fg_color = 'white'
+					fill = 'foreground'
+					width = '6'
+					height = '6'
+					xpos = 'right'
+					ypos = 'center'
+					padding = '0,0,0,0'
+					orientation = 'bottom'
+		/>
+	</drawdata>
+
+	<drawdata id = 'dropdown_button_disabled' cache = 'false' resolution = 'y>399'>
+		<text	font = 'text_button'
+				text_color = 'color_button_disabled'
+				vertical_align = 'center'
+				horizontal_align = 'center'
+		/>
+		<drawstep	func = 'roundedsq'
+					radius = '5'
+					fill = 'foreground'
+					fg_color = 'button_disabled'
+		/>
+		<drawstep	func = 'triangle'
+					fg_color = 'darkgray2'
+					fill = 'foreground'
+					width = '8'
+					height = '6'
+					xpos = 'right'
+					ypos = 'center'
+					padding = '0,0,2,0'
+					orientation = 'bottom'
+		/>
+	</drawdata>
+	<drawdata id = 'dropdown_button_disabled' cache = 'false' resolution = 'y<400'>
+		<text	font = 'text_button'
+				text_color = 'color_button_disabled'
+				vertical_align = 'center'
+				horizontal_align = 'center'
+		/>
+		<drawstep	func = 'roundedsq'
+					radius = '5'
+					fill = 'foreground'
+					fg_color = 'button_disabled'
+		/>
+		<drawstep	func = 'triangle'
+					fg_color = 'darkgray2'
+					fill = 'foreground'
+					width = '6'
+					height = '6'
+					xpos = 'right'
+					ypos = 'center'
+					padding = '0,0,0,0'
+					orientation = 'bottom'
+		/>
+	</drawdata>
+
+	<drawdata id = 'dropdown_button_pressed_left' cache = 'false' resolution = 'y>399'>
+		<text	font = 'text_button'
+				text_color = 'color_button'
+				vertical_align = 'center'
+				horizontal_align = 'center'
+		/>
+		<drawstep	func = 'roundedsq'
+					radius = '5'
+					fill = 'foreground'
+					fg_color = 'button_pressed'
+					clip = '0,0,-13,0'
+		/>
+		<drawstep	func = 'triangle'
+					fg_color = 'white'
+					fill = 'foreground'
+					width = '8'
+					height = '6'
+					xpos = 'right'
+					ypos = 'center'
+					padding = '0,0,2,0'
+					orientation = 'bottom'
+		/>
+	</drawdata>
+	<drawdata id = 'dropdown_button_pressed_left' cache = 'false' resolution = 'y<400'>
+		<text	font = 'text_button'
+				text_color = 'color_button'
+				vertical_align = 'center'
+				horizontal_align = 'center'
+		/>
+		<drawstep	func = 'roundedsq'
+					radius = '5'
+					fill = 'foreground'
+					fg_color = 'button_pressed'
+					clip = '0,0,-7,0'
+		/>
+		<drawstep	func = 'triangle'
+					fg_color = 'white'
+					fill = 'foreground'
+					width = '6'
+					height = '6'
+					xpos = 'right'
+					ypos = 'center'
+					padding = '0,0,0,0'
+					orientation = 'bottom'
+		/>
+	</drawdata>
+
+	<drawdata id = 'dropdown_button_pressed_right' cache = 'false' resolution = 'y>399'>
+		<text	font = 'text_button'
+				text_color = 'color_button'
+				vertical_align = 'center'
+				horizontal_align = 'center'
+		/>
+		<drawstep	func = 'roundedsq'
+					radius = '5'
+					fill = 'foreground'
+					fg_color = 'button_pressed'
+					clip = '-13,0,0,0'
+		/>
+		<drawstep	func = 'triangle'
+					fg_color = 'white'
+					fill = 'foreground'
+					width = '8'
+					height = '6'
+					xpos = 'right'
+					ypos = 'center'
+					padding = '0,0,2,0'
+					orientation = 'bottom'
+		/>
+	</drawdata>
+	<drawdata id = 'dropdown_button_pressed_right' cache = 'false' resolution = 'y<400'>
+		<text	font = 'text_button'
+				text_color = 'color_button'
+				vertical_align = 'center'
+				horizontal_align = 'center'
+		/>
+		<drawstep	func = 'roundedsq'
+					radius = '5'
+					fill = 'foreground'
+					fg_color = 'button_pressed'
+					clip = '-7,0,0,0'
+		/>
+		<drawstep	func = 'triangle'
+					fg_color = 'white'
+					fill = 'foreground'
+					width = '6'
+					height = '6'
+					xpos = 'right'
+					ypos = 'center'
+					padding = '0,0,0,0'
+					orientation = 'bottom'
+		/>
+	</drawdata>
+
 	<!-- Disabled checkbox -->
 	<drawdata id = 'checkbox_disabled' cache = 'false'>
 		<text	font = 'text_default'
diff --git a/gui/themes/scummremastered/remastered_layout.stx b/gui/themes/scummremastered/remastered_layout.stx
index 092bbc0..3d5587e 100644
--- a/gui/themes/scummremastered/remastered_layout.stx
+++ b/gui/themes/scummremastered/remastered_layout.stx
@@ -58,6 +58,8 @@
 		<def var = 'Predictive.Button.Width' value = '60' />
 		<def var = 'Predictive.ShowDeletePic' value = '1'/>
 
+		<def var = 'DropdownButton.Width' value = '13'/>
+
 		<widget name = 'OptionsLabel'
 				size = '115, Globals.Line.Height'
 				textalign = 'right'
@@ -75,6 +77,9 @@
 		<widget name = 'Button'
 				size = '108, 24'
 		/>
+		<widget name = 'LauncherButton'
+				size = '130, 24'
+		/>
 		<widget name = 'WideButton'
 				size = '216, 24'
 		/>
@@ -160,31 +165,31 @@
 				<widget name = 'GameList'/>
 				<layout type = 'vertical' padding = '10, 0, 0, 0'>
 					<widget name = 'StartButton'
-							type = 'Button'
+							type = 'LauncherButton'
 					/>
 					<widget name = 'LoadGameButton'
-							type = 'Button'
+							type = 'LauncherButton'
 					/>
 					<space size = '10' />
 					<widget name = 'AddGameButton'
-							type = 'Button'
+							type = 'LauncherButton'
 					/>
 					<widget name = 'EditGameButton'
-							type = 'Button'
+							type = 'LauncherButton'
 					/>
 					<widget name = 'RemoveGameButton'
-							type = 'Button'
+							type = 'LauncherButton'
 					/>
 					<space size = '10' />
 					<widget name = 'OptionsButton'
-							type = 'Button'
+							type = 'LauncherButton'
 					/>
 					<widget name = 'AboutButton'
-							type = 'Button'
+							type = 'LauncherButton'
 					/>
 					<space size = '10' />
 					<widget name = 'QuitButton'
-							type = 'Button'
+							type = 'LauncherButton'
 					/>
 				</layout>
 			</layout>
@@ -1820,5 +1825,6 @@
 			</layout>
 		</layout>
 	</dialog>
-
+	<dialog name = 'DropdownDialog' overlays = 'screen_center'>
+	</dialog>
 </layout_info>
diff --git a/gui/themes/scummremastered/remastered_layout_lowres.stx b/gui/themes/scummremastered/remastered_layout_lowres.stx
index 3f8ec01..2a20442 100644
--- a/gui/themes/scummremastered/remastered_layout_lowres.stx
+++ b/gui/themes/scummremastered/remastered_layout_lowres.stx
@@ -43,6 +43,8 @@
 		<def var = 'Predictive.Button.Height' value = '15' />
 		<def var = 'Predictive.ShowDeletePic' value = '0'/>
 
+		<def var = 'DropdownButton.Width' value = '7'/>
+
 		<widget name = 'Button'
 				size = '72, 16'
 		/>
@@ -1793,4 +1795,6 @@
 			</layout>
 		</layout>
 	</dialog>
+	<dialog name = 'DropdownDialog' overlays = 'screen_center'>
+	</dialog>
 </layout_info>
diff --git a/gui/widget.cpp b/gui/widget.cpp
index 750fa4e..bbb8b06 100644
--- a/gui/widget.cpp
+++ b/gui/widget.cpp
@@ -32,6 +32,7 @@
 #include "gui/ThemeEval.h"
 
 #include "gui/dialog.h"
+#include "gui/widgets/popup.h"
 
 namespace GUI {
 
@@ -322,7 +323,7 @@ void StaticTextWidget::drawWidget() {
 
 ButtonWidget::ButtonWidget(GuiObject *boss, int x, int y, int w, int h, const Common::String &label, const char *tooltip, uint32 cmd, uint8 hotkey)
 	: StaticTextWidget(boss, x, y, w, h, cleanupHotkey(label), Graphics::kTextAlignCenter, tooltip), CommandSender(boss),
-	  _cmd(cmd), _hotkey(hotkey), _lastTime(0), _duringPress(false) {
+	  _cmd(cmd), _hotkey(hotkey), _duringPress(false) {
 
 	if (hotkey == 0)
 		_hotkey = parseHotkey(label);
@@ -333,7 +334,7 @@ ButtonWidget::ButtonWidget(GuiObject *boss, int x, int y, int w, int h, const Co
 
 ButtonWidget::ButtonWidget(GuiObject *boss, const Common::String &name, const Common::String &label, const char *tooltip, uint32 cmd, uint8 hotkey)
 	: StaticTextWidget(boss, name, cleanupHotkey(label), tooltip), CommandSender(boss),
-	  _cmd(cmd), _hotkey(hotkey), _lastTime(0), _duringPress(false) {
+	  _cmd(cmd), _hotkey(hotkey), _duringPress(false) {
 	if (hotkey == 0)
 		_hotkey = parseHotkey(label);
 	setFlags(WIDGET_ENABLED/* | WIDGET_BORDER*/ | WIDGET_CLEARBG);
@@ -400,6 +401,104 @@ void ButtonWidget::setUnpressedState() {
 
 #pragma mark -
 
+DropdownButtonWidget::DropdownButtonWidget(GuiObject *boss, int x, int y, int w, int h, const Common::String &label, const char *tooltip, uint32 cmd, uint8 hotkey) :
+		ButtonWidget(boss, x, y, w, h, label, tooltip, cmd, hotkey) {
+	setFlags(getFlags() | WIDGET_TRACK_MOUSE);
+
+	reset();
+}
+
+DropdownButtonWidget::DropdownButtonWidget(GuiObject *boss, const Common::String &name, const Common::String &label, const char *tooltip, uint32 cmd, uint8 hotkey) :
+		ButtonWidget(boss, name, label, tooltip, cmd, hotkey) {
+	setFlags(getFlags() | WIDGET_TRACK_MOUSE);
+
+	reset();
+}
+
+void DropdownButtonWidget::reset() {
+	_inDropdown = false;
+	_inButton   = false;
+	_dropdownWidth = g_gui.xmlEval()->getVar("Globals.DropdownButton.Width", 13);
+}
+
+bool DropdownButtonWidget::isInDropDown(int x, int y) const {
+	Common::Rect dropdownRect(_w - _dropdownWidth, 0, _w, _h);
+	return dropdownRect.contains(x, y);
+}
+
+void DropdownButtonWidget::handleMouseMoved(int x, int y, int button) {
+	if (_entries.empty()) {
+		return;
+	}
+
+	// Detect which part of the button the cursor is over
+	bool inDropdown = isInDropDown(x, y);
+	bool inButton   = Common::Rect(_w, _h).contains(x, y) && !inDropdown;
+
+	if (inDropdown != _inDropdown) {
+		_inDropdown = inDropdown;
+		markAsDirty();
+	}
+
+	if (inButton != _inButton) {
+		_inButton = inButton;
+		markAsDirty();
+	}
+}
+
+void DropdownButtonWidget::handleMouseUp(int x, int y, int button, int clickCount) {
+	if (isEnabled() && !_entries.empty() && _duringPress && isInDropDown(x, y)) {
+
+		PopUpDialog popupDialog(this, "DropdownDialog", x + getAbsX(), y + getAbsY());
+		popupDialog.setPosition(getAbsX(), getAbsY() + _h);
+		popupDialog.setLineHeight(_h);
+		popupDialog.setPadding(_dropdownWidth, _dropdownWidth);
+
+		for (uint i = 0; i < _entries.size(); i++) {
+			popupDialog.appendEntry(_entries[i].label);
+		}
+
+		int newSel = popupDialog.runModal();
+		if (newSel != -1) {
+			sendCommand(_entries[newSel].cmd, 0);
+		}
+
+		setUnpressedState();
+		_duringPress = false;
+	} else {
+		ButtonWidget::handleMouseUp(x, y, button, clickCount);
+	}
+}
+
+void DropdownButtonWidget::reflowLayout() {
+	ButtonWidget::reflowLayout();
+
+	reset();
+}
+
+void DropdownButtonWidget::appendEntry(const Common::String &label, uint32 cmd) {
+	Entry e;
+	e.label = label;
+	e.cmd = cmd;
+	_entries.push_back(e);
+}
+
+void DropdownButtonWidget::clearEntries() {
+	_entries.clear();
+}
+
+void DropdownButtonWidget::drawWidget() {
+	if (_entries.empty()) {
+		// Degrade to a regular button
+		g_gui.theme()->drawButton(Common::Rect(_x, _y, _x + _w, _y + _h), _label, _state);
+	} else {
+		g_gui.theme()->drawDropDownButton(Common::Rect(_x, _y, _x + _w, _y + _h), _dropdownWidth, _label,
+		                                  _state, _inButton, _inDropdown);
+	}
+}
+
+#pragma mark -
+
 PicButtonWidget::PicButtonWidget(GuiObject *boss, int x, int y, int w, int h, const char *tooltip, uint32 cmd, uint8 hotkey)
 	: ButtonWidget(boss, x, y, w, h, "", tooltip, cmd, hotkey),
 	  _alpha(255), _transparency(false), _showButton(true) {
diff --git a/gui/widget.h b/gui/widget.h
index 06df496..2339012 100644
--- a/gui/widget.h
+++ b/gui/widget.h
@@ -234,8 +234,39 @@ public:
 protected:
 	void drawWidget();
 	bool _duringPress;
-private:
-	uint32 _lastTime;
+};
+
+/* DropdownButtonWidget */
+class DropdownButtonWidget : public ButtonWidget {
+public:
+	DropdownButtonWidget(GuiObject *boss, int x, int y, int w, int h, const Common::String &label, const char *tooltip = nullptr, uint32 cmd = 0, uint8 hotkey = 0);
+	DropdownButtonWidget(GuiObject *boss, const Common::String &name, const Common::String &label, const char *tooltip = nullptr, uint32 cmd = 0, uint8 hotkey = 0);
+
+	void handleMouseMoved(int x, int y, int button) override;
+	void handleMouseUp(int x, int y, int button, int clickCount) override;
+	void reflowLayout() override;
+
+	void appendEntry(const Common::String &label, uint32 cmd);
+	void clearEntries();
+
+protected:
+	struct Entry {
+		Common::String label;
+		uint32 cmd;
+	};
+	typedef Common::Array<Entry> EntryList;
+
+	// Widget API
+	void drawWidget() override;
+
+	void reset();
+	bool isInDropDown(int x, int y) const;
+
+	EntryList _entries;
+
+	uint32 _dropdownWidth;
+	bool _inDropdown;
+	bool _inButton;
 };
 
 /* PicButtonWidget */
diff --git a/gui/widgets/popup.cpp b/gui/widgets/popup.cpp
index 970e35a..351d0fe 100644
--- a/gui/widgets/popup.cpp
+++ b/gui/widgets/popup.cpp
@@ -21,7 +21,6 @@
  */
 
 #include "common/system.h"
-#include "gui/dialog.h"
 #include "gui/gui-manager.h"
 #include "gui/widgets/popup.h"
 
@@ -33,62 +32,35 @@ namespace GUI {
 // PopUpDialog
 //
 
-class PopUpDialog : public Dialog {
-protected:
-	PopUpWidget	*_popUpBoss;
-	int			_clickX, _clickY;
-	int			_selection;
-	uint32		_openTime;
-	bool		_twoColumns;
-	int			_entriesPerColumn;
-
-	int			_leftPadding;
-	int			_rightPadding;
-
-	int			_lastRead;
-
-public:
-	PopUpDialog(PopUpWidget *boss, int clickX, int clickY);
-
-	void drawDialog(DrawLayer layerToDraw) override;
-
-	void handleMouseUp(int x, int y, int button, int clickCount) override;
-	void handleMouseWheel(int x, int y, int direction) override;	// Scroll through entries with scroll wheel
-	void handleMouseMoved(int x, int y, int button) override;	// Redraw selections depending on mouse position
-	void handleMouseLeft(int button) override;
-	void handleKeyDown(Common::KeyState state) override;	// Scroll through entries with arrow keys etc.
-
-protected:
-	void drawMenuEntry(int entry, bool hilite);
-
-	int findItem(int x, int y) const;
-	void setSelection(int item);
-	bool isMouseDown();
-
-	void moveUp();
-	void moveDown();
-	void read(Common::String);
-};
-
-PopUpDialog::PopUpDialog(PopUpWidget *boss, int clickX, int clickY)
-	: Dialog(0, 0, 16, 16),
-	_popUpBoss(boss) {
+PopUpDialog::PopUpDialog(Widget *boss, const Common::String &name, int clickX, int clickY):
+		Dialog(name),
+		_boss(boss),
+		// Remember original mouse position
+		_clickX(clickX),
+		_clickY(clickY),
+		_selection(-1),
+		_initialSelection(-1),
+		_openTime(0),
+		_twoColumns(false),
+		_entriesPerColumn(1),
+		_leftPadding(0),
+		_rightPadding(0),
+		_lineHeight(kLineHeight),
+		_lastRead(-1) {
 	_backgroundType = ThemeEngine::kDialogBackgroundNone;
+	_w = _boss->getWidth();
+}
 
-	_openTime = 0;
-	_entriesPerColumn = 1;
+void PopUpDialog::open() {
+	// Time the popup was opened
+	_openTime = g_system->getMillis();
 
-	// Copy the selection index
-	_selection = _popUpBoss->_selectedItem;
+	_initialSelection = _selection;
 
 	// Calculate real popup dimensions
-	_x = _popUpBoss->getAbsX();
-	_y = _popUpBoss->getAbsY() - _popUpBoss->_selectedItem * kLineHeight;
-	_h = _popUpBoss->_entries.size() * kLineHeight + 2;
-	_w = _popUpBoss->_w - kLineHeight + 2;
+	_h = _entries.size() * _lineHeight + 2;
 
-	_leftPadding = _popUpBoss->_leftPadding;
-	_rightPadding = _popUpBoss->_rightPadding;
+	_entriesPerColumn = 1;
 
 	// Perform clipping / switch to scrolling mode if we don't fit on the screen
 	// FIXME - OSystem should send out notification messages when the screen
@@ -103,16 +75,16 @@ PopUpDialog::PopUpDialog(PopUpWidget *boss, int clickX, int clickY)
 		const int screenW = g_system->getOverlayWidth();
 
 		_twoColumns = true;
-		_entriesPerColumn = _popUpBoss->_entries.size() / 2;
+		_entriesPerColumn = _entries.size() / 2;
 
-		if (_popUpBoss->_entries.size() & 1)
+		if (_entries.size() & 1)
 			_entriesPerColumn++;
 
-		_h = _entriesPerColumn * kLineHeight + 2;
+		_h = _entriesPerColumn * _lineHeight + 2;
 		_w = 0;
 
-		for (uint i = 0; i < _popUpBoss->_entries.size(); i++) {
-			int width = g_gui.getStringWidth(_popUpBoss->_entries[i].name);
+		for (uint i = 0; i < _entries.size(); i++) {
+			int width = g_gui.getStringWidth(_entries[i]);
 
 			if (width > _w)
 				_w = width;
@@ -123,9 +95,9 @@ PopUpDialog::PopUpDialog(PopUpWidget *boss, int clickX, int clickY)
 		if (!(_w & 1))
 			_w++;
 
-		if (_popUpBoss->_selectedItem >= _entriesPerColumn) {
+		if (_selection >= _entriesPerColumn) {
 			_x -= _w / 2;
-			_y = _popUpBoss->getAbsY() - (_popUpBoss->_selectedItem - _entriesPerColumn) * kLineHeight;
+			_y = _boss->getAbsY() - (_selection - _entriesPerColumn) * _lineHeight;
 		}
 
 		if (_w >= screenW)
@@ -146,11 +118,12 @@ PopUpDialog::PopUpDialog(PopUpWidget *boss, int clickX, int clickY)
 
 	// TODO - implement scrolling if we had to move the menu, or if there are too many entries
 
-	// Remember original mouse position
-	_clickX = clickX - _x;
-	_clickY = clickY - _y;
-
 	_lastRead = -1;
+
+	Dialog::open();
+}
+
+void PopUpDialog::reflowLayout() {
 }
 
 void PopUpDialog::drawDialog(DrawLayer layerToDraw) {
@@ -163,7 +136,7 @@ void PopUpDialog::drawDialog(DrawLayer layerToDraw) {
 		g_gui.vLine(_x + _w / 2, _y, _y + _h - 2, g_gui._color);*/
 
 	// Draw the entries
-	int count = _popUpBoss->_entries.size();
+	int count = _entries.size();
 	for (int i = 0; i < count; i++) {
 		drawMenuEntry(i, i == _selection);
 	}
@@ -172,17 +145,15 @@ void PopUpDialog::drawDialog(DrawLayer layerToDraw) {
 	/*if (_twoColumns && (count & 1)) {
 		g_gui.fillRect(_x + 1 + _w / 2, _y + 1 + kLineHeight * (_entriesPerColumn - 1), _w / 2 - 1, kLineHeight, g_gui._bgcolor);
 	}*/
-
-	if (_openTime == 0) {
-		// Time the popup was opened
-		_openTime = g_system->getMillis();
-	}
 }
 
 void PopUpDialog::handleMouseUp(int x, int y, int button, int clickCount) {
+	int absX = x + getAbsX();
+	int absY = y + getAbsY();
+
 	// Mouse was released. If it wasn't moved much since the original mouse down,
 	// let the popup stay open. If it did move, assume the user made his selection.
-	int dist = (_clickX - x) * (_clickX - x) + (_clickY - y) * (_clickY - y);
+	int dist = (_clickX - absX) * (_clickX - absX) + (_clickY - absY) * (_clickY - absY);
 	if (dist > 3 * 3 || g_system->getMillis() - _openTime > 300) {
 		setResult(_selection);
 		close();
@@ -203,18 +174,18 @@ void PopUpDialog::handleMouseMoved(int x, int y, int button) {
 	// Compute over which item the mouse is...
 	int item = findItem(x, y);
 
-	if (item >= 0 && _popUpBoss->_entries[item].name.size() == 0)
+	if (item >= 0 && _entries[item].size() == 0)
 		item = -1;
 
 	if (item == -1 && !isMouseDown()) {
-		setSelection(_popUpBoss->_selectedItem);
+		setSelection(_initialSelection);
 		return;
 	}
 
 	// ...and update the selection accordingly
 	setSelection(item);
-	if (_lastRead != item && _popUpBoss->_entries.size() > 0 && item != -1) {
-		read(_popUpBoss->_entries[item].name);
+	if (_lastRead != item && _entries.size() > 0 && item != -1) {
+		read(_entries[item]);
 		_lastRead = item;
 	}
 }
@@ -261,7 +232,7 @@ void PopUpDialog::handleKeyDown(Common::KeyState state) {
 			break;
 		// fall through
 	case Common::KEYCODE_END:
-		setSelection(_popUpBoss->_entries.size()-1);
+		setSelection(_entries.size()-1);
 		break;
 
 	case Common::KEYCODE_KP2:
@@ -293,19 +264,45 @@ void PopUpDialog::handleKeyDown(Common::KeyState state) {
 	}
 }
 
+void PopUpDialog::setPosition(int x, int y) {
+	_x = x;
+	_y = y;
+}
+
+void PopUpDialog::setPadding(int left, int right) {
+	_leftPadding  = left;
+	_rightPadding = right;
+}
+
+void PopUpDialog::setLineHeight(int lineHeight) {
+	_lineHeight = lineHeight;
+}
+
+void PopUpDialog::setWidth(uint16 width) {
+	_w = width;
+}
+
+void PopUpDialog::appendEntry(const Common::String &entry) {
+	_entries.push_back(entry);
+}
+
+void PopUpDialog::clearEntries() {
+	_entries.clear();
+}
+
 int PopUpDialog::findItem(int x, int y) const {
 	if (x >= 0 && x < _w && y >= 0 && y < _h) {
 		if (_twoColumns) {
-			uint entry = (y - 2) / kLineHeight;
+			uint entry = (y - 2) / _lineHeight;
 			if (x > _w / 2) {
 				entry += _entriesPerColumn;
 
-				if (entry >= _popUpBoss->_entries.size())
+				if (entry >= _entries.size())
 					return -1;
 			}
 			return entry;
 		}
-		return (y - 2) / kLineHeight;
+		return (y - 2) / _lineHeight;
 	}
 	return -1;
 }
@@ -335,19 +332,19 @@ bool PopUpDialog::isMouseDown() {
 
 void PopUpDialog::moveUp() {
 	if (_selection < 0) {
-		setSelection(_popUpBoss->_entries.size() - 1);
+		setSelection(_entries.size() - 1);
 	} else if (_selection > 0) {
 		int item = _selection;
 		do {
 			item--;
-		} while (item >= 0 && _popUpBoss->_entries[item].name.size() == 0);
+		} while (item >= 0 && _entries[item].size() == 0);
 		if (item >= 0)
 			setSelection(item);
 	}
 }
 
 void PopUpDialog::moveDown() {
-	int lastItem = _popUpBoss->_entries.size() - 1;
+	int lastItem = _entries.size() - 1;
 
 	if (_selection < 0) {
 		setSelection(0);
@@ -355,7 +352,7 @@ void PopUpDialog::moveDown() {
 		int item = _selection;
 		do {
 			item++;
-		} while (item <= lastItem && _popUpBoss->_entries[item].name.size() == 0);
+		} while (item <= lastItem && _entries[item].size() == 0);
 		if (item <= lastItem)
 			setSelection(item);
 	}
@@ -367,34 +364,34 @@ void PopUpDialog::drawMenuEntry(int entry, bool hilite) {
 	int x, y, w;
 
 	if (_twoColumns) {
-		int n = _popUpBoss->_entries.size() / 2;
+		int n = _entries.size() / 2;
 
-		if (_popUpBoss->_entries.size() & 1)
+		if (_entries.size() & 1)
 			n++;
 
 		if (entry >= n) {
 			x = _x + 1 + _w / 2;
-			y = _y + 1 + kLineHeight * (entry - n);
+			y = _y + 1 + _lineHeight * (entry - n);
 		} else {
 			x = _x + 1;
-			y = _y + 1 + kLineHeight * entry;
+			y = _y + 1 + _lineHeight * entry;
 		}
 
 		w = _w / 2 - 1;
 	} else {
 		x = _x + 1;
-		y = _y + 1 + kLineHeight * entry;
+		y = _y + 1 + _lineHeight * entry;
 		w = _w - 2;
 	}
 
-	Common::String &name(_popUpBoss->_entries[entry].name);
+	Common::String &name(_entries[entry]);
 
 	if (name.size() == 0) {
 		// Draw a separator
-		g_gui.theme()->drawLineSeparator(Common::Rect(x, y, x + w, y + kLineHeight));
+		g_gui.theme()->drawLineSeparator(Common::Rect(x, y, x + w, y + _lineHeight));
 	} else {
 		g_gui.theme()->drawText(
-			Common::Rect(x + 1, y + 2, x + w, y + 2 + kLineHeight),
+			Common::Rect(x + 1, y + 2, x + w, y + 2 + _lineHeight),
 			name, hilite ? ThemeEngine::kStateHighlight : ThemeEngine::kStateEnabled,
 			Graphics::kTextAlignLeft, ThemeEngine::kTextInversionNone, _leftPadding
 		);
@@ -429,7 +426,17 @@ PopUpWidget::PopUpWidget(GuiObject *boss, int x, int y, int w, int h, const char
 
 void PopUpWidget::handleMouseDown(int x, int y, int button, int clickCount) {
 	if (isEnabled()) {
-		PopUpDialog popupDialog(this, x + getAbsX(), y + getAbsY());
+		PopUpDialog popupDialog(this, "", x + getAbsX(), y + getAbsY());
+		popupDialog.setPosition(getAbsX(), getAbsY() - _selectedItem * kLineHeight);
+		popupDialog.setPadding(_leftPadding, _rightPadding);
+		popupDialog.setWidth(getWidth() - kLineHeight + 2);
+
+
+		for (uint i = 0; i < _entries.size(); i++) {
+			popupDialog.appendEntry(_entries[i].name);
+		}
+		popupDialog.setSelection(_selectedItem);
+
 		int newSel = popupDialog.runModal();
 		if (newSel != -1 && _selectedItem != newSel) {
 			_selectedItem = newSel;
diff --git a/gui/widgets/popup.h b/gui/widgets/popup.h
index 3ccf878..d7c218f 100644
--- a/gui/widgets/popup.h
+++ b/gui/widgets/popup.h
@@ -23,6 +23,7 @@
 #ifndef GUI_WIDGETS_POPUP_H
 #define GUI_WIDGETS_POPUP_H
 
+#include "gui/dialog.h"
 #include "gui/widget.h"
 #include "common/str.h"
 #include "common/array.h"
@@ -41,7 +42,6 @@ enum {
  * is broadcast, with data being equal to the tag value of the selected entry.
  */
 class PopUpWidget : public Widget, public CommandSender {
-	friend class PopUpDialog;
 	typedef Common::String String;
 
 	struct Entry {
@@ -85,6 +85,63 @@ protected:
 	void drawWidget();
 };
 
+/**
+ * A small dialog showing a list of items and allowing the user to chose one of them
+ *
+ * Used by PopUpWidget and DropdownButtonWidget.
+ */
+class PopUpDialog : public Dialog {
+protected:
+	Widget		*_boss;
+	int			_clickX, _clickY;
+	int			_selection;
+	int			_initialSelection;
+	uint32		_openTime;
+	bool		_twoColumns;
+	int			_entriesPerColumn;
+
+	int			_leftPadding;
+	int			_rightPadding;
+	int			_lineHeight;
+
+	int			_lastRead;
+
+	typedef Common::Array<Common::String> EntryList;
+	EntryList		_entries;
+
+public:
+	PopUpDialog(Widget *boss, const Common::String &name, int clickX, int clickY);
+
+	void open() override;
+	void reflowLayout() override;
+	void drawDialog(DrawLayer layerToDraw) override;
+
+	void handleMouseUp(int x, int y, int button, int clickCount) override;
+	void handleMouseWheel(int x, int y, int direction) override;	// Scroll through entries with scroll wheel
+	void handleMouseMoved(int x, int y, int button) override;	// Redraw selections depending on mouse position
+	void handleMouseLeft(int button) override;
+	void handleKeyDown(Common::KeyState state) override;	// Scroll through entries with arrow keys etc.
+
+	void setPosition(int x, int y);
+	void setPadding(int left, int right);
+	void setLineHeight(int lineHeight);
+	void setWidth(uint16 width);
+
+	void appendEntry(const Common::String &entry);
+	void clearEntries();
+	void setSelection(int item);
+
+protected:
+	void drawMenuEntry(int entry, bool hilite);
+
+	int findItem(int x, int y) const;
+	bool isMouseDown();
+
+	void moveUp();
+	void moveDown();
+	void read(Common::String);
+};
+
 } // End of namespace GUI
 
 #endif


Commit: 41f2ef9745307ddbaf8b7746e0179d1448bb200f
    https://github.com/scummvm/scummvm/commit/41f2ef9745307ddbaf8b7746e0179d1448bb200f
Author: Bastien Bouclet (bastien.bouclet at gmail.com)
Date: 2019-11-24T14:06:25+01:00

Commit Message:
GUI: Use the item at the click position in PopUpDialog

Fixes the having the drag in the dialog to change the selection on
platforms with a touch screen.

Changed paths:
    gui/widgets/popup.cpp


diff --git a/gui/widgets/popup.cpp b/gui/widgets/popup.cpp
index 351d0fe..ebb69f1 100644
--- a/gui/widgets/popup.cpp
+++ b/gui/widgets/popup.cpp
@@ -155,7 +155,8 @@ void PopUpDialog::handleMouseUp(int x, int y, int button, int clickCount) {
 	// let the popup stay open. If it did move, assume the user made his selection.
 	int dist = (_clickX - absX) * (_clickX - absX) + (_clickY - absY) * (_clickY - absY);
 	if (dist > 3 * 3 || g_system->getMillis() - _openTime > 300) {
-		setResult(_selection);
+		int item = findItem(x, y);
+		setResult(item);
 		close();
 	}
 	_clickX = -1;




More information about the Scummvm-git-logs mailing list