[Scummvm-git-logs] scummvm master -> 4232178ab2286ea27c45c74d0ce1b902445aba13

sev- noreply at scummvm.org
Sat Nov 18 17:48:47 UTC 2023


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

Summary:
ec1ab82283 SCUMM: Initial work on a common MacGui class that handles Mac menus etc.
1b3e863801 GRAPHICS: MACGUI: Consider menu item font when calculating menu width
3a7593a8c7 SCUMM: Mac GUI restructuring
e2ca711d5b SCUMM: Finish migrating Mac banner drawing to Mac GUI
11b2197717 SCUMM: Add callback for Mac GUI menus
63f151dccf SCUMM: Fix verb keys in Indy3 Mac GUI
077b331a8a SCUMM: Fix crash in Macintosh black-and-white mode
0e76018a10 GRAPHICS: MACGUI: Handle menu shortcuts even when menus are hidden
89bd9e7a22 SCUMM: More Mac GUI cleanups and experimenting
d63ad4c2e3 GRAPHICS: MACGUI: Don't filter keypresses in processMenuShortCut()
0022f3fc93 SCUMM: Some more work on Mac GUI windows
49ccadc6bd SCUMM: Add minimal PICT v1 decoder to Mac GUI
b79916c7b9 SCUMM: Clean up Mac GUI simple window drawing
08ad42c917 SCUMM: Add delay() function for Mac GUI
50a4ebd27b SCUMM: Draw Indy 3 Mac About background
8c9a34fcc6 SCUMM: Cleanup and animation mock-up for Mac Indy 3 GUI
a61b982691 SCUMM: Mac GUI cleanups
1a1d3c1a29 SCUMM: Mock up first text screen of Mac Indy 3 About dialog
41ddad4bc3 SCUMM: More Indy 3 Mac About dialog mock-ups
e7e4f6fcc2 SCUMM: Began rewriting the animation loop for Indy 3 Mac About dialog
4a5b7a471c SCUMM: Indy 3 Mac About cleanups
c685c32d76 SCUMM: Add three more Indy 3 Mac About text pages
bf6ca7ea6f SCUMM: Add remaining Mac Indy 3 GUI text pages and remove old code
2798b124d1 SCUMM: Mac Indy 3 About animation fixes
16ac86cdeb GRAPHICS: Calculate bitmapOffset correctly. Again.
67d79cd5d0 SCUMM: Pause sounds during Mac dialogs and try to fix cursor regression
bb6860de1b SCUMM: Early Mac Loom About experimentation
ac57efc01b SCUMM: More work on animating the Mac Loom About dialog
102080ecd8 SCUMM: Fix Mac Loom cursor regression. I hope.
5659f1b736 SCUMM: Clean up Mac Loom About dialog animation a bit
90cea28887 SCUMM: Better fast forward for Mac Loom About dialog animation
6e6dcc8796 SCUMM: Mac Loom About fixes
51267fe4f6 SCUMM: Add remaning Mac Loom About screens
53d149b45b GRAPHICS: Make Mac font rendering more like the original
dddda5c497 SCUMM: More Mac GUI fixes
1fb0070ef8 SCUMM: Updated timing for Indy 3 Mac About dialog
5f51200454 SCUMM: Move Indy 3 Mac text box drawing into MacIndy3Gui
38942ff719 SCUMM: Move Mac Loom's practice box drawing into MacLoomGui
5ad9ac4c12 SCUMM: Add dialog drawing to Mac Gui
05a832be5c SCUMM: Draw "Okay" as default button in Mac GUI where appropriate
bb879fa2f7 SCUMM: Began work on making the Mac dialog buttons etc. into widgets
f45ba49b56 SCUMM: Give the Mac dialog an event loop, and start acting on events
9a6499b21d SCUMM: Mac GUI texts are now widgets, too
b1af72c697 SCUMM: Use correct text for Mac GUI quit dialog.
abc2c88c13 SCUMM: Add word-wrapping to Mac GUI
6fa8a9245d SCUMM: Make pictures be widgets in the Mac GUI
961cd2cffb SCUMM: Partially show in the Indy 3 Mac Open/Save dialogs
f83b5b80a5 SCUMM: Some cleanups and fixes to the Mac GUI
e90e08d9c6 SCUMM: Add most of the interactions for the Mac GUI widgets
71e1ffcb33 SCUMM: Implement disabled text, and fleshed out options dialogs
ca0f75065b SCUMM: Renamed SimpleWindow to MacDialogWindow
8f14ebbaa1 SCUMM: Mac GUI cleanups
dfc5e93802 SCUMM: Mac GUI cleanups
457db6cd83 SCUMM: Fix and actually use the MacCheckbox _hitBounds
ec52c86969 SCUMM: Disable Mac picture widgets by default
8475cbda0b SCUMM: Attempt to save background before drawing Mac slider widget
2935f247c3 SCUMM: Add Mac GUI slider widget
d6eb449a0d SCUMM: Change talk speed range to 0-9 for Mac GUI
a0ef5cebfa SCUMM: Add mouse wheel support to Indy 3 Mac inventory as an enhancement
00c66e2abc SCUMM: Add token base class for all Mac widgets
7e51ddfcc7 SCUMM: Simplify animation for Mac GUI default button when pressing Enter
3ab70467c5 SCUMM: Simplify Mac Button drawing
26b5c50195 SCUMM: Clean up Mac GUI things, and plug a few memory leaks
0d8cded769 SCUMM: Move MacGuiObject into MacGui
2f846ca92c SCUMM: Fix regression in Indy 3 Mac IQ dialog
4593b51860 SCUMM: Clip Mac widgets to the inner surface of the dialog
66cc660496 SCUMM: Make Indy 3/Loom Mac menu depend on original GUI setting
ded4903861 SCUMM: Remove MacText text position hack
16e6b89384 SCUMM: Fix Mac GUI potentially uninitialized variables (hopefully)
3cad2d6af3 SCUMM: Guard against bad font IDs in Mac charset renderer
4d81ee243e SCUMM: Add work-in-progress Mac edit text widget
22a3f619ff SCUMM: Hopefully fix Windows build errors
9d0e596864 SCUMM: Fix a few more selection behaviors in Mac text widget
a06163b767 SCUMM: Continue work on editable Mac text widget
95e66a0355 SCUMM: Change cursor to Mac "beam" when over a text edit widget
4fc6333252 SCUMM: Hide mouse cursor while typing in a Mac edit text widget
bab186c80f SCUMM: Improve Mac "beam" cursor handling
92697e882d SCUMM: Fix Mac "beam" cursor hiding while typing
33d6bdb378 SCUMM: Fix Mac text drag selection, I hope
6d9c2bb006 SCUMM: Fix double-click past end of text in Mac edit text widget
fdc5a38695 SCUMM: Restore Mac cursor when releasing mouse button
95f0097db7 SCUMM: Fix Mac edit text widget drag-selection. Again.
bdf92b93d1 SCUMM: Initial work on scrolling Mac edit text widget on drag-selection.
0a47c0aa84 SCUMM: Update selection on Mac edit text scrolling
2cda7f071e SCUMM: Simplify the still buggy Mac edit text scrolling
9b6dc9119d SCUMM: Reorder some Mac GUI code
de0b8679e5 SCUMM: Tweak Mac edit text selection
67d7933785 SCUMM: Fix jankiness in Mac edit text scrolling
99c8f345b9 SCUMM: Fix Map "beam" cursor inverted colors
d1e0e637dc SCUMM: Fix off-by-one errors in Mac edit text scrolling
a6778d61f3 SCUMM: Add cursor blinking for Mac edit text widget
7556308aa4 SCUMM: Preliminary work on Mac slider widget
fb6d85f64f SCUMM: Hard-code the width of the Mac slider widget
c4c605502b SCUMM: Further work on Mac slider widget
5606ed8ef9 SCUMM: Clarify some rules regarding the Mac dialog window event loop
d7b05633ad SCUMM: Fix Mac slider widget scroll button highlighting
9625471842 SCUMM: Clarify Mac scroll widget "paging" a bit
16056a889f SCUMM: Implement Mac scroll widget handle dragging
8c10e5160d SCUMM: Implement Mac scrollbar dragging
e44874b746 SCUMM: Cleanup Mac slider widget
eef41c8d0e SCUMM: Fix dragging the mouse out of the Mac slider's control
a39a08c4be SCUMM: Hopefully fix Mac slider drag glitch
bf14d55d8f SCUMM: Fix compile after enhancements update
0dc5b501e9 SCUMM: Fix cursor after closing Mac dialog window
f34b4f7891 SCUMM: Make beam cursor hotspot const
9c2548d6e7 SCUMM: Fix Mac Loom practice box redrawing
48d5aa655c SCUMM: Remove temporary Loom Mac GUI variables that were only used once
fb3613ef62 SCUMM: Fix use-after-free crash with Mac Loom practice box
fc3d662a8d SCUMM: Work around Indy 3 Mac GUI being displayed during a fight
93480c331a SCUMM: MACGUI: Wire up options for Loom
885f698b29 SCUMM: MACGUI: Implement partial quality override for Loom
44061890c9 SCUMM: MACGUI: Clarify comment about music quality
7ab3fa11c6 SCUMM: Player_Mac: Ensure threadsafeness for _channelMask override
2efd9ab70a GRAPHICS: MACGUI: Add kWMModeForceMacBorder mode
2965b5cb62 SCUMM: MACGUI: Add UX enhancement for Mac menu bar
204d900dd8 SCUMM: MACGUI: Wire up quit and restart callbacks
fb86877ec1 SCUMM: MACGUI: Wire up some more callbacks
6909ddbf37 SCUMM: MACGUI: Fix build
0aefad0775 SCUMM: MACGUI: Add scrollable list to Loom load game menu
8254e3a0f8 SCUMM: MACGUI: Implement temporary workaround for sound options
d549325a7e SCUMM: MACGUI: Wire up quitGame() for Indy3
f57f2b3af5 SCUMM: MACGUI: Add MacListBox and add test code for Open Dialog
1eccfc4381 SCUMM: MACGUI: Implement saving and loading for Loom
35acda4e34 SCUMM: MACGUI: Cleanup
bd485e25d0 SCUMM: Fix bugs in Mac list box and slider widgets
c4ed2b61c0 SCUMM: MACGUI: Hardcode save and open menu for Indy3
c1d4eb362e SCUMM: MACGUI: Add stub for IQ clearing
6bbc39d92d SCUMM: MACGUI: Implement saving and loading for Indy3
64b678b86b SCUMM: Make Loom save dialog list box a bit taller
4efb72ebc1 SCUMM: Fix list box scrolling in Loom save dialog
761f2946fc SCUMM: MACGUI: Handle and clear IQ points for Indy3
59d5a24b1a SCUMM: MACGUI: Clear IQ points in a more accurate way
ed61b52180 SCUMM: Make listbox strings disabled for save dialog
a9a6ec592b SCUMM: Make word-wrapping optional in Mac static text widgets
83c94305ec SCUMM: MACGUI: Add deferred widgets handling & disable Save button on empty editText string
3f707d009d SCUMM: MACGUI: Implement quit dialog handling for out-of-menu quit events
4232178ab2 SCUMM: MACGUI: Implement restart dialog handling outside the Mac menu


Commit: ec1ab82283369891f38d9b716a3d203200d89a0c
    https://github.com/scummvm/scummvm/commit/ec1ab82283369891f38d9b716a3d203200d89a0c
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Initial work on a common MacGui class that handles Mac menus etc.

This will be used for Last Crusade and Loom. Loom is currently the more
broken of the two, though.

Changed paths:
    engines/scumm/cursor.cpp
    engines/scumm/gfx.cpp
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h
    engines/scumm/input.cpp
    engines/scumm/palette.cpp
    engines/scumm/saveload.cpp
    engines/scumm/scumm.cpp
    engines/scumm/scumm.h
    engines/scumm/string.cpp
    engines/scumm/verbs.cpp


diff --git a/engines/scumm/cursor.cpp b/engines/scumm/cursor.cpp
index 971eab8134c..91025f8aaea 100644
--- a/engines/scumm/cursor.cpp
+++ b/engines/scumm/cursor.cpp
@@ -31,6 +31,7 @@
 #include "scumm/charset.h"
 #include "scumm/he/intern_he.h"
 #include "scumm/object.h"
+#include "scumm/gfx_mac.h"
 #include "scumm/he/resource_he.h"
 #include "scumm/scumm.h"
 #include "scumm/scumm_v2.h"
@@ -995,6 +996,11 @@ void ScummEngine_v5::setBuiltinCursor(int idx) {
 	if (!_macCursorFile.empty()) {
 		Common::MacResManager resource;
 		if (resource.open(_macCursorFile)) {
+#if 0
+			if (_macWindowManager->getCursorType() == Graphics::kMacCursorCustom)
+				return;
+#endif
+
 			Common::MacResIDArray resArray = resource.getResIDArray(MKTAG('C', 'U', 'R', 'S'));
 			Common::SeekableReadStream *curs = resource.getResource(MKTAG('C', 'U', 'R', 'S'), resArray[0]);
 			Graphics::MacCursor macCursor;
@@ -1012,32 +1018,17 @@ void ScummEngine_v5::setBuiltinCursor(int idx) {
 		}
 	}
 
-	if (_macIndy3Gui) {
-		const byte buf[15 * 15] = {
-			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
-			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
-			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
-			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
-			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
-			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-			0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0xFF, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
-			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
-			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
-			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
-			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
-			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
-		};
+	if (_macGui) {
+		int width, height, hotspotX, hotspotY, animate;
 
-		_cursor.animate = 0;
-		_cursor.width = 15;
-		_cursor.height = 15;
-		_cursor.hotspotX = 7;
-		_cursor.hotspotY = 7;
+		_macGui->setupCursor(width, height, hotspotX, hotspotY, animate);
+
+		_cursor.animate = animate;
+		_cursor.width = width;
+		_cursor.height = height;
+		_cursor.hotspotX = hotspotX;
+		_cursor.hotspotY = hotspotY;
 
-		CursorMan.replaceCursor(buf, 15, 15, 7, 7, 0xFF);
 		return;
 	}
 
diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp
index b2d9c52a93e..48e15d759c7 100644
--- a/engines/scumm/gfx.cpp
+++ b/engines/scumm/gfx.cpp
@@ -1264,7 +1264,7 @@ void ScummEngine::restoreCharsetBg() {
 		_charset->_str.left = -1;
 		_charset->_left = -1;
 
-		if (_macIndy3Gui && _charset->_textScreenID == kTextVirtScreen) {
+		if (_macGui && _game.id == GID_INDY3 && _charset->_textScreenID == kTextVirtScreen) {
 			mac_undrawIndy3TextBox();
 			return;
 		}
diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 5b81d66d722..d55290ed576 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -20,10 +20,12 @@
  */
 
 #include "common/system.h"
+#include "common/macresman.h"
 
 #include "graphics/macega.h"
 #include "graphics/fonts/macfont.h"
 #include "graphics/macgui/macfontmanager.h"
+#include "graphics/macgui/macwindowmanager.h"
 
 #include "scumm/actor.h"
 #include "scumm/charset.h"
@@ -56,7 +58,7 @@ void ScummEngine::mac_markScreenAsDirty(int x, int y, int w, int h) {
 void ScummEngine::mac_drawStripToScreen(VirtScreen *vs, int top, int x, int y, int width, int height) {
 	// The verb screen is completely replaced with a custom GUI. While
 	// it is active, all other drawing to that area is suspended.
-	if (_macIndy3Gui && vs->number == kVerbVirtScreen && _macIndy3Gui->isVerbGuiActive())
+	if (_macGui && vs->number == kVerbVirtScreen && _macGui->isVerbGuiActive())
 		return;
 
 	const byte *pixels = vs->getPixels(x, top);
@@ -345,6 +347,75 @@ Common::KeyState ScummEngine::mac_showOldStyleBannerAndPause(const char *msg, in
 	return ks;
 }
 
+// ===========================================================================
+// Macintosh user interface for the Macintosh versions of Loom and Indiana
+// Jones and the Last Crusade.
+// ===========================================================================
+
+MacGui::MacGui(ScummEngine *vm, Common::String resourceFile) {
+	Common::MacResManager resource;
+
+	_windowManager = new Graphics::MacWindowManager(Graphics::kWMModeNoDesktop | Graphics::kWMModeAutohideMenu | Graphics::kWMModalMenuMode);
+	_windowManager->setEngine(vm);
+	_windowManager->setScreen(640, 400);
+	_windowManager->setMenuHotzone(Common::Rect(0, 0, 640, 23));
+	_windowManager->setMenuDelay(250000);
+
+	resource.open(resourceFile);
+
+	Common::SeekableReadStream *res;
+
+	Graphics::MacMenu *menu = _windowManager->addMenu();
+
+	const Graphics::MacMenuData menuSubItems[] = {
+		{ 0, NULL, 0, 0, false }
+	};
+
+	menu->addStaticMenus(menuSubItems);
+
+	Graphics::MacMenuSubMenu *about = menu->addSubMenu(nullptr, 0);
+	menu->addMenuItem(about, "About " + name() + "...", 0);
+
+	for (int i = 129; i <= 130; i++) {
+		res = resource.getResource(MKTAG('M', 'E', 'N', 'U'), i);
+
+		if (!res)
+			continue;
+
+		Common::StringArray *menuDef = Graphics::MacMenu::readMenuFromResource(res);
+		Common::String name = menuDef->operator[](0);
+		Common::String string = menuDef->operator[](1);
+		int id = menu->addMenuItem(nullptr, name);
+		menu->createSubMenuFromString(id, string.c_str(), 0);
+	}
+
+	resource.close();
+}
+
+MacGui::~MacGui() {
+	delete _windowManager;
+}
+
+bool MacGui::handleEvent(Common::Event &event) {
+	return _windowManager->processEvent(event);
+}
+
+void MacGui::setPalette(const byte *palette, uint size) {
+	_windowManager->passPalette(palette, size);
+}
+
+void MacGui::updateWindowManager() {
+	if (_windowManager->isMenuActive()) {
+		if (_windowManager->getCursorType() == Graphics::kMacCursorCustom)
+			_windowManager->pushCursor(Graphics::kMacCursorArrow, nullptr);
+	} else {
+		if (_windowManager->getCursorType() == Graphics::kMacCursorArrow)
+			_windowManager->popCursor();
+	}
+
+	_windowManager->draw();
+}
+
 // ===========================================================================
 // The infamous verb GUI for the Macintosh version of Indiana Jones and the
 // Last Crusade.
@@ -1171,8 +1242,37 @@ void MacIndy3Gui::Inventory::ScrollButton::draw() {
 // the work to the individual widgets.
 // ---------------------------------------------------------------------------
 
-MacIndy3Gui::MacIndy3Gui(OSystem *system, ScummEngine *vm) :
-	_system(system), _vm(vm), _surface(vm->_macScreen), _visible(false) {
+void MacIndy3Gui::setupCursor(int &width, int &height, int &hotspotX, int &hotspotY, int &animate) {
+	if (_windowManager->getCursorType() == Graphics::kMacCursorCustom)
+		return;
+
+	const byte buf[15 * 15] = {
+		3, 3, 3, 3, 3, 3, 0, 1, 0, 3, 3, 3, 3, 3, 3,
+		3, 3, 3, 3, 3, 3, 0, 1, 0, 3, 3, 3, 3, 3, 3,
+		3, 3, 3, 3, 3, 3, 0, 1, 0, 3, 3, 3, 3, 3, 3,
+		3, 3, 3, 3, 3, 3, 0, 1, 0, 3, 3, 3, 3, 3, 3,
+		3, 3, 3, 3, 3, 3, 0, 1, 0, 3, 3, 3, 3, 3, 3,
+		3, 3, 3, 3, 3, 3, 0, 1, 0, 3, 3, 3, 3, 3, 3,
+		0, 0, 0, 0, 0, 0, 3, 0, 3, 0, 0, 0, 0, 0, 0,
+		1, 1, 1, 1, 1, 1, 0, 3, 0, 1, 1, 1, 1, 1, 1,
+		0, 0, 0, 0, 0, 0, 3, 0, 3, 0, 0, 0, 0, 0, 0,
+		3, 3, 3, 3, 3, 3, 0, 1, 0, 3, 3, 3, 3, 3, 3,
+		3, 3, 3, 3, 3, 3, 0, 1, 0, 3, 3, 3, 3, 3, 3,
+		3, 3, 3, 3, 3, 3, 0, 1, 0, 3, 3, 3, 3, 3, 3,
+		3, 3, 3, 3, 3, 3, 0, 1, 0, 3, 3, 3, 3, 3, 3,
+		3, 3, 3, 3, 3, 3, 0, 1, 0, 3, 3, 3, 3, 3, 3,
+		3, 3, 3, 3, 3, 3, 0, 1, 0, 3, 3, 3, 3, 3, 3
+	};
+
+	width = height = 15;
+	hotspotX = hotspotY = 7;
+	animate = 0;
+
+	_windowManager->pushCustomCursor(buf, width, height, hotspotX, hotspotY, 3);
+}
+
+MacIndy3Gui::MacIndy3Gui(OSystem *system, ScummEngine *vm, Common::String macResourceFile) :
+	MacGui(vm, macResourceFile), _system(system), _vm(vm), _surface(vm->_macScreen), _visible(false) {
 
 	// There is one widget for every verb in the game. Verbs include the
 	// inventory widget and conversation options.
@@ -1397,9 +1497,12 @@ void MacIndy3Gui::drawVerbs() {
 	}
 }
 
-void MacIndy3Gui::handleEvent(Common::Event &event) {
+bool MacIndy3Gui::handleEvent(Common::Event &event) {
+	if (MacGui::handleEvent(event))
+		return true;
+
 	if (!isVerbGuiActive() || _vm->_userPut <= 0)
-		return;
+		return false;
 
 	if (event.type == Common::EVENT_LBUTTONDOWN) {
 		if (!_leftButtonIsPressed) {
@@ -1434,9 +1537,11 @@ void MacIndy3Gui::handleEvent(Common::Event &event) {
 				w->draw();
 				copyDirtyRectsToScreen();
 			}
-			break;
+			return true;
 		}
 	}
+
+	return false;
 }
 
 void MacIndy3Gui::show() {
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index a2037381276..1fc9591021e 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -27,14 +27,17 @@ class OSystem;
 namespace Graphics {
 struct Surface;
 class Font;
+class MacWindowManager;
 }
 
 namespace Scumm {
 
 class ScummEngine;
 
-class MacIndy3Gui {
-public:
+class MacGui {
+protected:
+	Graphics::MacWindowManager *_windowManager;
+
 	enum Color {
 		kBlack = 0,
 		kBlue = 1,
@@ -56,6 +59,36 @@ public:
 		kTransparency = 255
 	};
 
+public:
+	MacGui(ScummEngine *vm, Common::String resourceFile);
+	virtual ~MacGui();
+
+	virtual const Common::String name() const { return ""; }
+	virtual bool isVerbGuiActive() const { return false; }
+	virtual void reset() {}
+	virtual void resetAfterLoad() {}
+	virtual void update(int delta) {}
+
+	void updateWindowManager();
+
+	virtual void setupCursor(int &width, int &height, int &hotspotX, int &hotspotY, int &animate) = 0;
+	virtual bool handleEvent(Common::Event &event);
+
+	void setPalette(const byte *palette, uint size);
+};
+
+class MacLoomGui : public MacGui {
+public:
+	MacLoomGui(OSystem *system, ScummEngine *vm, Common::String macResourceFile) : MacGui(vm, macResourceFile) {}
+	~MacLoomGui() {}
+
+	const Common::String name() const { return "Loom"; }
+
+	void setupCursor(int &width, int &height, int &hotspotX, int &hotspotY, int &animate) { warning("TODO"); }
+};
+
+class MacIndy3Gui : public MacGui {
+public:
 	enum FontId {
 		kRegular = 0,
 		kBold = 1,
@@ -67,9 +100,13 @@ public:
 		kScrollDown
 	};
 
-	MacIndy3Gui(OSystem *system, ScummEngine *vm);
+	MacIndy3Gui(OSystem *system, ScummEngine *vm, Common::String macResourceFile);
 	~MacIndy3Gui();
 
+	const Common::String name() const { return "Indy"; }
+
+	void setupCursor(int &width, int &height, int &hotspotX, int &hotspotY, int &animate);
+
 	// There is a distinction between the GUI being allowed and being
 	// active. Allowed means that it's allowed to draw verbs, but not that
 	// it necessarily is. Active means that there are verbs on screen. From
@@ -86,7 +123,7 @@ public:
 	void reset();
 	void resetAfterLoad();
 	void update(int delta);
-	void handleEvent(Common::Event &event);
+	bool handleEvent(Common::Event &event);
 
 	int getInventoryScrollOffset() const;
 	void setInventoryScrollOffset(int n) const;
diff --git a/engines/scumm/input.cpp b/engines/scumm/input.cpp
index 3d51876ba12..87c59fb2e16 100644
--- a/engines/scumm/input.cpp
+++ b/engines/scumm/input.cpp
@@ -105,9 +105,9 @@ void ScummEngine_v80he::parseEvent(Common::Event event) {
 #endif
 
 void ScummEngine::parseEvent(Common::Event event) {
-	// Handle Mac Indy3 events before scaling the mouse coordinates.
-	if (_macIndy3Gui && _macIndy3Gui->isVerbGuiActive())
-		_macIndy3Gui->handleEvent(event);
+	// Handle Macintosh events before scaling the mouse coordinates.
+	if (_macGui && _macGui->handleEvent(event))
+		return;
 
 	switch (event.type) {
 	case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
diff --git a/engines/scumm/palette.cpp b/engines/scumm/palette.cpp
index 50c230f91a5..0a84369809b 100644
--- a/engines/scumm/palette.cpp
+++ b/engines/scumm/palette.cpp
@@ -33,6 +33,7 @@
 #include "scumm/scumm_v8.h"
 #include "scumm/util.h"
 #include "scumm/charset.h"
+#include "scumm/gfx_mac.h"
 
 namespace Scumm {
 
@@ -274,6 +275,9 @@ void ScummEngine::resetPalette() {
 		}
 		setDirtyColors(0, 255);
 	}
+
+	if (_macGui)
+		_macGui->setPalette(_currentPalette, 16);
 }
 
 void ScummEngine::setPaletteFromTable(const byte *ptr, int numcolor, int index) {
diff --git a/engines/scumm/saveload.cpp b/engines/scumm/saveload.cpp
index 428fe2a0da9..e1bf4eadddf 100644
--- a/engines/scumm/saveload.cpp
+++ b/engines/scumm/saveload.cpp
@@ -872,8 +872,8 @@ bool ScummEngine::loadState(int slot, bool compat, Common::String &filename) {
 		_macScreen->fillRect(Common::Rect(_macScreen->w, _macScreen->h), 0);
 	clearTextSurface();
 
-	if (_macIndy3Gui)
-		_macIndy3Gui->resetAfterLoad();
+	if (_macGui)
+		_macGui->resetAfterLoad();
 
 	_lastCodePtr = nullptr;
 	_drawObjectQueNr = 0;
@@ -2076,7 +2076,7 @@ void ScummEngine_v5::saveLoadWithSerializer(Common::Serializer &s) {
 	// invisible after loading.
 
 	if (s.isLoading() && _game.platform == Common::kPlatformMacintosh) {
-		if ((_game.id == GID_LOOM && !_macCursorFile.empty()) || _macIndy3Gui) {
+		if ((_game.id == GID_LOOM && !_macCursorFile.empty()) || _macGui) {
 			setBuiltinCursor(0);
 		}
 	}
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index be67a708884..86547d40e60 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -122,7 +122,6 @@ struct dbgChannelDesc {
 
 const char *const insaneKeymapId = "scumm-insane";
 
-
 ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)
 	: Engine(syst),
 	  _game(dr.game),
@@ -481,7 +480,7 @@ ScummEngine::~ScummEngine() {
 		delete _macIndy3TextBox;
 	}
 
-	delete _macIndy3Gui;
+	delete _macGui;
 
 #ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
 	delete _townsScreen;
@@ -1173,7 +1172,7 @@ Common::Error ScummEngine::init() {
 
 					_macIndy3TextBox = new Graphics::Surface();
 					_macIndy3TextBox->create(448, 47, Graphics::PixelFormat::createFormatCLUT8());
-					_macIndy3Gui = new MacIndy3Gui(_system, this);
+					_macGui = new MacIndy3Gui(_system, this, macResourceFile);
 					break;
 				}
 			}
@@ -1198,6 +1197,7 @@ Common::Error ScummEngine::init() {
 					_textSurfaceMultiplier = 2;
 					_macScreen = new Graphics::Surface();
 					_macScreen->create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
+					_macGui = new MacLoomGui(_system, this, macResourceFile);
 					break;
 				}
 			}
@@ -1703,9 +1703,10 @@ void ScummEngine::resetScumm() {
 		_macScreen->fillRect(Common::Rect(_macScreen->w, _macScreen->h), 0);
 	}
 
-	if (_macIndy3Gui) {
-		_macIndy3TextBox->fillRect(Common::Rect(_macIndy3TextBox->w, _macIndy3TextBox->h), 0);
-		_macIndy3Gui->reset();
+	if (_macGui) {
+		if (_game.id == GID_INDY3)
+			_macIndy3TextBox->fillRect(Common::Rect(_macIndy3TextBox->w, _macIndy3TextBox->h), 0);
+		_macGui->reset();
 	}
 
 	if (_game.version == 0) {
@@ -2453,8 +2454,8 @@ Common::Error ScummEngine::go() {
 		// Run the main loop
 		scummLoop(delta);
 
-		if (_macIndy3Gui)
-			_macIndy3Gui->update(delta);
+		if (_macGui)
+			_macGui->update(delta);
 
 		if (_game.heversion >= 60) {
 			((SoundHE *)_sound)->feedMixer();
@@ -2494,6 +2495,9 @@ void ScummEngine::waitForTimer(int quarterFrames) {
 		towns_updateGfx();
 #endif
 
+		if (_macGui)
+			_macGui->updateWindowManager();
+
 		_system->updateScreen();
 		cur = _system->getMillis();
 
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index 95897519afa..84871ba743b 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -91,7 +91,7 @@ class BaseScummFile;
 class CharsetRenderer;
 class IMuse;
 class IMuseDigital;
-class MacIndy3Gui;
+class MacGui;
 class MusicEngine;
 class Player_Towns;
 class ScummEngine;
@@ -1577,7 +1577,7 @@ public:
 	Graphics::MacFontManager *_macFontManager = nullptr;
 	Graphics::Surface *_macScreen = nullptr;
 	Graphics::Surface *_macIndy3TextBox = nullptr;
-	MacIndy3Gui *_macIndy3Gui = nullptr;
+	MacGui *_macGui = nullptr;
 
 protected:
 	byte _charsetColor = 0;
diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp
index 3874ab87440..4aac24a5fa1 100644
--- a/engines/scumm/string.cpp
+++ b/engines/scumm/string.cpp
@@ -1107,7 +1107,7 @@ void ScummEngine::CHARSET_1() {
 	if (_isRTL)
 		fakeBidiString(_charsetBuffer + _charsetBufPos, true, sizeof(_charsetBuffer) - _charsetBufPos);
 
-	bool createTextBox = (_macIndy3Gui != nullptr);
+	bool createTextBox = (_macGui && _game.id == GID_INDY3);
 	bool drawTextBox = false;
 
 	while (handleNextCharsetCode(a, &c)) {
diff --git a/engines/scumm/verbs.cpp b/engines/scumm/verbs.cpp
index 55f868735a6..b6e2e73633f 100644
--- a/engines/scumm/verbs.cpp
+++ b/engines/scumm/verbs.cpp
@@ -566,7 +566,7 @@ void ScummEngine::checkExecVerbs() {
 
 		// The Mac version of Last Crusade handles verb shortcut keys on
 		// its own, so that is also disabled here.
-		if (_macIndy3Gui && _macIndy3Gui->isVerbGuiActive())
+		if (_macGui && _macGui->isVerbGuiActive())
 			ignoreVerbKeys = true;
 
 		/* Check keypresses */
@@ -643,7 +643,7 @@ void ScummEngine::checkExecVerbs() {
 		if (!zone)
 			return;
 
-		if (_macIndy3Gui && _macIndy3Gui->isVerbGuiActive() && zone->number == kVerbVirtScreen)
+		if (_macGui && _macGui->isVerbGuiActive() && zone->number == kVerbVirtScreen)
 			return;
 
 		over = findVerbAtPos(_mouse.x, _mouse.y);
@@ -1089,7 +1089,7 @@ void ScummEngine::drawVerb(int verb, int mode) {
 	VerbSlot *vs;
 	bool tmp;
 
-	if (_macIndy3Gui)
+	if (_macGui && _game.id == GID_INDY3)
 		return;
 
 	if (!verb)
@@ -1145,7 +1145,7 @@ void ScummEngine::drawVerb(int verb, int mode) {
 }
 
 void ScummEngine::restoreVerbBG(int verb) {
-	if (_macIndy3Gui)
+	if (_macGui && _game.id == GID_INDY3)
 		return;
 
 	VerbSlot *vs;


Commit: 1b3e86380101ae417d89d346342d54bc7bc1766a
    https://github.com/scummvm/scummvm/commit/1b3e86380101ae417d89d346342d54bc7bc1766a
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
GRAPHICS: MACGUI: Consider menu item font when calculating menu width

Mac Loom and Last Crusade use bold for their "About" menu item, and that
made the menu too narrow.

Changed paths:
    graphics/macgui/macmenu.cpp


diff --git a/graphics/macgui/macmenu.cpp b/graphics/macgui/macmenu.cpp
index a9825fdb986..53e23fad3a9 100644
--- a/graphics/macgui/macmenu.cpp
+++ b/graphics/macgui/macmenu.cpp
@@ -898,14 +898,18 @@ int MacMenu::calcSubMenuWidth(MacMenuSubMenu *submenu) {
 		if (!item->text.empty()) {
 			Common::String text(item->text);
 			Common::String acceleratorText(getAcceleratorString(item, "  "));
+
+			const Font *font = getMenuFont(item->style);
+			int width = font->getStringWidth(text);
+
 			if (!acceleratorText.empty()) {
-				text += acceleratorText;
+				width += _font->getStringWidth(acceleratorText);
 			}
 
-			if (item->submenu != nullptr) // If we're drawing triangle
-				text += "  ";
+			if (item->submenu != nullptr) { // If we're drawing triangle
+				width += _font->getStringWidth("  ");
+			}
 
-			int width = _font->getStringWidth(text);
 			if (width > maxWidth) {
 				maxWidth = width;
 			}


Commit: 3a7593a8c78955a8faa85e3fba730d1d8aeaf915
    https://github.com/scummvm/scummvm/commit/3a7593a8c78955a8faa85e3fba730d1d8aeaf915
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Mac GUI restructuring

My goal right now is to move all font drawing that isn't explicitly
handled by scripts into the Mac GUI. The Indy 3 speech boxes will
probably be moved into the Mac GUI as well, since they're a bit special.
This means the charset class will only have to keep track of the game
font, which will remove some odd special cases.

Changed paths:
    engines/scumm/charset.cpp
    engines/scumm/charset.h
    engines/scumm/cursor.cpp
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h
    engines/scumm/scumm.cpp


diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp
index 6648ea3bd3b..310ad8e81b1 100644
--- a/engines/scumm/charset.cpp
+++ b/engines/scumm/charset.cpp
@@ -19,11 +19,10 @@
  *
  */
 
-#include "graphics/fonts/macfont.h"
-#include "graphics/macgui/macfontmanager.h"
-
+#include "graphics/font.h"
 #include "scumm/charset.h"
 #include "scumm/file.h"
+#include "scumm/gfx_mac.h"
 #include "scumm/scumm.h"
 #include "scumm/nut_renderer.h"
 #include "scumm/util.h"
@@ -1619,58 +1618,15 @@ CharsetRendererMac::CharsetRendererMac(ScummEngine *vm, const Common::String &fo
 	_pad = false;
 	_glyphSurface = nullptr;
 
-	// Indy 3 provides an "Indy" font in two sizes, 9 and 12, which are
-	// used for the text boxes. The smaller font can be used for a
-	// headline. The rest of the Mac GUI seems to use a system font, but
-	// that is not implemented.
-
-	// As far as I can tell, Loom uses only font size 13 for in-game text,
-	// but size 12 is used for system messages, e.g. the original pause
-	// dialog. I have no idea what size 9 is used for. Possibly the
-	// original About dialog?
-	//
-	// As far as I can tell, the game does not use anything fancy, like
-	// different styles, and the font does not appear to have a kerning
-	// table.
-	//
-	// Special characters:
-	//
-	// 16-23 are the note names c through c'.
-	// 60 is an upside-down note, i.e. the one used for c'.
-	// 95 is a used for the rest of the notes.
-
-	Graphics::MacFontManager *mfm = _vm->_macFontManager;
-
-	mfm->loadFonts(fontFile);
-
-	const Common::String fontFamily = (_vm->_game.id == GID_LOOM) ? "Loom" : "Indy";
-	const Common::Array<Graphics::MacFontFamily *> &fontFamilies = mfm->getFontFamilies();
-	int fontId = 0;
-	for (uint i = 0; i < fontFamilies.size(); i++) {
-		if (fontFamilies[i]->getName() == fontFamily) {
-			fontId = mfm->registerFontName(fontFamilies[i]->getName(), fontFamilies[i]->getFontFamilyId());
-			break;
-		}
-	}
-
-	for (uint i = 0; i < ARRAYSIZE(_macFonts); i++)
-		_macFonts[i] = nullptr;
-
-	if (_vm->_game.id == GID_INDY3) {
-		_macFonts[0] = mfm->getFont(Graphics::MacFont(fontId, 12));
-		_macFonts[1] = mfm->getFont(Graphics::MacFont(fontId, 9));
-	} else {
-		_macFonts[0] = mfm->getFont(Graphics::MacFont(fontId, 13));
-		_macFonts[1] = mfm->getFont(Graphics::MacFont(fontId, 12));
-	}
-
 	if (_vm->_renderMode == Common::kRenderMacintoshBW) {
 		int maxHeight = -1;
 		int maxWidth = -1;
 
-		for (int i = 0; i < ARRAYSIZE(_macFonts); i++) {
-			maxHeight = MAX(maxHeight, _macFonts[i]->getFontHeight());
-			maxWidth = MAX(maxWidth, _macFonts[i]->getMaxCharWidth());
+		for (int i = 0; i < 2; i++) {
+			const Graphics::Font *font = _vm->_macGui->getFontByScummId(i);
+
+			maxHeight = MAX(maxHeight, font->getFontHeight());
+			maxWidth = MAX(maxWidth, font->getMaxCharWidth());
 		}
 
 		_glyphSurface = new Graphics::Surface();
@@ -1705,7 +1661,7 @@ void CharsetRendererMac::setCurID(int32 id) {
 		}
 	}
 
-	if (id < 0 || id > ARRAYSIZE(_macFonts) || !_macFonts[id]) {
+	if (id < 0 || id > 1) {
 		warning("CharsetRendererMac::setCurID(%d) - invalid charset", id);
 		id = 0;
 	}
@@ -1735,7 +1691,9 @@ int CharsetRendererMac::getStringWidth(int arg, const byte *text) {
 }
 
 int CharsetRendererMac::getDrawWidthIntern(uint16 chr) const {
-	return _macFonts[_curId]->getCharWidth(chr);
+	const Graphics::Font *font = _vm->_macGui->getFontByScummId(_curId);
+
+	return font->getCharWidth(chr);
 }
 
 // HACK: Usually, we want the approximate width and height in the unscaled
@@ -1743,7 +1701,8 @@ int CharsetRendererMac::getDrawWidthIntern(uint16 chr) const {
 //       crusade we want the actual dimensions for drawing the text boxes.
 
 int CharsetRendererMac::getFontHeight() const {
-	int height = _macFonts[_curId]->getFontHeight();
+	const Graphics::Font *font = _vm->_macGui->getFontByScummId(_curId);
+	int height = font->getFontHeight();
 
         // If we ever need the height for font 1 in Last Crusade (we don't at
 	// the moment), we need the actual height.
@@ -1866,16 +1825,18 @@ void CharsetRendererMac::printChar(int chr, bool ignoreCharsetMask) {
 	if (!_useCorrectFontSpacing && !drawToTextBox && (width & 1))
 		width++;
 
+	const Graphics::Font *font = _vm->_macGui->getFontByScummId(_curId);
+
 	if (enableShadow) {
 		left = macLeft / 2;
 		right = (macLeft + width + 3) / 2;
 		top = macTop / 2;
-		bottom = (macTop + _macFonts[_curId]->getFontHeight() + 3) / 2;
+		bottom = (macTop + font->getFontHeight() + 3) / 2;
 	} else {
 		left = (macLeft + 1) / 2;
 		right = (macLeft + width + 1) / 2;
 		top = (macTop + 1) / 2;
-		bottom = (macTop + _macFonts[_curId]->getFontHeight() + 1) / 2;
+		bottom = (macTop + font->getFontHeight() + 1) / 2;
 	}
 
 	if (_firstChar) {
@@ -1940,6 +1901,8 @@ void CharsetRendererMac::printCharInternal(int chr, int color, bool shadow, int
 		y++;
 	}
 
+	const Graphics::Font *font = _vm->_macGui->getFontByScummId(_curId);
+
 	if (shadow) {
 		byte shadowColor = getTextShadowColor();
 
@@ -1950,32 +1913,32 @@ void CharsetRendererMac::printCharInternal(int chr, int color, bool shadow, int
 			// particularly good anyway). This seems to match the
 			// original look for normal text.
 
-			_macFonts[_curId]->drawChar(&_vm->_textSurface, chr, x + 1, y - 1, 0);
-			_macFonts[_curId]->drawChar(&_vm->_textSurface, chr, x - 1, y + 1, 0);
-			_macFonts[_curId]->drawChar(&_vm->_textSurface, chr, x + 2, y + 2, 0);
+			font->drawChar(&_vm->_textSurface, chr, x + 1, y - 1, 0);
+			font->drawChar(&_vm->_textSurface, chr, x - 1, y + 1, 0);
+			font->drawChar(&_vm->_textSurface, chr, x + 2, y + 2, 0);
 
 			if (color != -1) {
-				_macFonts[_curId]->drawChar(_vm->_macScreen, chr, x + 1, y - 1, shadowColor);
-				_macFonts[_curId]->drawChar(_vm->_macScreen, chr, x - 1, y + 1, shadowColor);
-				_macFonts[_curId]->drawChar(_vm->_macScreen, chr, x + 2, y + 2, shadowColor);
+				font->drawChar(_vm->_macScreen, chr, x + 1, y - 1, shadowColor);
+				font->drawChar(_vm->_macScreen, chr, x - 1, y + 1, shadowColor);
+				font->drawChar(_vm->_macScreen, chr, x + 2, y + 2, shadowColor);
 			}
 		} else {
 			// Indy 3 uses simpler shadowing, and doesn't need the
 			// "draw only on text surface" hack.
 
-			_macFonts[_curId]->drawChar(&_vm->_textSurface, chr, x + 1, y + 1, 0);
-			_macFonts[_curId]->drawChar(_vm->_macScreen, chr, x + 1, y + 1, shadowColor);
+			font->drawChar(&_vm->_textSurface, chr, x + 1, y + 1, 0);
+			font->drawChar(_vm->_macScreen, chr, x + 1, y + 1, shadowColor);
 		}
 	}
 
-	_macFonts[_curId]->drawChar(&_vm->_textSurface, chr, x, y, 0);
+	font->drawChar(&_vm->_textSurface, chr, x, y, 0);
 
 	if (color != -1) {
 		color = getTextColor();
 
 		if (_vm->_renderMode == Common::kRenderMacintoshBW && color != 0 && color != 15) {
 			_glyphSurface->fillRect(Common::Rect(_glyphSurface->w, _glyphSurface->h), 0);
-			_macFonts[_curId]->drawChar(_glyphSurface, chr, 0, 0, 15);
+			font->drawChar(_glyphSurface, chr, 0, 0, 15);
 
 			byte *src = (byte *)_glyphSurface->getBasePtr(0, 0);
 			byte *dst = (byte *)_vm->_macScreen->getBasePtr(x, y);
@@ -1996,14 +1959,7 @@ void CharsetRendererMac::printCharInternal(int chr, int color, bool shadow, int
 				dst += _vm->_macScreen->pitch;
 			}
 		} else {
-			_macFonts[_curId]->drawChar(_vm->_macScreen, chr, x, y, color);
-
-			// Outlined text in Indy 3 should be filled. We simulate
-			// that by drawing the text again in bold.
-			if (_vm->_game.id == GID_INDY3 && _curId == 4) {
-				_macFonts[3]->drawChar(&_vm->_textSurface, chr, x + 1, y, 0);
-				_macFonts[3]->drawChar(_vm->_macScreen, chr, x + 1, y, 15);
-			}
+			font->drawChar(_vm->_macScreen, chr, x, y, color);
 		}
 	}
 }
@@ -2024,7 +1980,9 @@ void CharsetRendererMac::printCharToTextBox(int chr, int color, int x, int y) {
 	if (y > 0)
 		y = 17;
 
-	_macFonts[_curId]->drawChar(_vm->_macIndy3TextBox, chr, x + 5, y + 11, color);
+	const Graphics::Font *font = _vm->_macGui->getFontByScummId(_curId);
+
+	font->drawChar(_vm->_macIndy3TextBox, chr, x + 5, y + 11, color);
 }
 
 void CharsetRendererMac::drawChar(int chr, Graphics::Surface &s, int x, int y) {
@@ -2037,7 +1995,9 @@ void CharsetRendererMac::drawChar(int chr, Graphics::Surface &s, int x, int y) {
 	if (_vm->_renderMode == Common::kRenderMacintoshBW)
 		color = 15;
 
-	_macFonts[_curId]->drawChar(&s, chr, x, y, color);
+	const Graphics::Font *font = _vm->_macGui->getFontByScummId(_curId);
+
+	font->drawChar(&s, chr, x, y, color);
 }
 
 void CharsetRendererMac::setColor(byte color) {
diff --git a/engines/scumm/charset.h b/engines/scumm/charset.h
index 21aec0bc367..2e84495bbcb 100644
--- a/engines/scumm/charset.h
+++ b/engines/scumm/charset.h
@@ -112,6 +112,7 @@ public:
 	virtual int getCharWidth(uint16 chr) const = 0;
 
 	virtual void setColor(byte color) { _color = color; translateColor(); }
+	virtual byte getColor() { return _color; }
 
 	void saveLoadWithSerializer(Common::Serializer &ser);
 };
@@ -284,12 +285,13 @@ public:
 
 class CharsetRendererMac : public CharsetRendererCommon {
 protected:
-	const Graphics::Font *_macFonts[2];
 	bool _useRealCharWidth;
 	bool _useCorrectFontSpacing;
 	bool _pad;
 	int _lastTop;
 
+	const Graphics::Font *getFont(int id) const;
+
 	int getDrawWidthIntern(uint16 chr) const;
 
 	void printCharInternal(int chr, int color, bool shadow, int x, int y);
diff --git a/engines/scumm/cursor.cpp b/engines/scumm/cursor.cpp
index 91025f8aaea..09d6f265e2e 100644
--- a/engines/scumm/cursor.cpp
+++ b/engines/scumm/cursor.cpp
@@ -23,7 +23,6 @@
 #include "common/macresman.h"
 #include "common/util.h"
 #include "graphics/cursorman.h"
-#include "graphics/maccursor.h"
 #ifdef ENABLE_HE
 #include "graphics/wincursor.h"
 #endif
@@ -993,31 +992,6 @@ void ScummEngine_v5::resetCursors() {
 }
 
 void ScummEngine_v5::setBuiltinCursor(int idx) {
-	if (!_macCursorFile.empty()) {
-		Common::MacResManager resource;
-		if (resource.open(_macCursorFile)) {
-#if 0
-			if (_macWindowManager->getCursorType() == Graphics::kMacCursorCustom)
-				return;
-#endif
-
-			Common::MacResIDArray resArray = resource.getResIDArray(MKTAG('C', 'U', 'R', 'S'));
-			Common::SeekableReadStream *curs = resource.getResource(MKTAG('C', 'U', 'R', 'S'), resArray[0]);
-			Graphics::MacCursor macCursor;
-			if (macCursor.readFromStream(*curs)) {
-				_cursor.animate = 0;
-				_cursor.width = macCursor.getWidth();
-				_cursor.height = macCursor.getHeight();
-				_cursor.hotspotX = macCursor.getHotspotX();
-				_cursor.hotspotY = macCursor.getHotspotY();
-				CursorMan.replaceCursor(&macCursor);
-				delete curs;
-				return;
-			}
-			delete curs;
-		}
-	}
-
 	if (_macGui) {
 		int width, height, hotspotX, hotspotY, animate;
 
diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index d55290ed576..c1d4660fd8a 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -22,6 +22,7 @@
 #include "common/system.h"
 #include "common/macresman.h"
 
+#include "graphics/maccursor.h"
 #include "graphics/macega.h"
 #include "graphics/fonts/macfont.h"
 #include "graphics/macgui/macfontmanager.h"
@@ -182,22 +183,21 @@ void ScummEngine::mac_createIndy3TextBox(Actor *a) {
 	_macIndy3TextBox->fillRect(Common::Rect(width, height), 0);
 
 	int nameWidth = 0;
+	byte color = _charset->getColor();
 
 	if (a) {
-		int oldID = _charset->getCurID();
-		_charset->setCurID(2 | 0x80);
+		const Graphics::Font *font = _macGui->getFont(MacGui::kIndy3FontSmall);
 
 		const char *name = (const char *)a->getActorName();
 		int charX = 25;
 
 		for (int i = 0; name[i] && nameWidth < width - 50; i++) {
-			_charset->drawChar(name[i], *_macIndy3TextBox, charX, 0);
-			nameWidth += _charset->getCharWidth(name[i]);
-			charX += _charset->getCharWidth(name[i]);
+			font->drawChar(_macIndy3TextBox, name[i], charX, 0, color);
+			nameWidth += font->getCharWidth(name[i]);
+			charX += font->getCharWidth(name[i]);
 		}
 
-		_charset->drawChar(':', *_macIndy3TextBox, charX, 0);
-		_charset->setCurID(oldID);
+		font->drawChar(_macIndy3TextBox, ':', charX, 0, color);
 	}
 
 	if (nameWidth) {
@@ -265,13 +265,15 @@ void ScummEngine::mac_drawBorder(int x, int y, int w, int h, byte color) {
 }
 
 Common::KeyState ScummEngine::mac_showOldStyleBannerAndPause(const char *msg, int32 waitTime) {
-	char bannerMsg[512];
+	byte bannerMsg[512];
 
 	_messageBannerActive = true;
 
 	// Fetch the translated string for the message...
 	convertMessageToString((const byte *)msg, (byte *)bannerMsg, sizeof(bannerMsg));
 
+	_macGui->drawBanner(bannerMsg);
+#if 0
 	// Backup the surfaces...
 	int x = 70;
 	int y = 189;
@@ -286,6 +288,7 @@ Common::KeyState ScummEngine::mac_showOldStyleBannerAndPause(const char *msg, in
 
 	backupTextSurface.copyRectToSurface(_textSurface, 0, 0, Common::Rect(x, y, x + w + 1, y + h));
 	backupMacScreen.copyRectToSurface(*_macScreen, 0, 0, Common::Rect(x, y, x + w + 1, y + h));
+#endif
 
 	// Pause shake effect
 	_shakeTempSavedState = _shakeEnabled;
@@ -294,30 +297,29 @@ Common::KeyState ScummEngine::mac_showOldStyleBannerAndPause(const char *msg, in
 	// Pause the engine
 	PauseToken pt = pauseEngine();
 
-	// Backup the current charsetId...
-	int oldId = _charset->getCurID();
-	_charset->setCurID(1 | 0x80);
-	_charset->setColor(0);
-
+#if 0
 	_textSurface.fillRect(Common::Rect(x, y, x + w + 1, y + h), 0);
 	_macScreen->fillRect(Common::Rect(x + 1, y + 1, x + w, y + h - 1), 15);
 	mac_drawBorder(x, y, w, h, 0);
 	mac_drawBorder(x + 2, y + 2, w - 4, h - 4, 0);
 
+	const Graphics::Font *font = _macGui->getFont(_game.id == GID_INDY3 ? MacGui::kIndy3FontMedium : MacGui::kLoomFontMedium);
+
 	int stringWidth = 0;
 
 	for (int i = 0; msg[i]; i++)
-		stringWidth += _charset->getCharWidth(msg[i]);
+		stringWidth += font->getCharWidth(msg[i]);
 
 	int stringX = 1 + x + (w - stringWidth) / 2;
 
 	for (int i = 0; msg[i]; i++) {
-		_charset->drawChar(msg[i], *_macScreen, stringX, y + 4);
-		stringX += _charset->getCharWidth(msg[i]);
+		font->drawChar(_macScreen, msg[i], stringX, y + 4, 0);
+		stringX += font->getCharWidth(msg[i]);
 	}
 
 	mac_markScreenAsDirty(x, y, w, h);
 	ScummEngine::drawDirtyScreenParts();
+#endif
 
 	Common::KeyState ks = Common::KEYCODE_INVALID;
 	bool leftBtnPressed = false, rightBtnPressed = false;
@@ -325,6 +327,8 @@ Common::KeyState ScummEngine::mac_showOldStyleBannerAndPause(const char *msg, in
 		waitForBannerInput(waitTime, ks, leftBtnPressed, rightBtnPressed);
 	}
 
+	_macGui->undrawBanner();
+#if 0
 	// Restore the surfaces...
 	_textSurface.copyRectToSurface(backupTextSurface, x, y, Common::Rect(0, 0, w + 1, h));
 	_macScreen->copyRectToSurface(backupMacScreen, x, y, Common::Rect(0, 0, w + 1, h));
@@ -335,13 +339,12 @@ Common::KeyState ScummEngine::mac_showOldStyleBannerAndPause(const char *msg, in
 	// Notify the gfx system that we restored the surfaces...
 	mac_markScreenAsDirty(x, y, w + 1, h);
 	ScummEngine::drawDirtyScreenParts();
+#endif
 
 	// Finally, resume the engine, clear the input state, and restore the charset.
 	pt.clear();
 	clearClickedStatus();
 
-	_charset->setCurID(oldId);
-
 	_messageBannerActive = false;
 
 	return ks;
@@ -352,29 +355,38 @@ Common::KeyState ScummEngine::mac_showOldStyleBannerAndPause(const char *msg, in
 // Jones and the Last Crusade.
 // ===========================================================================
 
-MacGui::MacGui(ScummEngine *vm, Common::String resourceFile) {
-	Common::MacResManager resource;
+MacGui::MacGui(ScummEngine *vm, Common::String resourceFile) : _vm(vm), _system(_vm->_system), _surface(_vm->_macScreen), _resourceFile(resourceFile) {
+	_fonts.clear();
+}
 
+MacGui::~MacGui() {
+	delete _windowManager;
+	delete _backupSurface;
+}
+
+void MacGui::initialize() {
 	_windowManager = new Graphics::MacWindowManager(Graphics::kWMModeNoDesktop | Graphics::kWMModeAutohideMenu | Graphics::kWMModalMenuMode);
-	_windowManager->setEngine(vm);
+	_windowManager->setEngine(_vm);
 	_windowManager->setScreen(640, 400);
 	_windowManager->setMenuHotzone(Common::Rect(0, 0, 640, 23));
 	_windowManager->setMenuDelay(250000);
 
-	resource.open(resourceFile);
-
+	Common::MacResManager resource;
 	Common::SeekableReadStream *res;
-
 	Graphics::MacMenu *menu = _windowManager->addMenu();
 
+	resource.open(_resourceFile);
+
+	// Add the Apple menu
+
 	const Graphics::MacMenuData menuSubItems[] = {
 		{ 0, NULL, 0, 0, false }
 	};
 
-	menu->addStaticMenus(menuSubItems);
+	Common::String aboutMenuDef = "About " + name() + "...<B";
 
-	Graphics::MacMenuSubMenu *about = menu->addSubMenu(nullptr, 0);
-	menu->addMenuItem(about, "About " + name() + "...", 0);
+	menu->addStaticMenus(menuSubItems);
+	menu->createSubMenuFromString(0, aboutMenuDef.c_str(), 0);
 
 	for (int i = 129; i <= 130; i++) {
 		res = resource.getResource(MKTAG('M', 'E', 'N', 'U'), i);
@@ -390,10 +402,41 @@ MacGui::MacGui(ScummEngine *vm, Common::String resourceFile) {
 	}
 
 	resource.close();
+
+	// Register custom fonts. The font family just happens to match the
+	// printed name of the game.
+
+	const Common::String fontFamily = name();
+
+	const Common::Array<Graphics::MacFontFamily *> &fontFamilies = _windowManager->_fontMan->getFontFamilies();
+
+	_windowManager->_fontMan->loadFonts(_resourceFile);
+
+	for (uint i = 0; i < fontFamilies.size(); i++) {
+		if (fontFamilies[i]->getName() == fontFamily) {
+			_gameFontId = _windowManager->_fontMan->registerFontName(fontFamily, fontFamilies[i]->getFontFamilyId());
+debug("_gameFontId = %d", _gameFontId);
+			break;
+		}
+	}
 }
 
-MacGui::~MacGui() {
-	delete _windowManager;
+const Graphics::Font *MacGui::getFont(FontId fontId) {
+	const Graphics::Font *font = _fonts.getValOrDefault((int)fontId);
+
+	if (font)
+		return font;
+
+	int id;
+	int size;
+	int slant;
+
+	getFontParams(fontId, id, size, slant);
+
+	font = _windowManager->_fontMan->getFont(Graphics::MacFont(id, size, slant));
+	_fonts[(int)fontId] = font;
+
+	return font;
 }
 
 bool MacGui::handleEvent(Common::Event &event) {
@@ -416,9 +459,122 @@ void MacGui::updateWindowManager() {
 	_windowManager->draw();
 }
 
+void MacGui::drawBanner(byte *message) {
+	_bannerBounds.left = 70;
+	_bannerBounds.top = 189;
+	_bannerBounds.setWidth(499);
+	_bannerBounds.setHeight(22);
+
+	if (!_backupSurface) {
+		_backupSurface = new Graphics::Surface();
+		_backupSurface->create(_bannerBounds.width(), _bannerBounds.height(), Graphics::PixelFormat::createFormatCLUT8());
+	}
+
+	_backupSurface->copyRectToSurface(*_surface, 0, 0, _bannerBounds);
+	_surface->fillRect(Common::Rect(_bannerBounds.left + 1, _bannerBounds.top + 1, _bannerBounds.right - 1, _bannerBounds.bottom - 1), kWhite);
+	drawBannerBorder(_bannerBounds.left, _bannerBounds.top, _bannerBounds.right - 1, _bannerBounds.bottom - 1, kBlack);
+	drawBannerBorder(_bannerBounds.left + 2, _bannerBounds.top + 2, _bannerBounds.right - 3, _bannerBounds.bottom - 3, kBlack);
+	_system->copyRectToScreen(_surface->getBasePtr(_bannerBounds.left, _bannerBounds.top), _surface->pitch, _bannerBounds.left, _bannerBounds.top, _bannerBounds.width(), _bannerBounds.height());
+}
+
+void MacGui::undrawBanner() {
+	if (!_backupSurface)
+		return;
+
+	_surface->copyRectToSurface(*_backupSurface, _bannerBounds.left, _bannerBounds.top, Common::Rect(0, 0, _bannerBounds.width(), _bannerBounds.height()));
+	_system->copyRectToScreen(_surface->getBasePtr(_bannerBounds.left, _bannerBounds.top), _surface->pitch, _bannerBounds.left, _bannerBounds.top, _bannerBounds.width(), _bannerBounds.height());
+
+	_backupSurface->free();
+	delete _backupSurface;
+	_backupSurface = nullptr;
+}
+
+void MacGui::drawBannerBorder(int x0, int y0, int x1, int y1, byte color) {
+	_surface->hLine(x0 + 2, y0, x1 - 2, color);
+	_surface->hLine(x0 + 2, y1 - 1, x1 - 2, color);
+	_surface->vLine(x0, y0 + 2, y1 - 3, color);
+	_surface->vLine(x1, y0 + 2, y1 - 3, color);
+	_surface->setPixel(x0 + 1, y0 + 1, color);
+	_surface->setPixel(x1 - 1, y0 + 1, color);
+	_surface->setPixel(x0 + 1, y1 - 2, color);
+	_surface->setPixel(x1 - 1, y1 - 2, color);
+}
+
 // ===========================================================================
-// The infamous verb GUI for the Macintosh version of Indiana Jones and the
-// Last Crusade.
+// The Mac Loom GUI. This one is pretty simple.
+// ===========================================================================
+
+const Graphics::Font *MacLoomGui::getFontByScummId(int32 id) {
+	switch (id) {
+	case 0:
+		return getFont(kLoomFontLarge);
+	default:
+		error("MacLoomGui::getFontByScummId: Invalid font id %d", id);
+	}
+}
+
+void MacLoomGui::getFontParams(FontId fontId, int &id, int &size, int &slant) {
+	// Loom uses only font size 13 for in-game text, but size 12 is used
+	// for system messages, e.g. the original pause dialog.
+	//
+	// Special characters:
+	//
+	// 16-23 are the note names c through c'.
+	// 60 is an upside-down note, i.e. the one used for c'.
+	// 95 is a used for the rest of the notes.
+
+	switch (fontId) {
+	case FontId::kLoomFontSmall:
+		id = _gameFontId;
+		size = 9;
+		slant = Graphics::kMacFontRegular;
+		break;
+
+	case FontId::kLoomFontMedium:
+		id = _gameFontId;
+		size = 12;
+		slant = Graphics::kMacFontRegular;
+		break;
+
+	case FontId::kLoomFontLarge:
+		id = _gameFontId;
+		size = 13;
+		slant = Graphics::kMacFontRegular;
+		break;
+
+	default:
+		error("MacLoomGui: getFontParams: Unknown font id %d", (int)fontId);
+	}
+}
+
+void MacLoomGui::setupCursor(int &width, int &height, int &hotspotX, int &hotspotY, int &animate) {
+	if (_windowManager->getCursorType() == Graphics::kMacCursorCustom)
+		return;
+
+	Common::MacResManager resource;
+
+	resource.open(_resourceFile);
+
+	Common::MacResIDArray resArray = resource.getResIDArray(MKTAG('C', 'U', 'R', 'S'));
+	Common::SeekableReadStream *curs = resource.getResource(MKTAG('C', 'U', 'R', 'S'), resArray[0]);
+	Graphics::MacCursor macCursor;
+
+	if (macCursor.readFromStream(*curs)) {
+		width = macCursor.getWidth();
+		height = macCursor.getHeight();
+		hotspotX = macCursor.getHotspotX();
+		hotspotY = macCursor.getHotspotY();
+		animate = 0;
+
+		_windowManager->pushCustomCursor(&macCursor);
+	}
+
+	delete curs;
+}
+
+// ===========================================================================
+// The Mac GUI for Indiana Jones and the Last Crusade, including the infamous
+// verb GUI.
 //
 // It's likely that the original interpreter used more hooks from the engine
 // into the GUI. In particular, the inventory script uses a variable that I
@@ -686,8 +842,8 @@ void MacIndy3Gui::Button::draw() {
 	// This gives us pixel perfect rendering for the English verbs.
 
 	if (!_text.empty()) {
-		const Graphics::Font *boldFont = _gui->getFont(kBold);
-		const Graphics::Font *outlineFont = _gui->getFont(kOutline);
+		const Graphics::Font *boldFont = _gui->getFont(kIndy3VerbFontBold);
+		const Graphics::Font *outlineFont = _gui->getFont(kIndy3VerbFontOutline);
 
 		int stringWidth = 0;
 		for (uint i = 0; i < _text.size(); i++)
@@ -1036,7 +1192,7 @@ void MacIndy3Gui::Inventory::Slot::draw() {
 	_surface->fillRect(_bounds, bg);
 
 	if (hasName()) {
-		const Graphics::Font *font = _gui->getFont(kRegular);
+		const Graphics::Font *font = _gui->getFont(kIndy3VerbFontRegular);
 
 		int y = _bounds.top - 1;
 		int x = _bounds.left + 4;
@@ -1271,8 +1427,8 @@ void MacIndy3Gui::setupCursor(int &width, int &height, int &hotspotX, int &hotsp
 	_windowManager->pushCustomCursor(buf, width, height, hotspotX, hotspotY, 3);
 }
 
-MacIndy3Gui::MacIndy3Gui(OSystem *system, ScummEngine *vm, Common::String macResourceFile) :
-	MacGui(vm, macResourceFile), _system(system), _vm(vm), _surface(vm->_macScreen), _visible(false) {
+MacIndy3Gui::MacIndy3Gui(ScummEngine *vm, Common::String resourceFile) :
+	MacGui(vm, resourceFile), _visible(false) {
 
 	// There is one widget for every verb in the game. Verbs include the
 	// inventory widget and conversation options.
@@ -1281,9 +1437,6 @@ MacIndy3Gui::MacIndy3Gui(OSystem *system, ScummEngine *vm, Common::String macRes
 	Widget::_surface = _surface;
 	Widget::_gui = this;
 
-	for (int i = 0; i < ARRAYSIZE(_fonts); i++)
-		_fonts[i] = nullptr;
-
 	_widgets[  1] = new Button(137, 312,  68, 18); // Open
 	_widgets[  2] = new Button(137, 332,  68, 18); // Close
 	_widgets[  3] = new Button( 67, 352,  68, 18); // Give
@@ -1325,6 +1478,56 @@ MacIndy3Gui::~MacIndy3Gui() {
 		delete it._value;
 }
 
+const Graphics::Font *MacIndy3Gui::getFontByScummId(int32 id) {
+	switch (id) {
+	case 0:
+		return getFont(kIndy3FontMedium);
+	default:
+		error("MacIndy3Gui::getFontByScummId: Invalid font id %d", id);
+	}
+}
+
+void MacIndy3Gui::getFontParams(FontId fontId, int &id, int &size, int &slant) {
+	// Indy 3 provides an "Indy" font in two sizes, 9 and 12, which are
+	// used for the text boxes. The smaller font can be used for a
+	// headline. The rest of the Indy 3 verb GUI uses Geneva.
+
+	switch (fontId) {
+	case FontId::kIndy3FontSmall:
+		id = _gameFontId;
+		size = 9;
+		slant = Graphics::kMacFontRegular;
+		break;
+
+	case FontId::kIndy3FontMedium:
+		id = _gameFontId;
+		size = 12;
+		slant = Graphics::kMacFontRegular;
+		break;
+
+	case FontId::kIndy3VerbFontRegular:
+		id = Graphics::kMacFontGeneva;
+		size = 9;
+		slant = Graphics::kMacFontRegular;
+		break;
+
+	case FontId::kIndy3VerbFontBold:
+		id = Graphics::kMacFontGeneva;
+		size = 9;
+		slant = Graphics::kMacFontBold;
+		break;
+
+	case FontId::kIndy3VerbFontOutline:
+		id = Graphics::kMacFontGeneva;
+		size = 9;
+		slant = Graphics::kMacFontBold | Graphics::kMacFontOutline | Graphics::kMacFontCondense;
+		break;
+
+	default:
+		error("MacIndy3Gui: getFontParams: Unknown font id %d", (int)fontId);
+	}
+}
+
 // Before the GUI rewrite, the scroll offset was saved in variable 67. Let's
 // continue that tradition, just in case. If nothing else, it gives us an easy
 // way to still store it in savegames.
@@ -1355,34 +1558,6 @@ bool MacIndy3Gui::isVerbGuiActive() const {
 	return _visible && isVerbGuiAllowed();
 }
 
-const Graphics::Font *MacIndy3Gui::getFont(FontId fontId) {
-	if (!_fonts[fontId]) {
-		int id = Graphics::kMacFontGeneva;
-		int size = 9;
-		int slant = Graphics::kMacFontRegular;
-
-		switch (fontId) {
-		case kRegular:
-			break;
-
-		case kBold:
-			slant = Graphics::kMacFontBold;
-			break;
-
-		case kOutline:
-			slant = Graphics::kMacFontBold | Graphics::kMacFontOutline | Graphics::kMacFontCondense;
-			break;
-
-		default:
-			error("MacIndy3Gui: getFont: Unknown font id %d", fontId);
-		}
-
-		_fonts[fontId] = _vm->_macFontManager->getFont(Graphics::MacFont(id, size, slant));
-	}
-
-	return _fonts[fontId];
-}
-
 void MacIndy3Gui::reset() {
 	_visible = false;
 
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 1fc9591021e..89dbec62b77 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -36,7 +36,18 @@ class ScummEngine;
 
 class MacGui {
 protected:
-	Graphics::MacWindowManager *_windowManager;
+	ScummEngine *_vm = nullptr;
+	OSystem *_system = nullptr;
+
+	Graphics::MacWindowManager *_windowManager = nullptr;
+	Graphics::Surface *_surface = nullptr;
+	Common::String _resourceFile;
+
+	Common::HashMap<int, const Graphics::Font *> _fonts;
+	int _gameFontId = -1;
+
+	Common::Rect _bannerBounds;
+	Graphics::Surface *_backupSurface = nullptr;
 
 	enum Color {
 		kBlack = 0,
@@ -60,10 +71,29 @@ protected:
 	};
 
 public:
+	enum FontId {
+		kIndy3FontSmall,
+		kIndy3FontMedium,
+		kIndy3VerbFontRegular,
+		kIndy3VerbFontBold,
+		kIndy3VerbFontOutline,
+
+		kLoomFontSmall,
+		kLoomFontMedium,
+		kLoomFontLarge
+	};
+
 	MacGui(ScummEngine *vm, Common::String resourceFile);
 	virtual ~MacGui();
 
-	virtual const Common::String name() const { return ""; }
+	virtual const Common::String name() const = 0;
+
+	virtual void initialize();
+
+	const Graphics::Font *getFont(FontId fontId);
+	virtual const Graphics::Font *getFontByScummId(int32 id) = 0;
+	virtual void getFontParams(FontId fontId, int &id, int &size, int &slant) = 0;
+
 	virtual bool isVerbGuiActive() const { return false; }
 	virtual void reset() {}
 	virtual void resetAfterLoad() {}
@@ -75,36 +105,40 @@ public:
 	virtual bool handleEvent(Common::Event &event);
 
 	void setPalette(const byte *palette, uint size);
+
+	void drawBanner(byte *message);
+	void undrawBanner();
+	void drawBannerBorder(int x, int y, int w, int h, byte color);
 };
 
 class MacLoomGui : public MacGui {
 public:
-	MacLoomGui(OSystem *system, ScummEngine *vm, Common::String macResourceFile) : MacGui(vm, macResourceFile) {}
+	MacLoomGui(OSystem *system, ScummEngine *vm, Common::String resourceFile) : MacGui(vm, resourceFile) {}
 	~MacLoomGui() {}
 
 	const Common::String name() const { return "Loom"; }
 
-	void setupCursor(int &width, int &height, int &hotspotX, int &hotspotY, int &animate) { warning("TODO"); }
+	const Graphics::Font *getFontByScummId(int32 id);
+	void getFontParams(FontId fontId, int &id, int &size, int &slant);
+
+	void setupCursor(int &width, int &height, int &hotspotX, int &hotspotY, int &animate);
 };
 
 class MacIndy3Gui : public MacGui {
 public:
-	enum FontId {
-		kRegular = 0,
-		kBold = 1,
-		kOutline = 2
-	};
-
 	enum ScrollDirection {
 		kScrollUp,
 		kScrollDown
 	};
 
-	MacIndy3Gui(OSystem *system, ScummEngine *vm, Common::String macResourceFile);
+	MacIndy3Gui(ScummEngine *vm, Common::String resourceFile);
 	~MacIndy3Gui();
 
 	const Common::String name() const { return "Indy"; }
 
+	const Graphics::Font *getFontByScummId(int32 id);
+	void getFontParams(FontId fontId, int &id, int &size, int &slant);
+
 	void setupCursor(int &width, int &height, int &hotspotX, int &hotspotY, int &animate);
 
 	// There is a distinction between the GUI being allowed and being
@@ -118,7 +152,6 @@ public:
 	// the verb area to clear the power meters and text.
 
 	bool isVerbGuiActive() const;
-	const Graphics::Font *getFont(FontId fontId);
 
 	void reset();
 	void resetAfterLoad();
@@ -129,11 +162,6 @@ public:
 	void setInventoryScrollOffset(int n) const;
 
 private:
-	OSystem *_system = nullptr;
-	ScummEngine *_vm = nullptr;
-	Graphics::Surface *_surface = nullptr;
-	const Graphics::Font *_fonts[3];
-
 	bool _visible = false;
 
 	bool _leftButtonIsPressed = false;
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 86547d40e60..1e53bc9f3d6 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -1172,7 +1172,7 @@ Common::Error ScummEngine::init() {
 
 					_macIndy3TextBox = new Graphics::Surface();
 					_macIndy3TextBox->create(448, 47, Graphics::PixelFormat::createFormatCLUT8());
-					_macGui = new MacIndy3Gui(_system, this, macResourceFile);
+					_macGui = new MacIndy3Gui(this, macResourceFile);
 					break;
 				}
 			}
@@ -1230,9 +1230,11 @@ Common::Error ScummEngine::init() {
 			}
 		}
 
-		if (!_macScreen && _renderMode == Common::kRenderMacintoshBW) {
+		if (!_macScreen && _renderMode == Common::kRenderMacintoshBW)
 			_renderMode = Common::kRenderDefault;
-		}
+
+		if (_macGui)
+			_macGui->initialize();
 	}
 
 	// Initialize backend
@@ -2452,13 +2454,15 @@ Common::Error ScummEngine::go() {
 		waitForTimer(delta * 4);
 
 		// Run the main loop
-		scummLoop(delta);
+		if (!isPaused()) {
+			scummLoop(delta);
 
-		if (_macGui)
-			_macGui->update(delta);
+			if (_macGui)
+				_macGui->update(delta);
 
-		if (_game.heversion >= 60) {
-			((SoundHE *)_sound)->feedMixer();
+			if (_game.heversion >= 60) {
+				((SoundHE *)_sound)->feedMixer();
+			}
 		}
 
 		if (shouldQuit()) {


Commit: e2ca711d5b7bcffd61a6b424e8eb49857406c1e3
    https://github.com/scummvm/scummvm/commit/e2ca711d5b7bcffd61a6b424e8eb49857406c1e3
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Finish migrating Mac banner drawing to Mac GUI

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h
    engines/scumm/input.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index c1d4660fd8a..e7bf82fcd5b 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -265,7 +265,7 @@ void ScummEngine::mac_drawBorder(int x, int y, int w, int h, byte color) {
 }
 
 Common::KeyState ScummEngine::mac_showOldStyleBannerAndPause(const char *msg, int32 waitTime) {
-	byte bannerMsg[512];
+	char bannerMsg[512];
 
 	_messageBannerActive = true;
 
@@ -273,22 +273,6 @@ Common::KeyState ScummEngine::mac_showOldStyleBannerAndPause(const char *msg, in
 	convertMessageToString((const byte *)msg, (byte *)bannerMsg, sizeof(bannerMsg));
 
 	_macGui->drawBanner(bannerMsg);
-#if 0
-	// Backup the surfaces...
-	int x = 70;
-	int y = 189;
-	int w = 499;
-	int h = 22;
-
-	Graphics::Surface backupTextSurface;
-	Graphics::Surface backupMacScreen;
-
-	backupTextSurface.create(w + 1, h, Graphics::PixelFormat::createFormatCLUT8());
-	backupMacScreen.create(w + 1, h, Graphics::PixelFormat::createFormatCLUT8());
-
-	backupTextSurface.copyRectToSurface(_textSurface, 0, 0, Common::Rect(x, y, x + w + 1, y + h));
-	backupMacScreen.copyRectToSurface(*_macScreen, 0, 0, Common::Rect(x, y, x + w + 1, y + h));
-#endif
 
 	// Pause shake effect
 	_shakeTempSavedState = _shakeEnabled;
@@ -297,30 +281,6 @@ Common::KeyState ScummEngine::mac_showOldStyleBannerAndPause(const char *msg, in
 	// Pause the engine
 	PauseToken pt = pauseEngine();
 
-#if 0
-	_textSurface.fillRect(Common::Rect(x, y, x + w + 1, y + h), 0);
-	_macScreen->fillRect(Common::Rect(x + 1, y + 1, x + w, y + h - 1), 15);
-	mac_drawBorder(x, y, w, h, 0);
-	mac_drawBorder(x + 2, y + 2, w - 4, h - 4, 0);
-
-	const Graphics::Font *font = _macGui->getFont(_game.id == GID_INDY3 ? MacGui::kIndy3FontMedium : MacGui::kLoomFontMedium);
-
-	int stringWidth = 0;
-
-	for (int i = 0; msg[i]; i++)
-		stringWidth += font->getCharWidth(msg[i]);
-
-	int stringX = 1 + x + (w - stringWidth) / 2;
-
-	for (int i = 0; msg[i]; i++) {
-		font->drawChar(_macScreen, msg[i], stringX, y + 4, 0);
-		stringX += font->getCharWidth(msg[i]);
-	}
-
-	mac_markScreenAsDirty(x, y, w, h);
-	ScummEngine::drawDirtyScreenParts();
-#endif
-
 	Common::KeyState ks = Common::KEYCODE_INVALID;
 	bool leftBtnPressed = false, rightBtnPressed = false;
 	if (waitTime) {
@@ -328,18 +288,6 @@ Common::KeyState ScummEngine::mac_showOldStyleBannerAndPause(const char *msg, in
 	}
 
 	_macGui->undrawBanner();
-#if 0
-	// Restore the surfaces...
-	_textSurface.copyRectToSurface(backupTextSurface, x, y, Common::Rect(0, 0, w + 1, h));
-	_macScreen->copyRectToSurface(backupMacScreen, x, y, Common::Rect(0, 0, w + 1, h));
-
-	backupTextSurface.free();
-	backupMacScreen.free();
-
-	// Notify the gfx system that we restored the surfaces...
-	mac_markScreenAsDirty(x, y, w + 1, h);
-	ScummEngine::drawDirtyScreenParts();
-#endif
 
 	// Finally, resume the engine, clear the input state, and restore the charset.
 	pt.clear();
@@ -357,6 +305,11 @@ Common::KeyState ScummEngine::mac_showOldStyleBannerAndPause(const char *msg, in
 
 MacGui::MacGui(ScummEngine *vm, Common::String resourceFile) : _vm(vm), _system(_vm->_system), _surface(_vm->_macScreen), _resourceFile(resourceFile) {
 	_fonts.clear();
+
+	_bannerBounds.left = 70;
+	_bannerBounds.top = 189;
+	_bannerBounds.setWidth(500);
+	_bannerBounds.setHeight(22);
 }
 
 MacGui::~MacGui() {
@@ -459,21 +412,36 @@ void MacGui::updateWindowManager() {
 	_windowManager->draw();
 }
 
-void MacGui::drawBanner(byte *message) {
-	_bannerBounds.left = 70;
-	_bannerBounds.top = 189;
-	_bannerBounds.setWidth(499);
-	_bannerBounds.setHeight(22);
-
+void MacGui::drawBanner(char *message) {
 	if (!_backupSurface) {
 		_backupSurface = new Graphics::Surface();
 		_backupSurface->create(_bannerBounds.width(), _bannerBounds.height(), Graphics::PixelFormat::createFormatCLUT8());
 	}
 
 	_backupSurface->copyRectToSurface(*_surface, 0, 0, _bannerBounds);
-	_surface->fillRect(Common::Rect(_bannerBounds.left + 1, _bannerBounds.top + 1, _bannerBounds.right - 1, _bannerBounds.bottom - 1), kWhite);
-	drawBannerBorder(_bannerBounds.left, _bannerBounds.top, _bannerBounds.right - 1, _bannerBounds.bottom - 1, kBlack);
-	drawBannerBorder(_bannerBounds.left + 2, _bannerBounds.top + 2, _bannerBounds.right - 3, _bannerBounds.bottom - 3, kBlack);
+
+	Common::Rect r = _bannerBounds;
+	r.grow(-1);
+	_surface->fillRect(r, kWhite);
+
+	r = _bannerBounds;
+
+	for (int i = 0; i < 2; i++) {
+		_surface->hLine(r.left + 2, r.top, r.right - 3, kBlack);
+		_surface->hLine(r.left + 2, r.bottom - 1, r.right - 3, kBlack);
+		_surface->vLine(r.left, r.top + 2, r.bottom - 3, kBlack);
+		_surface->vLine(r.right - 1, r.top + 2, r.bottom - 3, kBlack);
+		_surface->setPixel(r.left + 1, r.top + 1, kBlack);
+		_surface->setPixel(r.left + 1, r.bottom - 2, kBlack);
+		_surface->setPixel(r.right - 2, r.top + 1, kBlack);
+		_surface->setPixel(r.right - 2, r.bottom - 2, kBlack);
+		r.grow(-2);
+	}
+
+	const Graphics::Font *font = getFont(_vm->_game.id == GID_INDY3 ? kIndy3FontMedium : kLoomFontMedium);
+
+	font->drawString(_surface, (char *)message, _bannerBounds.left, _bannerBounds.top + 4, _bannerBounds.width(), kBlack, Graphics::kTextAlignCenter);
+
 	_system->copyRectToScreen(_surface->getBasePtr(_bannerBounds.left, _bannerBounds.top), _surface->pitch, _bannerBounds.left, _bannerBounds.top, _bannerBounds.width(), _bannerBounds.height());
 }
 
@@ -489,17 +457,6 @@ void MacGui::undrawBanner() {
 	_backupSurface = nullptr;
 }
 
-void MacGui::drawBannerBorder(int x0, int y0, int x1, int y1, byte color) {
-	_surface->hLine(x0 + 2, y0, x1 - 2, color);
-	_surface->hLine(x0 + 2, y1 - 1, x1 - 2, color);
-	_surface->vLine(x0, y0 + 2, y1 - 3, color);
-	_surface->vLine(x1, y0 + 2, y1 - 3, color);
-	_surface->setPixel(x0 + 1, y0 + 1, color);
-	_surface->setPixel(x1 - 1, y0 + 1, color);
-	_surface->setPixel(x0 + 1, y1 - 2, color);
-	_surface->setPixel(x1 - 1, y1 - 2, color);
-}
-
 // ===========================================================================
 // The Mac Loom GUI. This one is pretty simple.
 // ===========================================================================
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 89dbec62b77..67d6703ee11 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -106,9 +106,8 @@ public:
 
 	void setPalette(const byte *palette, uint size);
 
-	void drawBanner(byte *message);
+	void drawBanner(char *message);
 	void undrawBanner();
-	void drawBannerBorder(int x, int y, int w, int h, byte color);
 };
 
 class MacLoomGui : public MacGui {
diff --git a/engines/scumm/input.cpp b/engines/scumm/input.cpp
index 87c59fb2e16..0e6a0d7c8bf 100644
--- a/engines/scumm/input.cpp
+++ b/engines/scumm/input.cpp
@@ -106,6 +106,10 @@ void ScummEngine_v80he::parseEvent(Common::Event event) {
 
 void ScummEngine::parseEvent(Common::Event event) {
 	// Handle Macintosh events before scaling the mouse coordinates.
+	//
+	// TODO: Don't allow menu while message banner is active. Don't allow
+	// message banner while menu is active.
+
 	if (_macGui && _macGui->handleEvent(event))
 		return;
 


Commit: 11b2197717da2cbb9ea8db408190580b9db3db8d
    https://github.com/scummvm/scummvm/commit/11b2197717da2cbb9ea8db408190580b9db3db8d
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Add callback for Mac GUI menus

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index e7bf82fcd5b..fb0053b1fcc 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -303,6 +303,10 @@ Common::KeyState ScummEngine::mac_showOldStyleBannerAndPause(const char *msg, in
 // Jones and the Last Crusade.
 // ===========================================================================
 
+static void menuCallback(int id, Common::String &name, void *data) {
+	((MacGui *)data)->handleMenu(id, name);
+}
+
 MacGui::MacGui(ScummEngine *vm, Common::String resourceFile) : _vm(vm), _system(_vm->_system), _surface(_vm->_macScreen), _resourceFile(resourceFile) {
 	_fonts.clear();
 
@@ -341,6 +345,8 @@ void MacGui::initialize() {
 	menu->addStaticMenus(menuSubItems);
 	menu->createSubMenuFromString(0, aboutMenuDef.c_str(), 0);
 
+	menu->setCommandsCallback(menuCallback, this);
+
 	for (int i = 129; i <= 130; i++) {
 		res = resource.getResource(MKTAG('M', 'E', 'N', 'U'), i);
 
@@ -356,6 +362,23 @@ void MacGui::initialize() {
 
 	resource.close();
 
+	// Assign sensible IDs to the menu items
+
+	int numberOfMenus = menu->numberOfMenus();
+
+	for (int i = 0; i < numberOfMenus; i++) {
+		Graphics::MacMenuItem *item = menu->getMenuItem(i);
+		int numberOfMenuItems = menu->numberOfMenuItems(item);
+		int id = 100 * (i + 1);
+		for (int j = 0; j < numberOfMenuItems; j++) {
+			Graphics::MacMenuItem *subItem = menu->getSubMenuItem(item, j);
+			Common::String name = menu->getName(subItem);
+
+			if (!name.empty())
+				menu->setAction(subItem, id++);
+		}
+	}
+
 	// Register custom fonts. The font family just happens to match the
 	// printed name of the game.
 
@@ -368,7 +391,6 @@ void MacGui::initialize() {
 	for (uint i = 0; i < fontFamilies.size(); i++) {
 		if (fontFamilies[i]->getName() == fontFamily) {
 			_gameFontId = _windowManager->_fontMan->registerFontName(fontFamily, fontFamilies[i]->getFontFamilyId());
-debug("_gameFontId = %d", _gameFontId);
 			break;
 		}
 	}
@@ -392,6 +414,15 @@ const Graphics::Font *MacGui::getFont(FontId fontId) {
 	return font;
 }
 
+bool MacGui::handleMenu(int id, Common::String &name) {
+	// This menu item (e.g. a menu separator) has no action, so it's
+	// handled trivially.
+	if (id == 0)
+		return true;
+
+	return false;
+}
+
 bool MacGui::handleEvent(Common::Event &event) {
 	return _windowManager->processEvent(event);
 }
@@ -529,6 +560,12 @@ void MacLoomGui::setupCursor(int &width, int &height, int &hotspotX, int &hotspo
 	delete curs;
 }
 
+bool MacLoomGui::handleMenu(int id, Common::String &name) {
+	if (MacGui::handleMenu(id, name))
+		return true;
+	return false;
+}
+
 // ===========================================================================
 // The Mac GUI for Indiana Jones and the Last Crusade, including the infamous
 // verb GUI.
@@ -1355,35 +1392,6 @@ void MacIndy3Gui::Inventory::ScrollButton::draw() {
 // the work to the individual widgets.
 // ---------------------------------------------------------------------------
 
-void MacIndy3Gui::setupCursor(int &width, int &height, int &hotspotX, int &hotspotY, int &animate) {
-	if (_windowManager->getCursorType() == Graphics::kMacCursorCustom)
-		return;
-
-	const byte buf[15 * 15] = {
-		3, 3, 3, 3, 3, 3, 0, 1, 0, 3, 3, 3, 3, 3, 3,
-		3, 3, 3, 3, 3, 3, 0, 1, 0, 3, 3, 3, 3, 3, 3,
-		3, 3, 3, 3, 3, 3, 0, 1, 0, 3, 3, 3, 3, 3, 3,
-		3, 3, 3, 3, 3, 3, 0, 1, 0, 3, 3, 3, 3, 3, 3,
-		3, 3, 3, 3, 3, 3, 0, 1, 0, 3, 3, 3, 3, 3, 3,
-		3, 3, 3, 3, 3, 3, 0, 1, 0, 3, 3, 3, 3, 3, 3,
-		0, 0, 0, 0, 0, 0, 3, 0, 3, 0, 0, 0, 0, 0, 0,
-		1, 1, 1, 1, 1, 1, 0, 3, 0, 1, 1, 1, 1, 1, 1,
-		0, 0, 0, 0, 0, 0, 3, 0, 3, 0, 0, 0, 0, 0, 0,
-		3, 3, 3, 3, 3, 3, 0, 1, 0, 3, 3, 3, 3, 3, 3,
-		3, 3, 3, 3, 3, 3, 0, 1, 0, 3, 3, 3, 3, 3, 3,
-		3, 3, 3, 3, 3, 3, 0, 1, 0, 3, 3, 3, 3, 3, 3,
-		3, 3, 3, 3, 3, 3, 0, 1, 0, 3, 3, 3, 3, 3, 3,
-		3, 3, 3, 3, 3, 3, 0, 1, 0, 3, 3, 3, 3, 3, 3,
-		3, 3, 3, 3, 3, 3, 0, 1, 0, 3, 3, 3, 3, 3, 3
-	};
-
-	width = height = 15;
-	hotspotX = hotspotY = 7;
-	animate = 0;
-
-	_windowManager->pushCustomCursor(buf, width, height, hotspotX, hotspotY, 3);
-}
-
 MacIndy3Gui::MacIndy3Gui(ScummEngine *vm, Common::String resourceFile) :
 	MacGui(vm, resourceFile), _visible(false) {
 
@@ -1435,6 +1443,35 @@ MacIndy3Gui::~MacIndy3Gui() {
 		delete it._value;
 }
 
+void MacIndy3Gui::setupCursor(int &width, int &height, int &hotspotX, int &hotspotY, int &animate) {
+	if (_windowManager->getCursorType() == Graphics::kMacCursorCustom)
+		return;
+
+	const byte buf[15 * 15] = {
+		3, 3, 3, 3, 3, 3, 0, 1, 0, 3, 3, 3, 3, 3, 3,
+		3, 3, 3, 3, 3, 3, 0, 1, 0, 3, 3, 3, 3, 3, 3,
+		3, 3, 3, 3, 3, 3, 0, 1, 0, 3, 3, 3, 3, 3, 3,
+		3, 3, 3, 3, 3, 3, 0, 1, 0, 3, 3, 3, 3, 3, 3,
+		3, 3, 3, 3, 3, 3, 0, 1, 0, 3, 3, 3, 3, 3, 3,
+		3, 3, 3, 3, 3, 3, 0, 1, 0, 3, 3, 3, 3, 3, 3,
+		0, 0, 0, 0, 0, 0, 3, 0, 3, 0, 0, 0, 0, 0, 0,
+		1, 1, 1, 1, 1, 1, 0, 3, 0, 1, 1, 1, 1, 1, 1,
+		0, 0, 0, 0, 0, 0, 3, 0, 3, 0, 0, 0, 0, 0, 0,
+		3, 3, 3, 3, 3, 3, 0, 1, 0, 3, 3, 3, 3, 3, 3,
+		3, 3, 3, 3, 3, 3, 0, 1, 0, 3, 3, 3, 3, 3, 3,
+		3, 3, 3, 3, 3, 3, 0, 1, 0, 3, 3, 3, 3, 3, 3,
+		3, 3, 3, 3, 3, 3, 0, 1, 0, 3, 3, 3, 3, 3, 3,
+		3, 3, 3, 3, 3, 3, 0, 1, 0, 3, 3, 3, 3, 3, 3,
+		3, 3, 3, 3, 3, 3, 0, 1, 0, 3, 3, 3, 3, 3, 3
+	};
+
+	width = height = 15;
+	hotspotX = hotspotY = 7;
+	animate = 0;
+
+	_windowManager->pushCustomCursor(buf, width, height, hotspotX, hotspotY, 3);
+}
+
 const Graphics::Font *MacIndy3Gui::getFontByScummId(int32 id) {
 	switch (id) {
 	case 0:
@@ -1485,6 +1522,12 @@ void MacIndy3Gui::getFontParams(FontId fontId, int &id, int &size, int &slant) {
 	}
 }
 
+bool MacIndy3Gui::handleMenu(int id, Common::String &name) {
+	if (MacGui::handleMenu(id, name))
+		return true;
+	return false;
+}
+
 // Before the GUI rewrite, the scroll offset was saved in variable 67. Let's
 // continue that tradition, just in case. If nothing else, it gives us an easy
 // way to still store it in savegames.
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 67d6703ee11..78fa86013eb 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -94,6 +94,8 @@ public:
 	virtual const Graphics::Font *getFontByScummId(int32 id) = 0;
 	virtual void getFontParams(FontId fontId, int &id, int &size, int &slant) = 0;
 
+	virtual bool handleMenu(int id, Common::String &name);
+
 	virtual bool isVerbGuiActive() const { return false; }
 	virtual void reset() {}
 	virtual void resetAfterLoad() {}
@@ -121,6 +123,8 @@ public:
 	void getFontParams(FontId fontId, int &id, int &size, int &slant);
 
 	void setupCursor(int &width, int &height, int &hotspotX, int &hotspotY, int &animate);
+
+	bool handleMenu(int id, Common::String &name);
 };
 
 class MacIndy3Gui : public MacGui {
@@ -140,6 +144,8 @@ public:
 
 	void setupCursor(int &width, int &height, int &hotspotX, int &hotspotY, int &animate);
 
+	bool handleMenu(int id, Common::String &name);
+
 	// There is a distinction between the GUI being allowed and being
 	// active. Allowed means that it's allowed to draw verbs, but not that
 	// it necessarily is. Active means that there are verbs on screen. From


Commit: 63f151dccf229d1859bdf563f67ef40b19471ec5
    https://github.com/scummvm/scummvm/commit/63f151dccf229d1859bdf563f67ef40b19471ec5
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Fix verb keys in Indy3 Mac GUI

Correctly exclude non-sticky keyboard modifiers.

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index fb0053b1fcc..087844a062a 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -749,7 +749,7 @@ bool MacIndy3Gui::Button::handleEvent(Common::Event &event) {
 	bool caughtEvent = false;
 
 	if (event.type == Common::EVENT_KEYDOWN) {
-		if (!event.kbd.hasFlags(Common::KBD_NON_STICKY) && event.kbd.keycode == vs->key)
+		if (!(event.kbd.flags & Common::KBD_NON_STICKY) && event.kbd.keycode == vs->key)
 			caughtEvent = true;
 	} else if (event.type == Common::EVENT_LBUTTONDOWN) {
 		if (_bounds.contains(event.mouse))


Commit: 077b331a8a17056234dbda3b4db6d1cbd7cda237
    https://github.com/scummvm/scummvm/commit/077b331a8a17056234dbda3b4db6d1cbd7cda237
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Fix crash in Macintosh black-and-white mode

It was trying to figure out the size of the largest glyph, but there is
now only one font to consider.

Changed paths:
    engines/scumm/charset.cpp


diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp
index 310ad8e81b1..d2a44fbaa01 100644
--- a/engines/scumm/charset.cpp
+++ b/engines/scumm/charset.cpp
@@ -1619,18 +1619,10 @@ CharsetRendererMac::CharsetRendererMac(ScummEngine *vm, const Common::String &fo
 	_glyphSurface = nullptr;
 
 	if (_vm->_renderMode == Common::kRenderMacintoshBW) {
-		int maxHeight = -1;
-		int maxWidth = -1;
-
-		for (int i = 0; i < 2; i++) {
-			const Graphics::Font *font = _vm->_macGui->getFontByScummId(i);
-
-			maxHeight = MAX(maxHeight, font->getFontHeight());
-			maxWidth = MAX(maxWidth, font->getMaxCharWidth());
-		}
+		const Graphics::Font *font = _vm->_macGui->getFontByScummId(0);
 
 		_glyphSurface = new Graphics::Surface();
-		_glyphSurface->create(maxWidth, maxHeight, Graphics::PixelFormat::createFormatCLUT8());
+		_glyphSurface->create(font->getFontHeight(), font->getMaxCharWidth(), Graphics::PixelFormat::createFormatCLUT8());
 	}
 }
 


Commit: 0e76018a109c033796da872adf28a0f7dbb6f297
    https://github.com/scummvm/scummvm/commit/0e76018a109c033796da872adf28a0f7dbb6f297
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
GRAPHICS: MACGUI: Handle menu shortcuts even when menus are hidden

Changed paths:
    graphics/macgui/macmenu.cpp


diff --git a/graphics/macgui/macmenu.cpp b/graphics/macgui/macmenu.cpp
index 53e23fad3a9..3f92fdb5cf5 100644
--- a/graphics/macgui/macmenu.cpp
+++ b/graphics/macgui/macmenu.cpp
@@ -1210,7 +1210,7 @@ void MacMenu::drawSubMenuArrow(ManagedSurface *dst, int x, int y, int color) {
 }
 
 bool MacMenu::processEvent(Common::Event &event) {
-	if (!_isVisible)
+	if (!_isVisible && event.type != Common::EVENT_KEYDOWN)
 		return false;
 
 	switch (event.type) {


Commit: 89bd9e7a22fd1e3e5683de3b41f476b326563960
    https://github.com/scummvm/scummvm/commit/89bd9e7a22fd1e3e5683de3b41f476b326563960
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: More Mac GUI cleanups and experimenting

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h
    engines/scumm/scumm.cpp
    engines/scumm/scumm.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 087844a062a..59a60ce9113 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -22,6 +22,7 @@
 #include "common/system.h"
 #include "common/macresman.h"
 
+#include "graphics/cursorman.h"
 #include "graphics/maccursor.h"
 #include "graphics/macega.h"
 #include "graphics/fonts/macfont.h"
@@ -253,17 +254,6 @@ void ScummEngine::mac_undrawIndy3CreditsText() {
 	restoreCharsetBg();
 }
 
-void ScummEngine::mac_drawBorder(int x, int y, int w, int h, byte color) {
-	_macScreen->hLine(x + 2, y, x + w - 2, 0);
-	_macScreen->hLine(x + 2, y + h - 1, x + w - 2, 0);
-	_macScreen->vLine(x, y + 2, y + h - 3, 0);
-	_macScreen->vLine(x + w, y + 2, y + h - 3, 0);
-	_macScreen->setPixel(x + 1, y + 1, 0);
-	_macScreen->setPixel(x + w - 1, y + 1, 0);
-	_macScreen->setPixel(x + 1, y + h - 2, 0);
-	_macScreen->setPixel(x + w - 1, y + h - 2, 0);
-}
-
 Common::KeyState ScummEngine::mac_showOldStyleBannerAndPause(const char *msg, int32 waitTime) {
 	char bannerMsg[512];
 
@@ -272,7 +262,7 @@ Common::KeyState ScummEngine::mac_showOldStyleBannerAndPause(const char *msg, in
 	// Fetch the translated string for the message...
 	convertMessageToString((const byte *)msg, (byte *)bannerMsg, sizeof(bannerMsg));
 
-	_macGui->drawBanner(bannerMsg);
+	MacGui::SimpleWindow *window = _macGui->drawBanner(bannerMsg);
 
 	// Pause shake effect
 	_shakeTempSavedState = _shakeEnabled;
@@ -287,7 +277,7 @@ Common::KeyState ScummEngine::mac_showOldStyleBannerAndPause(const char *msg, in
 		waitForBannerInput(waitTime, ks, leftBtnPressed, rightBtnPressed);
 	}
 
-	_macGui->undrawBanner();
+	delete window;
 
 	// Finally, resume the engine, clear the input state, and restore the charset.
 	pt.clear();
@@ -298,6 +288,55 @@ Common::KeyState ScummEngine::mac_showOldStyleBannerAndPause(const char *msg, in
 	return ks;
 }
 
+// Very simple window class
+
+MacGui::SimpleWindow::SimpleWindow(OSystem *system, Graphics::Surface *from, Common::Rect bounds, SimpleWindowStyle style) : _system(system), _from(from), _bounds(bounds) {
+	_backup = new Graphics::Surface();
+	_backup->create(_bounds.width(), _bounds.height(), Graphics::PixelFormat::createFormatCLUT8());
+	_backup->copyRectToSurface(*_from, 0, 0, _bounds);
+	_surface = _from->getSubArea(bounds);
+
+	Graphics::Surface *s = surface();
+	Common::Rect r = Common::Rect(0, 0, s->w, s->h);
+
+	r.grow(-1);
+	s->fillRect(r, kWhite);
+
+	if (style == MacGui::kStyleNormal) {
+		int growths[] = { 1, -2, -1 };
+
+		for (int i = 0; i < ARRAYSIZE(growths); i++) {
+			r.grow(growths[i]);
+
+			s->hLine(r.left, r.top, r.right - 1, kBlack);
+			s->hLine(r.left, r.bottom - 1, r.right - 1, kBlack);
+			s->hLine(r.left, r.top + 1, r.bottom - 2, kBlack);
+			s->hLine(r.right - 1, r.top + 1, r.bottom - 2, kBlack);
+		}
+	} else if (style == MacGui::kStyleRounded) {
+		r.grow(1);
+
+		for (int i = 0; i < 2; i++) {
+			s->hLine(r.left + 2, r.top, r.right - 3, kBlack);
+			s->hLine(r.left + 2, r.bottom - 1, r.right - 3, kBlack);
+			s->vLine(r.left, r.top + 2, r.bottom - 3, kBlack);
+			s->vLine(r.right - 1, r.top + 2, r.bottom - 3, kBlack);
+			s->setPixel(r.left + 1, r.top + 1, kBlack);
+			s->setPixel(r.left + 1, r.bottom - 2, kBlack);
+			s->setPixel(r.right - 2, r.top + 1, kBlack);
+			s->setPixel(r.right - 2, r.bottom - 2, kBlack);
+			r.grow(-2);
+		}
+	}
+}
+
+MacGui::SimpleWindow::~SimpleWindow() {
+	_from->copyRectToSurface(*_backup, _bounds.left, _bounds.top, Common::Rect(0, 0, _bounds.width(), _bounds.height()));
+	_system->copyRectToScreen(_from->getBasePtr(_bounds.left, _bounds.top), _from->pitch, _bounds.left, _bounds.top, _bounds.width(), _bounds.height());
+	_backup->free();
+	delete _backup;
+}
+
 // ===========================================================================
 // Macintosh user interface for the Macintosh versions of Loom and Indiana
 // Jones and the Last Crusade.
@@ -309,20 +348,14 @@ static void menuCallback(int id, Common::String &name, void *data) {
 
 MacGui::MacGui(ScummEngine *vm, Common::String resourceFile) : _vm(vm), _system(_vm->_system), _surface(_vm->_macScreen), _resourceFile(resourceFile) {
 	_fonts.clear();
-
-	_bannerBounds.left = 70;
-	_bannerBounds.top = 189;
-	_bannerBounds.setWidth(500);
-	_bannerBounds.setHeight(22);
 }
 
 MacGui::~MacGui() {
 	delete _windowManager;
-	delete _backupSurface;
 }
 
 void MacGui::initialize() {
-	_windowManager = new Graphics::MacWindowManager(Graphics::kWMModeNoDesktop | Graphics::kWMModeAutohideMenu | Graphics::kWMModalMenuMode);
+	_windowManager = new Graphics::MacWindowManager(Graphics::kWMModeNoDesktop | Graphics::kWMModeAutohideMenu | Graphics::kWMModalMenuMode | Graphics::kWMModeNoCursorOverride);
 	_windowManager->setEngine(_vm);
 	_windowManager->setScreen(640, 400);
 	_windowManager->setMenuHotzone(Common::Rect(0, 0, 640, 23));
@@ -420,7 +453,50 @@ bool MacGui::handleMenu(int id, Common::String &name) {
 	if (id == 0)
 		return true;
 
-	return false;
+	_windowManager->getMenu()->closeMenu();
+
+	Common::Rect bounds(100, 100, 350, 200);
+	SimpleWindow *window = drawWindow(bounds);
+
+	_windowManager->pushCursor(Graphics::kMacCursorArrow, nullptr);
+
+	_system->copyRectToScreen(_surface->getBasePtr(100, 100), _surface->pitch, bounds.left, bounds.top, bounds.width(), bounds.height());
+	_system->updateScreen();
+
+	bool shouldQuit = false;
+	bool shouldQuitEngine = false;
+
+	while (!shouldQuit) {
+		Common::Event event;
+
+		while (_system->getEventManager()->pollEvent(event)) {
+			switch (event.type) {
+			case Common::EVENT_QUIT:
+				shouldQuit = true;
+				shouldQuitEngine = true;
+				break;
+
+			case Common::EVENT_LBUTTONDOWN:
+				shouldQuit = true;
+				break;
+
+			default:
+				break;
+			}
+
+			_system->updateScreen();
+			_system->delayMillis(10);
+		}
+	}
+
+	_windowManager->popCursor();
+
+	delete window;
+
+	if (shouldQuitEngine)
+		debug("Quit everything");
+
+	return true;
 }
 
 bool MacGui::handleEvent(Common::Event &event) {
@@ -432,60 +508,75 @@ void MacGui::setPalette(const byte *palette, uint size) {
 }
 
 void MacGui::updateWindowManager() {
-	if (_windowManager->isMenuActive()) {
-		if (_windowManager->getCursorType() == Graphics::kMacCursorCustom)
-			_windowManager->pushCursor(Graphics::kMacCursorArrow, nullptr);
+	// TODO: Originally, the left Alt button opens the menu. In ScummVM,
+	//       it triggers on mouse-over. Preferrably both should work (the
+	//       latter should work better for touch screens), but it's hard
+	//       to get them to coexist.
+
+	// We want the arrow cursor for menus. Note that the menu triggers even
+	// when the mouse is invisible, which may or may not be a bug. But the
+	// original did allow you to open the menu with Alt even when the
+	// cursor was visible, so for now it's a feature.
+
+	bool isActive = _windowManager->isMenuActive();
+
+	if (isActive) {
+		if (!_menuIsActive) {
+			_cursorWasVisible = CursorMan.showMouse(true);
+			_windowManager->pushCursor(Graphics::kMacCursorArrow);
+		}
 	} else {
-		if (_windowManager->getCursorType() == Graphics::kMacCursorArrow)
+		if (_menuIsActive) {
 			_windowManager->popCursor();
+			CursorMan.showMouse(_cursorWasVisible);
+		}
 	}
 
+	_menuIsActive = isActive;
 	_windowManager->draw();
 }
 
-void MacGui::drawBanner(char *message) {
-	if (!_backupSurface) {
-		_backupSurface = new Graphics::Surface();
-		_backupSurface->create(_bannerBounds.width(), _bannerBounds.height(), Graphics::PixelFormat::createFormatCLUT8());
-	}
-
-	_backupSurface->copyRectToSurface(*_surface, 0, 0, _bannerBounds);
+MacGui::SimpleWindow *MacGui::drawBanner(char *message) {
+	Common::Rect bounds(70, 189, 570, 211);
 
-	Common::Rect r = _bannerBounds;
-	r.grow(-1);
-	_surface->fillRect(r, kWhite);
-
-	r = _bannerBounds;
-
-	for (int i = 0; i < 2; i++) {
-		_surface->hLine(r.left + 2, r.top, r.right - 3, kBlack);
-		_surface->hLine(r.left + 2, r.bottom - 1, r.right - 3, kBlack);
-		_surface->vLine(r.left, r.top + 2, r.bottom - 3, kBlack);
-		_surface->vLine(r.right - 1, r.top + 2, r.bottom - 3, kBlack);
-		_surface->setPixel(r.left + 1, r.top + 1, kBlack);
-		_surface->setPixel(r.left + 1, r.bottom - 2, kBlack);
-		_surface->setPixel(r.right - 2, r.top + 1, kBlack);
-		_surface->setPixel(r.right - 2, r.bottom - 2, kBlack);
-		r.grow(-2);
-	}
+	MacGui::SimpleWindow *window = new SimpleWindow(_system, _surface, bounds, MacGui::kStyleRounded);
 
 	const Graphics::Font *font = getFont(_vm->_game.id == GID_INDY3 ? kIndy3FontMedium : kLoomFontMedium);
 
-	font->drawString(_surface, (char *)message, _bannerBounds.left, _bannerBounds.top + 4, _bannerBounds.width(), kBlack, Graphics::kTextAlignCenter);
+	font->drawString(_surface, (char *)message, bounds.left, bounds.top + 4, bounds.width(), kBlack, Graphics::kTextAlignCenter);
+
+	_system->copyRectToScreen(_surface->getBasePtr(bounds.left, bounds.top), _surface->pitch, bounds.left, bounds.top, bounds.width(), bounds.height());
 
-	_system->copyRectToScreen(_surface->getBasePtr(_bannerBounds.left, _bannerBounds.top), _surface->pitch, _bannerBounds.left, _bannerBounds.top, _bannerBounds.width(), _bannerBounds.height());
+	return window;
 }
 
-void MacGui::undrawBanner() {
-	if (!_backupSurface)
-		return;
+MacGui::SimpleWindow *MacGui::drawWindow(Common::Rect bounds) {
+	MacGui::SimpleWindow *window = new SimpleWindow(_system, _surface, bounds);
+
+	_surface->hLine(bounds.left, bounds.top, bounds.right - 1, kBlack);
+	_surface->hLine(bounds.left, bounds.bottom - 1, bounds.right - 1, kBlack);
+	_surface->vLine(bounds.left, bounds.top, bounds.bottom - 1, kBlack);
+	_surface->vLine(bounds.right - 1, bounds.top, bounds.bottom - 1, kBlack);
+
+	bounds.grow(-1);
+
+	_surface->fillRect(bounds, kWhite);
+
+	bounds.grow(-2);
+
+	_surface->hLine(bounds.left, bounds.top, bounds.right - 1, kBlack);
+	_surface->hLine(bounds.left, bounds.bottom - 1, bounds.right - 1, kBlack);
+	_surface->vLine(bounds.left, bounds.top, bounds.bottom - 1, kBlack);
+	_surface->vLine(bounds.right - 1, bounds.top, bounds.bottom - 1, kBlack);
+
+	bounds.grow(-1);
 
-	_surface->copyRectToSurface(*_backupSurface, _bannerBounds.left, _bannerBounds.top, Common::Rect(0, 0, _bannerBounds.width(), _bannerBounds.height()));
-	_system->copyRectToScreen(_surface->getBasePtr(_bannerBounds.left, _bannerBounds.top), _surface->pitch, _bannerBounds.left, _bannerBounds.top, _bannerBounds.width(), _bannerBounds.height());
+	_surface->hLine(bounds.left, bounds.top, bounds.right - 1, kBlack);
+	_surface->hLine(bounds.left, bounds.bottom - 1, bounds.right - 1, kBlack);
+	_surface->vLine(bounds.left, bounds.top, bounds.bottom - 1, kBlack);
+	_surface->vLine(bounds.right - 1, bounds.top, bounds.bottom - 1, kBlack);
 
-	_backupSurface->free();
-	delete _backupSurface;
-	_backupSurface = nullptr;
+	return window;
 }
 
 // ===========================================================================
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 78fa86013eb..d38e44c4ac2 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -43,12 +43,12 @@ protected:
 	Graphics::Surface *_surface = nullptr;
 	Common::String _resourceFile;
 
+	bool _menuIsActive = false;
+	bool _cursorWasVisible = false;
+
 	Common::HashMap<int, const Graphics::Font *> _fonts;
 	int _gameFontId = -1;
 
-	Common::Rect _bannerBounds;
-	Graphics::Surface *_backupSurface = nullptr;
-
 	enum Color {
 		kBlack = 0,
 		kBlue = 1,
@@ -71,6 +71,26 @@ protected:
 	};
 
 public:
+	enum SimpleWindowStyle {
+		kStyleNormal,
+		kStyleRounded
+	};
+
+	class SimpleWindow {
+	private:
+		OSystem *_system;
+		Common::Rect _bounds;
+		Graphics::Surface *_from = nullptr;
+		Graphics::Surface *_backup = nullptr;
+		Graphics::Surface _surface;
+
+	public:
+		Graphics::Surface *surface() { return &_surface; }
+		void close();
+		SimpleWindow(OSystem *system, Graphics::Surface *from, Common::Rect bounds, SimpleWindowStyle = kStyleNormal);
+		~SimpleWindow();
+	};
+
 	enum FontId {
 		kIndy3FontSmall,
 		kIndy3FontMedium,
@@ -108,13 +128,14 @@ public:
 
 	void setPalette(const byte *palette, uint size);
 
-	void drawBanner(char *message);
-	void undrawBanner();
+	SimpleWindow *drawBanner(char *message);
+
+	SimpleWindow *drawWindow(Common::Rect bounds);
 };
 
 class MacLoomGui : public MacGui {
 public:
-	MacLoomGui(OSystem *system, ScummEngine *vm, Common::String resourceFile) : MacGui(vm, resourceFile) {}
+	MacLoomGui(ScummEngine *vm, Common::String resourceFile) : MacGui(vm, resourceFile) {}
 	~MacLoomGui() {}
 
 	const Common::String name() const { return "Loom"; }
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 1e53bc9f3d6..d6316b41473 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -1197,7 +1197,7 @@ Common::Error ScummEngine::init() {
 					_textSurfaceMultiplier = 2;
 					_macScreen = new Graphics::Surface();
 					_macScreen->create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
-					_macGui = new MacLoomGui(_system, this, macResourceFile);
+					_macGui = new MacLoomGui(this, macResourceFile);
 					break;
 				}
 			}
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index 84871ba743b..92398aa50d5 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -1407,7 +1407,6 @@ protected:
 	void mac_drawIndy3TextBox();
 	void mac_undrawIndy3TextBox();
 	void mac_undrawIndy3CreditsText();
-	void mac_drawBorder(int x, int y, int w, int h, byte color);
 	Common::KeyState mac_showOldStyleBannerAndPause(const char *msg, int32 waitTime);
 
 	const byte *postProcessDOSGraphics(VirtScreen *vs, int &pitch, int &x, int &y, int &width, int &height) const;


Commit: d63ad4c2e3d70200a778b3ece3b623729ae42861
    https://github.com/scummvm/scummvm/commit/d63ad4c2e3d70200a778b3ece3b623729ae42861
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
GRAPHICS: MACGUI: Don't filter keypresses in processMenuShortCut()

This is already done in keyEvent(), which is the only place where
processMenuShortCut() is called. This allows using Alt-<key> as
shortcut, which is consistent with how the Mac emulators I've tried does
it.

Changed paths:
    graphics/macgui/macmenu.cpp
    graphics/macgui/macmenu.h


diff --git a/graphics/macgui/macmenu.cpp b/graphics/macgui/macmenu.cpp
index 3f92fdb5cf5..ceee71b7761 100644
--- a/graphics/macgui/macmenu.cpp
+++ b/graphics/macgui/macmenu.cpp
@@ -1236,7 +1236,7 @@ bool MacMenu::keyEvent(Common::Event &event) {
 
 	if (event.kbd.flags & (Common::KBD_ALT | Common::KBD_CTRL | Common::KBD_META)) {
 		if (event.kbd.ascii >= 0x20 && event.kbd.ascii <= 0x7f) {
-			return processMenuShortCut(event.kbd.flags, event.kbd.ascii);
+			return processMenuShortCut(event.kbd.ascii);
 		}
 	}
 
@@ -1471,23 +1471,23 @@ bool MacMenu::mouseRelease(int x, int y) {
 	return true;
 }
 
-bool MacMenu::processMenuShortCut(byte flags, uint16 ascii) {
+bool MacMenu::processMenuShortCut(uint16 ascii) {
 	ascii = tolower(ascii);
 
-	if (flags & (Common::KBD_CTRL | Common::KBD_META)) {
-		for (uint i = 0; i < _items.size(); i++)
-			if (_items[i]->submenu != nullptr) {
-				for (uint j = 0; j < _items[i]->submenu->items.size(); j++)
-					if (_items[i]->submenu->items[j]->enabled && tolower(_items[i]->submenu->items[j]->shortcut) == ascii) {
-						if (_items[i]->submenu->items[j]->unicode) {
-							if (checkCallback(true))
-								(*_unicodeccallback)(_items[i]->submenu->items[j]->action, _items[i]->submenu->items[j]->unicodeText, _cdata);
-						} else {
-							if (checkCallback())
-								(*_ccallback)(_items[i]->submenu->items[j]->action, _items[i]->submenu->items[j]->text, _cdata);
-						}
-						return true;
+	for (uint i = 0; i < _items.size(); i++) {
+		if (_items[i]->submenu != nullptr) {
+			for (uint j = 0; j < _items[i]->submenu->items.size(); j++) {
+				if (_items[i]->submenu->items[j]->enabled && tolower(_items[i]->submenu->items[j]->shortcut) == ascii) {
+					if (_items[i]->submenu->items[j]->unicode) {
+						if (checkCallback(true))
+							(*_unicodeccallback)(_items[i]->submenu->items[j]->action, _items[i]->submenu->items[j]->unicodeText, _cdata);
+					} else {
+						if (checkCallback())
+							(*_ccallback)(_items[i]->submenu->items[j]->action, _items[i]->submenu->items[j]->text, _cdata);
 					}
+					return true;
+				}
+			}
 		}
 	}
 
diff --git a/graphics/macgui/macmenu.h b/graphics/macgui/macmenu.h
index 455ce5a1c33..be27d80b604 100644
--- a/graphics/macgui/macmenu.h
+++ b/graphics/macgui/macmenu.h
@@ -202,7 +202,7 @@ private:
 	bool mouseRelease(int x, int y);
 	bool mouseMove(int x, int y);
 
-	bool processMenuShortCut(byte flags, uint16 ascii);
+	bool processMenuShortCut(uint16 ascii);
 
 	void drawSubMenuArrow(ManagedSurface *dst, int x, int y, int color);
 	bool contains(int x, int y);


Commit: 0022f3fc93a85439ad38a6a3612f0df698ff4b29
    https://github.com/scummvm/scummvm/commit/0022f3fc93a85439ad38a6a3612f0df698ff4b29
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Some more work on Mac GUI windows

I don't think I'll be able to use the pre-existing Mac dialog class
because it seems too tailored to the Wage engine. So at least for now
I'm making my own.

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 59a60ce9113..7ec58154ec5 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -331,12 +331,20 @@ MacGui::SimpleWindow::SimpleWindow(OSystem *system, Graphics::Surface *from, Com
 }
 
 MacGui::SimpleWindow::~SimpleWindow() {
-	_from->copyRectToSurface(*_backup, _bounds.left, _bounds.top, Common::Rect(0, 0, _bounds.width(), _bounds.height()));
-	_system->copyRectToScreen(_from->getBasePtr(_bounds.left, _bounds.top), _from->pitch, _bounds.left, _bounds.top, _bounds.width(), _bounds.height());
+	copyToScreen(_backup);
 	_backup->free();
 	delete _backup;
 }
 
+void MacGui::SimpleWindow::copyToScreen(Graphics::Surface *s) {
+	_from->copyRectToSurface(*s, _bounds.left, _bounds.top, Common::Rect(0, 0, _bounds.width(), _bounds.height()));
+	_system->copyRectToScreen(_from->getBasePtr(_bounds.left, _bounds.top), _from->pitch, _bounds.left, _bounds.top, _bounds.width(), _bounds.height());
+}
+
+void MacGui::SimpleWindow::show() {
+	copyToScreen(surface());
+}
+
 // ===========================================================================
 // Macintosh user interface for the Macintosh versions of Loom and Indiana
 // Jones and the Last Crusade.
@@ -439,7 +447,18 @@ const Graphics::Font *MacGui::getFont(FontId fontId) {
 	int size;
 	int slant;
 
-	getFontParams(fontId, id, size, slant);
+	switch (fontId) {
+	case kSystemFont:
+		id = Graphics::kMacFontChicago;
+		size = 12;
+		slant = Graphics::kMacFontRegular;
+		break;
+
+	default:
+		getFontParams(fontId, id, size, slant);
+		break;
+	}
+
 
 	font = _windowManager->_fontMan->getFont(Graphics::MacFont(id, size, slant));
 	_fonts[(int)fontId] = font;
@@ -455,47 +474,10 @@ bool MacGui::handleMenu(int id, Common::String &name) {
 
 	_windowManager->getMenu()->closeMenu();
 
-	Common::Rect bounds(100, 100, 350, 200);
-	SimpleWindow *window = drawWindow(bounds);
-
-	_windowManager->pushCursor(Graphics::kMacCursorArrow, nullptr);
-
-	_system->copyRectToScreen(_surface->getBasePtr(100, 100), _surface->pitch, bounds.left, bounds.top, bounds.width(), bounds.height());
-	_system->updateScreen();
-
-	bool shouldQuit = false;
-	bool shouldQuitEngine = false;
-
-	while (!shouldQuit) {
-		Common::Event event;
-
-		while (_system->getEventManager()->pollEvent(event)) {
-			switch (event.type) {
-			case Common::EVENT_QUIT:
-				shouldQuit = true;
-				shouldQuitEngine = true;
-				break;
-
-			case Common::EVENT_LBUTTONDOWN:
-				shouldQuit = true;
-				break;
-
-			default:
-				break;
-			}
-
-			_system->updateScreen();
-			_system->delayMillis(10);
-		}
+	if (id == 100) {
+		showAboutDialog();
 	}
 
-	_windowManager->popCursor();
-
-	delete window;
-
-	if (shouldQuitEngine)
-		debug("Quit everything");
-
 	return true;
 }
 
@@ -657,6 +639,9 @@ bool MacLoomGui::handleMenu(int id, Common::String &name) {
 	return false;
 }
 
+void MacLoomGui::showAboutDialog() {
+}
+
 // ===========================================================================
 // The Mac GUI for Indiana Jones and the Last Crusade, including the infamous
 // verb GUI.
@@ -1619,6 +1604,71 @@ bool MacIndy3Gui::handleMenu(int id, Common::String &name) {
 	return false;
 }
 
+void MacIndy3Gui::showAboutDialog() {
+	// The About window is not a a dialog resource. Its size appears to be
+	// hard-coded (416x166), and it's drawn centered. The graphics are in
+	// PICT 2000.
+
+	int width = 416;
+	int height = 166;
+	int x = (640 - width) / 2;
+	int y = (400 - height) / 2;
+
+	Common::Rect bounds(x, y, x + width, y + height);
+
+	SimpleWindow *window = drawWindow(bounds);
+
+	Common::MacResManager resource;
+
+	resource.open(_resourceFile);
+
+	Common::SeekableReadStream *res = resource.getResource(MKTAG('P', 'I', 'C', 'T'), 2000);
+
+	resource.close();
+
+#if 0
+	const Graphics::Font *font = getFont(kSystemFont);
+	font->drawString(window->surface(), "Another mock-up? Seriously?", 0, 40, 250, kBlack, Graphics::kTextAlignCenter);
+#endif
+
+	_windowManager->pushCursor(Graphics::kMacCursorArrow, nullptr);
+
+	window->show();
+
+	bool shouldQuit = false;
+	bool shouldQuitEngine = false;
+
+	while (!shouldQuit) {
+		Common::Event event;
+
+		while (_system->getEventManager()->pollEvent(event)) {
+			switch (event.type) {
+			case Common::EVENT_QUIT:
+				shouldQuit = true;
+				shouldQuitEngine = true;
+				break;
+
+			case Common::EVENT_LBUTTONDOWN:
+				shouldQuit = true;
+				break;
+
+			default:
+				break;
+			}
+		}
+
+		_system->updateScreen();
+		_system->delayMillis(10);
+	}
+
+	_windowManager->popCursor();
+
+	delete window;
+
+	if (shouldQuitEngine)
+		debug("Quit everything");
+}
+
 // Before the GUI rewrite, the scroll offset was saved in variable 67. Let's
 // continue that tradition, just in case. If nothing else, it gives us an easy
 // way to still store it in savegames.
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index d38e44c4ac2..eceb04d3d96 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -84,14 +84,17 @@ public:
 		Graphics::Surface *_backup = nullptr;
 		Graphics::Surface _surface;
 
+		void copyToScreen(Graphics::Surface *s);
 	public:
 		Graphics::Surface *surface() { return &_surface; }
-		void close();
+		void show();
 		SimpleWindow(OSystem *system, Graphics::Surface *from, Common::Rect bounds, SimpleWindowStyle = kStyleNormal);
 		~SimpleWindow();
 	};
 
 	enum FontId {
+		kSystemFont,
+
 		kIndy3FontSmall,
 		kIndy3FontMedium,
 		kIndy3VerbFontRegular,
@@ -116,6 +119,8 @@ public:
 
 	virtual bool handleMenu(int id, Common::String &name);
 
+	virtual void showAboutDialog() = 0;
+
 	virtual bool isVerbGuiActive() const { return false; }
 	virtual void reset() {}
 	virtual void resetAfterLoad() {}
@@ -146,6 +151,8 @@ public:
 	void setupCursor(int &width, int &height, int &hotspotX, int &hotspotY, int &animate);
 
 	bool handleMenu(int id, Common::String &name);
+
+	void showAboutDialog();
 };
 
 class MacIndy3Gui : public MacGui {
@@ -167,6 +174,8 @@ public:
 
 	bool handleMenu(int id, Common::String &name);
 
+	void showAboutDialog();
+
 	// There is a distinction between the GUI being allowed and being
 	// active. Allowed means that it's allowed to draw verbs, but not that
 	// it necessarily is. Active means that there are verbs on screen. From
@@ -262,7 +271,6 @@ private:
 		bool _kill = false;
 
 	public:
-
 		VerbWidget(int x, int y, int width, int height) : Widget(x, y, width, height) {}
 
 		void setVerbid(int n) { _verbid = n; }


Commit: 49ccadc6bdb428a2e5ff1f4951f92362a6a523a9
    https://github.com/scummvm/scummvm/commit/49ccadc6bdb428a2e5ff1f4951f92362a6a523a9
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Add minimal PICT v1 decoder to Mac GUI

At least until I know exactly how tiny a subset of PICT v1 I need, it's
easier to keep the code here. It needs to be cleaned up, but it works
well enough for the one picture I've tried it on.

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 7ec58154ec5..adc5824c49b 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -337,12 +337,14 @@ MacGui::SimpleWindow::~SimpleWindow() {
 }
 
 void MacGui::SimpleWindow::copyToScreen(Graphics::Surface *s) {
-	_from->copyRectToSurface(*s, _bounds.left, _bounds.top, Common::Rect(0, 0, _bounds.width(), _bounds.height()));
+	if (s) {
+		_from->copyRectToSurface(*s, _bounds.left, _bounds.top, Common::Rect(0, 0, _bounds.width(), _bounds.height()));
+	}
 	_system->copyRectToScreen(_from->getBasePtr(_bounds.left, _bounds.top), _from->pitch, _bounds.left, _bounds.top, _bounds.width(), _bounds.height());
 }
 
 void MacGui::SimpleWindow::show() {
-	copyToScreen(surface());
+	copyToScreen();
 }
 
 // ===========================================================================
@@ -466,6 +468,128 @@ const Graphics::Font *MacGui::getFont(FontId fontId) {
 	return font;
 }
 
+const Graphics::Surface *MacGui::loadPICT(int id) {
+	Common::MacResManager resource;
+
+	resource.open(_resourceFile);
+
+	Common::SeekableReadStream *res = resource.getResource(MKTAG('P', 'I', 'C', 'T'), id);
+
+	// The PICT decoder is mainly for v2 pictures, so at least for now we
+	// don't use it. The images we need to decode are simple bitmaps
+	// anyway.
+
+	uint16 size = res->readUint16BE();
+
+	uint16 top = res->readUint16BE();
+	uint16 left = res->readUint16BE();
+	uint16 bottom = res->readUint16BE();
+	uint16 right = res->readUint16BE();
+
+	Graphics::Surface *s = new Graphics::Surface();
+	s->create(right - left, bottom - top, Graphics::PixelFormat::createFormatCLUT8());
+	s->fillRect(Common::Rect(left, top, right, bottom), kWhite);
+
+	bool endOfPicture = false;
+
+	while (!endOfPicture) {
+		byte opcode = res->readByte();
+		byte value;
+		int x1, x2, y1, y2;
+		int rowBytes;
+		int mode;
+
+		int x = 0;
+		int y = 0;
+
+		switch (opcode) {
+		case 0x01: // clipRgn
+			// The clip region is not important
+			size = res->readUint16BE();
+			assert(size == 10);
+			res->skip(8);
+			break;
+
+		case 0x11: // picVersion
+			value = res->readByte();
+			assert(value == 1);
+			break;
+
+		case 0x99: // PackBitsRgn
+			rowBytes = res->readUint16BE();
+			y1 = res->readSint16BE();
+			x1 = res->readSint16BE();
+			y2 = res->readSint16BE();
+			x2 = res->readSint16BE();
+
+			// srcRect isn't interesting
+			res->skip(8);
+
+			// dstRect isn't interesting
+			res->skip(8);
+
+			mode = res->readUint16BE();
+
+			// maskRgn isn't interesting
+			size = res->readUint16BE();
+			assert(size == 10);
+			res->skip(size - 2);
+
+			for (y = y1; y < y2; y++) {
+				size = res->readByte();
+
+				x = x1;
+				int bytesRead = 0;
+
+				while (bytesRead < size) {
+					byte count = res->readByte();
+					bytesRead++;
+					if (count >= 128) {
+						count = 256 - count;
+						int b = res->readByte();
+						bytesRead++;
+						for (int j = 0; j <= count; j++) {
+							for (int k = 7; k >= 0; k--) {
+								if (b & (1 << k))
+									s->setPixel(x, y, kBlack);
+								x++;
+							}
+						}
+					} else {
+						for (int j = 0; j <= count; j++) {
+							int b = res->readByte();
+							bytesRead++;
+							for (int k = 7; k >= 0; k--) {
+								if (b & (1 << k))
+									s->setPixel(x, y, kBlack);
+								x++;
+							}
+						}
+					}
+				}
+			}
+
+			break;
+
+		case 0xA0: // shortComment
+			res->skip(2);
+			break;
+
+		case 0xFF: // EndOfPicture
+			endOfPicture = true;
+			break;
+
+		default:
+			debug("Unknown opcode: 0x%02x", opcode);
+			break;
+		}
+	}
+
+	resource.close();
+
+	return s;
+}
+
 bool MacGui::handleMenu(int id, Common::String &name) {
 	// This menu item (e.g. a menu separator) has no action, so it's
 	// handled trivially.
@@ -476,9 +600,10 @@ bool MacGui::handleMenu(int id, Common::String &name) {
 
 	if (id == 100) {
 		showAboutDialog();
+		return true;
 	}
 
-	return true;
+	return false;
 }
 
 bool MacGui::handleEvent(Common::Event &event) {
@@ -1609,8 +1734,13 @@ void MacIndy3Gui::showAboutDialog() {
 	// hard-coded (416x166), and it's drawn centered. The graphics are in
 	// PICT 2000.
 
+#if 0
 	int width = 416;
 	int height = 166;
+#else
+	int width = 540;
+	int height = 105;
+#endif
 	int x = (640 - width) / 2;
 	int y = (400 - height) / 2;
 
@@ -1618,18 +1748,9 @@ void MacIndy3Gui::showAboutDialog() {
 
 	SimpleWindow *window = drawWindow(bounds);
 
-	Common::MacResManager resource;
-
-	resource.open(_resourceFile);
+	const Graphics::Surface *pict = loadPICT(2000);
 
-	Common::SeekableReadStream *res = resource.getResource(MKTAG('P', 'I', 'C', 'T'), 2000);
-
-	resource.close();
-
-#if 0
-	const Graphics::Font *font = getFont(kSystemFont);
-	font->drawString(window->surface(), "Another mock-up? Seriously?", 0, 40, 250, kBlack, Graphics::kTextAlignCenter);
-#endif
+	window->surface()->copyRectToSurface(*pict, 6, 6, Common::Rect(0, 0, 528, 93));
 
 	_windowManager->pushCursor(Graphics::kMacCursorArrow, nullptr);
 
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index eceb04d3d96..66b572f6c79 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -84,7 +84,7 @@ public:
 		Graphics::Surface *_backup = nullptr;
 		Graphics::Surface _surface;
 
-		void copyToScreen(Graphics::Surface *s);
+		void copyToScreen(Graphics::Surface *s = nullptr);
 	public:
 		Graphics::Surface *surface() { return &_surface; }
 		void show();
@@ -117,6 +117,8 @@ public:
 	virtual const Graphics::Font *getFontByScummId(int32 id) = 0;
 	virtual void getFontParams(FontId fontId, int &id, int &size, int &slant) = 0;
 
+	const Graphics::Surface *loadPICT(int id);
+
 	virtual bool handleMenu(int id, Common::String &name);
 
 	virtual void showAboutDialog() = 0;


Commit: b79916c7b99e4c543293dc8065c1baa4156fa4c4
    https://github.com/scummvm/scummvm/commit/b79916c7b99e4c543293dc8065c1baa4156fa4c4
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Clean up Mac GUI simple window drawing

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index adc5824c49b..56d8404f72c 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -292,10 +292,14 @@ Common::KeyState ScummEngine::mac_showOldStyleBannerAndPause(const char *msg, in
 
 MacGui::SimpleWindow::SimpleWindow(OSystem *system, Graphics::Surface *from, Common::Rect bounds, SimpleWindowStyle style) : _system(system), _from(from), _bounds(bounds) {
 	_backup = new Graphics::Surface();
-	_backup->create(_bounds.width(), _bounds.height(), Graphics::PixelFormat::createFormatCLUT8());
-	_backup->copyRectToSurface(*_from, 0, 0, _bounds);
+	_backup->create(bounds.width(), bounds.height(), Graphics::PixelFormat::createFormatCLUT8());
+	_backup->copyRectToSurface(*_from, 0, 0, bounds);
+
 	_surface = _from->getSubArea(bounds);
 
+	bounds.grow((style == kStyleNormal) ? -6 : -4);
+	_innerSurface = _from->getSubArea(bounds);
+
 	Graphics::Surface *s = surface();
 	Common::Rect r = Common::Rect(0, 0, s->w, s->h);
 
@@ -303,15 +307,15 @@ MacGui::SimpleWindow::SimpleWindow(OSystem *system, Graphics::Surface *from, Com
 	s->fillRect(r, kWhite);
 
 	if (style == MacGui::kStyleNormal) {
-		int growths[] = { 1, -2, -1 };
+		int growths[] = { 1, -3, -1 };
 
 		for (int i = 0; i < ARRAYSIZE(growths); i++) {
 			r.grow(growths[i]);
 
 			s->hLine(r.left, r.top, r.right - 1, kBlack);
 			s->hLine(r.left, r.bottom - 1, r.right - 1, kBlack);
-			s->hLine(r.left, r.top + 1, r.bottom - 2, kBlack);
-			s->hLine(r.right - 1, r.top + 1, r.bottom - 2, kBlack);
+			s->vLine(r.left, r.top + 1, r.bottom - 2, kBlack);
+			s->vLine(r.right - 1, r.top + 1, r.bottom - 2, kBlack);
 		}
 	} else if (style == MacGui::kStyleRounded) {
 		r.grow(1);
@@ -644,46 +648,18 @@ void MacGui::updateWindowManager() {
 }
 
 MacGui::SimpleWindow *MacGui::drawBanner(char *message) {
-	Common::Rect bounds(70, 189, 570, 211);
-
-	MacGui::SimpleWindow *window = new SimpleWindow(_system, _surface, bounds, MacGui::kStyleRounded);
-
+	MacGui::SimpleWindow *window = openWindow(Common::Rect(70, 189, 570, 211), kStyleRounded);
 	const Graphics::Font *font = getFont(_vm->_game.id == GID_INDY3 ? kIndy3FontMedium : kLoomFontMedium);
 
-	font->drawString(_surface, (char *)message, bounds.left, bounds.top + 4, bounds.width(), kBlack, Graphics::kTextAlignCenter);
-
-	_system->copyRectToScreen(_surface->getBasePtr(bounds.left, bounds.top), _surface->pitch, bounds.left, bounds.top, bounds.width(), bounds.height());
+	Graphics::Surface *s = window->innerSurface();
+	font->drawString(s, (char *)message, 0, 0, s->w, kBlack, Graphics::kTextAlignCenter);
 
+	window->show();
 	return window;
 }
 
-MacGui::SimpleWindow *MacGui::drawWindow(Common::Rect bounds) {
-	MacGui::SimpleWindow *window = new SimpleWindow(_system, _surface, bounds);
-
-	_surface->hLine(bounds.left, bounds.top, bounds.right - 1, kBlack);
-	_surface->hLine(bounds.left, bounds.bottom - 1, bounds.right - 1, kBlack);
-	_surface->vLine(bounds.left, bounds.top, bounds.bottom - 1, kBlack);
-	_surface->vLine(bounds.right - 1, bounds.top, bounds.bottom - 1, kBlack);
-
-	bounds.grow(-1);
-
-	_surface->fillRect(bounds, kWhite);
-
-	bounds.grow(-2);
-
-	_surface->hLine(bounds.left, bounds.top, bounds.right - 1, kBlack);
-	_surface->hLine(bounds.left, bounds.bottom - 1, bounds.right - 1, kBlack);
-	_surface->vLine(bounds.left, bounds.top, bounds.bottom - 1, kBlack);
-	_surface->vLine(bounds.right - 1, bounds.top, bounds.bottom - 1, kBlack);
-
-	bounds.grow(-1);
-
-	_surface->hLine(bounds.left, bounds.top, bounds.right - 1, kBlack);
-	_surface->hLine(bounds.left, bounds.bottom - 1, bounds.right - 1, kBlack);
-	_surface->vLine(bounds.left, bounds.top, bounds.bottom - 1, kBlack);
-	_surface->vLine(bounds.right - 1, bounds.top, bounds.bottom - 1, kBlack);
-
-	return window;
+MacGui::SimpleWindow *MacGui::openWindow(Common::Rect bounds, SimpleWindowStyle style) {
+	return new SimpleWindow(_system, _surface, bounds, style);
 }
 
 // ===========================================================================
@@ -1746,7 +1722,7 @@ void MacIndy3Gui::showAboutDialog() {
 
 	Common::Rect bounds(x, y, x + width, y + height);
 
-	SimpleWindow *window = drawWindow(bounds);
+	SimpleWindow *window = openWindow(bounds);
 
 	const Graphics::Surface *pict = loadPICT(2000);
 
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 66b572f6c79..9c3007aa67e 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -83,12 +83,14 @@ public:
 		Graphics::Surface *_from = nullptr;
 		Graphics::Surface *_backup = nullptr;
 		Graphics::Surface _surface;
+		Graphics::Surface _innerSurface;
 
 		void copyToScreen(Graphics::Surface *s = nullptr);
 	public:
 		Graphics::Surface *surface() { return &_surface; }
+		Graphics::Surface *innerSurface() { return &_innerSurface; }
 		void show();
-		SimpleWindow(OSystem *system, Graphics::Surface *from, Common::Rect bounds, SimpleWindowStyle = kStyleNormal);
+		SimpleWindow(OSystem *system, Graphics::Surface *from, Common::Rect bounds, SimpleWindowStyle style = kStyleNormal);
 		~SimpleWindow();
 	};
 
@@ -137,7 +139,7 @@ public:
 
 	SimpleWindow *drawBanner(char *message);
 
-	SimpleWindow *drawWindow(Common::Rect bounds);
+	SimpleWindow *openWindow(Common::Rect bounds, SimpleWindowStyle style = kStyleNormal);
 };
 
 class MacLoomGui : public MacGui {


Commit: 08ad42c91743c054766566291f1d61e8ad96a3d8
    https://github.com/scummvm/scummvm/commit/08ad42c91743c054766566291f1d61e8ad96a3d8
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Add delay() function for Mac GUI

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 56d8404f72c..6b8f0dd5212 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -658,6 +658,33 @@ MacGui::SimpleWindow *MacGui::drawBanner(char *message) {
 	return window;
 }
 
+bool MacGui::delay(uint32 ms) {
+	uint32 to = _system->getMillis() + ms;
+
+	while (_system->getMillis() < to) {
+		Common::Event event;
+
+		while (_system->getEventManager()->pollEvent(event)) {
+			switch (event.type) {
+			case Common::EVENT_QUIT:
+			case Common::EVENT_LBUTTONDOWN:
+			case Common::EVENT_KEYDOWN:
+				return true;
+
+			default:
+				break;
+			}
+
+			uint32 delta = to - _system->getMillis();
+
+			if (delta > 0)
+				_system->delayMillis(MIN<uint32>(delta, 10));
+		}
+	}
+
+	return false;
+}
+
 MacGui::SimpleWindow *MacGui::openWindow(Common::Rect bounds, SimpleWindowStyle style) {
 	return new SimpleWindow(_system, _surface, bounds, style);
 }
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 9c3007aa67e..c505f156662 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -139,6 +139,7 @@ public:
 
 	SimpleWindow *drawBanner(char *message);
 
+	bool delay(uint32 ms);
 	SimpleWindow *openWindow(Common::Rect bounds, SimpleWindowStyle style = kStyleNormal);
 };
 


Commit: 50a4ebd27b06759ac1f038fecc51e651668a4abd
    https://github.com/scummvm/scummvm/commit/50a4ebd27b06759ac1f038fecc51e651668a4abd
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Draw Indy 3 Mac About background

That should be enough to then draw the animations and text on top of.

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 6b8f0dd5212..a2a58a638d4 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -1737,7 +1737,7 @@ void MacIndy3Gui::showAboutDialog() {
 	// hard-coded (416x166), and it's drawn centered. The graphics are in
 	// PICT 2000.
 
-#if 0
+#if 1
 	int width = 416;
 	int height = 166;
 #else
@@ -1753,7 +1753,14 @@ void MacIndy3Gui::showAboutDialog() {
 
 	const Graphics::Surface *pict = loadPICT(2000);
 
-	window->surface()->copyRectToSurface(*pict, 6, 6, Common::Rect(0, 0, 528, 93));
+//	window->surface()->copyRectToSurface(*pict, 6, 6, Common::Rect(0, 0, 528, 93));
+
+	Graphics::Surface *s = window->innerSurface();
+
+	fillPattern(s, Common::Rect(2, 2, s->w - 2, 130), 0x8020);
+	fillPattern(s, Common::Rect(2, 130, s->w - 2, 133), 0xA5A5);
+	fillPattern(s, Common::Rect(2, 133, s->w - 2, 136), 0xFFFF);
+	fillPattern(s, Common::Rect(2, 136, s->w - 2, s->h - 4), 0xA5A5);
 
 	_windowManager->pushCursor(Graphics::kMacCursorArrow, nullptr);
 
@@ -2051,6 +2058,15 @@ void MacIndy3Gui::copyDirtyRectsToScreen() {
 	_dirtyRects.clear();
 }
 
+void MacIndy3Gui::fillPattern(Graphics::Surface *s, Common::Rect r, uint16 pattern) {
+	for (int y = r.top; y < r.bottom; y++) {
+		for (int x = r.left; x < r.right; x++) {
+			int bit = 0x8000 >> (4 * (y % 4) + (x % 4));
+			s->setPixel(x, y, (pattern & bit) ? kBlack : kWhite);
+		}
+	}
+}
+
 void MacIndy3Gui::fill(Common::Rect r) const {
 	int pitch = _surface->pitch;
 
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index c505f156662..13fb1e6296e 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -398,6 +398,8 @@ private:
 	void show();
 	void hide();
 
+	void fillPattern(Graphics::Surface *s, Common::Rect r, uint16 pattern);
+
 	void fill(Common::Rect r) const;
 	void drawBitmap(Common::Rect r, const uint16 *bitmap, Color color) const;
 


Commit: 8c9a34fcc633d83eab03d26f1608497a64ebcd47
    https://github.com/scummvm/scummvm/commit/8c9a34fcc633d83eab03d26f1608497a64ebcd47
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Cleanup and animation mock-up for Mac Indy 3 GUI

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index a2a58a638d4..d46df1a4c8c 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -25,6 +25,7 @@
 #include "graphics/cursorman.h"
 #include "graphics/maccursor.h"
 #include "graphics/macega.h"
+#include "graphics/primitives.h"
 #include "graphics/fonts/macfont.h"
 #include "graphics/macgui/macfontmanager.h"
 #include "graphics/macgui/macwindowmanager.h"
@@ -288,6 +289,12 @@ Common::KeyState ScummEngine::mac_showOldStyleBannerAndPause(const char *msg, in
 	return ks;
 }
 
+static void plotPixel(int x, int y, int color, void *data) {
+	MacGui::SimpleWindow *window = (MacGui::SimpleWindow *)data;
+	Graphics::Surface *s = window->innerSurface();
+	s->setPixel(x, y, color);
+}
+
 // Very simple window class
 
 MacGui::SimpleWindow::SimpleWindow(OSystem *system, Graphics::Surface *from, Common::Rect bounds, SimpleWindowStyle style) : _system(system), _from(from), _bounds(bounds) {
@@ -295,11 +302,14 @@ MacGui::SimpleWindow::SimpleWindow(OSystem *system, Graphics::Surface *from, Com
 	_backup->create(bounds.width(), bounds.height(), Graphics::PixelFormat::createFormatCLUT8());
 	_backup->copyRectToSurface(*_from, 0, 0, bounds);
 
-	_surface = _from->getSubArea(bounds);
+	_margin = (style == kStyleNormal) ? 6 : 4;
 
-	bounds.grow((style == kStyleNormal) ? -6 : -4);
+	_surface = _from->getSubArea(bounds);
+	bounds.grow(-_margin);
 	_innerSurface = _from->getSubArea(bounds);
 
+	_dirtyRects.clear();
+
 	Graphics::Surface *s = surface();
 	Common::Rect r = Common::Rect(0, 0, s->w, s->h);
 
@@ -349,6 +359,61 @@ void MacGui::SimpleWindow::copyToScreen(Graphics::Surface *s) {
 
 void MacGui::SimpleWindow::show() {
 	copyToScreen();
+	_dirtyRects.clear();
+}
+
+void MacGui::SimpleWindow::markRectAsDirty(Common::Rect r) {
+	_dirtyRects.push_back(r);
+}
+
+void MacGui::SimpleWindow::update() {
+	for (uint i = 0; i < _dirtyRects.size(); i++) {
+		_system->copyRectToScreen(
+			_innerSurface.getBasePtr(_dirtyRects[i].left, _dirtyRects[i].top),
+			_innerSurface.pitch,
+			_bounds.left + _margin + _dirtyRects[i].left, _bounds.top + _margin + _dirtyRects[i].top,
+			_dirtyRects[i].width(), _dirtyRects[i].height());
+	}
+
+	_dirtyRects.clear();
+}
+
+void MacGui::SimpleWindow::fillPattern(Common::Rect r, uint16 pattern) {
+	for (int y = r.top; y < r.bottom; y++) {
+		for (int x = r.left; x < r.right; x++) {
+			int bit = 0x8000 >> (4 * (y % 4) + (x % 4));
+			_innerSurface.setPixel(x, y, (pattern & bit) ? kBlack : kWhite);
+		}
+	}
+
+	markRectAsDirty(r);
+}
+
+void MacGui::SimpleWindow::drawSprite(Graphics::Surface *sprite, int x, int y, Common::Rect(clipRect)) {
+	Common::Rect subRect(0, 0, sprite->w, sprite->h);
+
+	if (x < clipRect.left) {
+		subRect.left += (clipRect.left - x);
+		x = clipRect.left;
+	}
+
+	if (y < clipRect.top) {
+		subRect.top += (clipRect.top - y);
+		y = clipRect.top;
+	}
+
+	if (x + sprite->w >= clipRect.right) {
+		subRect.right -= (x + sprite->w - clipRect.right);
+	}
+
+	if (y + sprite->h >= clipRect.bottom) {
+		subRect.bottom -= (y + sprite->h - clipRect.bottom);
+	}
+
+	if (subRect.width() > 0 && subRect.height() > 0) {
+		_innerSurface.copyRectToSurface(*sprite, x, y, subRect);
+		markRectAsDirty(Common::Rect(x, y, x + subRect.width(), y + subRect.height()));
+	}
 }
 
 // ===========================================================================
@@ -492,7 +557,6 @@ const Graphics::Surface *MacGui::loadPICT(int id) {
 
 	Graphics::Surface *s = new Graphics::Surface();
 	s->create(right - left, bottom - top, Graphics::PixelFormat::createFormatCLUT8());
-	s->fillRect(Common::Rect(left, top, right, bottom), kWhite);
 
 	bool endOfPicture = false;
 
@@ -509,9 +573,7 @@ const Graphics::Surface *MacGui::loadPICT(int id) {
 		switch (opcode) {
 		case 0x01: // clipRgn
 			// The clip region is not important
-			size = res->readUint16BE();
-			assert(size == 10);
-			res->skip(8);
+			res->skip(res->readUint16BE() - 2);
 			break;
 
 		case 0x11: // picVersion
@@ -520,54 +582,49 @@ const Graphics::Surface *MacGui::loadPICT(int id) {
 			break;
 
 		case 0x99: // PackBitsRgn
-			rowBytes = res->readUint16BE();
+			res->skip(2);	// Skip rowBytes
+
 			y1 = res->readSint16BE();
 			x1 = res->readSint16BE();
 			y2 = res->readSint16BE();
 			x2 = res->readSint16BE();
 
-			// srcRect isn't interesting
-			res->skip(8);
-
-			// dstRect isn't interesting
-			res->skip(8);
-
-			mode = res->readUint16BE();
-
-			// maskRgn isn't interesting
-			size = res->readUint16BE();
-			assert(size == 10);
-			res->skip(size - 2);
+			res->skip(8);	// Skip srcRect
+			res->skip(8);	// Skip dstRect
+			res->skip(2);	// Skip mode
+			res->skip(res->readUint16BE() - 2);	// Skip maskRgn
 
 			for (y = y1; y < y2; y++) {
-				size = res->readByte();
-
 				x = x1;
-				int bytesRead = 0;
+				size = res->readByte();
 
-				while (bytesRead < size) {
+				while (size > 0) {
 					byte count = res->readByte();
-					bytesRead++;
+					size--;
+
+					bool repeat;
+
 					if (count >= 128) {
+						// Repeat value
 						count = 256 - count;
-						int b = res->readByte();
-						bytesRead++;
-						for (int j = 0; j <= count; j++) {
-							for (int k = 7; k >= 0; k--) {
-								if (b & (1 << k))
-									s->setPixel(x, y, kBlack);
-								x++;
-							}
-						}
+						repeat = true;
+						value = res->readByte();
+						size--;
 					} else {
-						for (int j = 0; j <= count; j++) {
-							int b = res->readByte();
-							bytesRead++;
-							for (int k = 7; k >= 0; k--) {
-								if (b & (1 << k))
-									s->setPixel(x, y, kBlack);
-								x++;
-							}
+						// Copy values
+						repeat = false;
+					}
+
+					for (int j = 0; j <= count; j++) {
+						if (!repeat) {
+							value = res->readByte();
+							size--;
+						}
+						for (int k = 7; k >= 0 && x < x2; k--, x++) {
+							if (value & (1 << k))
+								s->setPixel(x, y, kBlack);
+							else
+								s->setPixel(x, y, kWhite);
 						}
 					}
 				}
@@ -668,17 +725,18 @@ bool MacGui::delay(uint32 ms) {
 			switch (event.type) {
 			case Common::EVENT_QUIT:
 			case Common::EVENT_LBUTTONDOWN:
-			case Common::EVENT_KEYDOWN:
 				return true;
 
 			default:
 				break;
 			}
+		}
 
-			uint32 delta = to - _system->getMillis();
+		uint32 delta = to - _system->getMillis();
 
-			if (delta > 0)
-				_system->delayMillis(MIN<uint32>(delta, 10));
+		if (delta > 0) {
+			_system->delayMillis(MIN<uint32>(delta, 10));
+			_system->updateScreen();
 		}
 	}
 
@@ -1737,34 +1795,96 @@ void MacIndy3Gui::showAboutDialog() {
 	// hard-coded (416x166), and it's drawn centered. The graphics are in
 	// PICT 2000.
 
-#if 1
 	int width = 416;
 	int height = 166;
-#else
-	int width = 540;
-	int height = 105;
-#endif
 	int x = (640 - width) / 2;
 	int y = (400 - height) / 2;
 
 	Common::Rect bounds(x, y, x + width, y + height);
-
 	SimpleWindow *window = openWindow(bounds);
-
 	const Graphics::Surface *pict = loadPICT(2000);
 
-//	window->surface()->copyRectToSurface(*pict, 6, 6, Common::Rect(0, 0, 528, 93));
+	Graphics::Surface train = pict->getSubArea(Common::Rect(0, 0, 241, 93));
+
+	Graphics::Surface trolley[3];
+
+	for (int i = 0; i < 3; i++)
+		trolley[i] = pict->getSubArea(Common::Rect(251 + 92 * i, 38, 335 + 92 * i, 93));
+
+	clearAboutDialog(window);
+	window->show();
+
+	bool skip = false;
 
 	Graphics::Surface *s = window->innerSurface();
+	Common::Rect clipRect(2, 2, s->w - 4, s->h - 4);
+
+	// For the background of the sprite to match the background of the
+	// window, we have to move it a multiple of 4 pixels each step. The
+	// original seems to move it by 12 pixels at a time. When recorded at
+	// 30 fps, the train moves every 3 frames.
+
+	// The trolley moves 4 pixels at a time every three frames, and the
+	// animation frame changes every time it moves. But the animation cycle
+	// seems semi-random to me. I think this is a case of not caring too
+	// deeply about pixel accuracy as long as everything stops and starts
+	// at the right time, moves at the right speed, and stops and starts
+	// on the correct animation frame.
+
+	for (x = 0; x < train.w; x += 4) {
+		window->drawSprite(&train, 2 - x, 40, clipRect);
+		window->update();
+
+		if (delay(33)) {
+			skip = true;
+			break;
+		}
+	}
 
-	fillPattern(s, Common::Rect(2, 2, s->w - 2, 130), 0x8020);
-	fillPattern(s, Common::Rect(2, 130, s->w - 2, 133), 0xA5A5);
-	fillPattern(s, Common::Rect(2, 133, s->w - 2, 136), 0xFFFF);
-	fillPattern(s, Common::Rect(2, 136, s->w - 2, s->h - 4), 0xA5A5);
+	if (skip) {
+		clearAboutDialog(window);
+		window->update();
+		skip = false;
+	}
 
-	_windowManager->pushCursor(Graphics::kMacCursorArrow, nullptr);
+	Common::Rect r(20, 4, 380, 100);
 
-	window->show();
+	Graphics::drawRoundRect(r, 8, kWhite, true, plotPixel, window);
+	Graphics::drawRoundRect(r, 8, kBlack, false, plotPixel, window);
+	window->markRectAsDirty(r);
+	window->update();
+
+	if (delay(2000))
+		skip = true;
+
+	clearAboutDialog(window);
+
+	int trolleyFrame = 0;
+	int trolleyFrameDelta = 1;
+
+	for (x = width + 1; !skip && x >= -85; x -= 4) {
+		window->drawSprite(&trolley[trolleyFrame], x, 78, clipRect);
+		window->update();
+
+		trolleyFrame += trolleyFrameDelta;
+		if (trolleyFrame < 0 || trolleyFrame > 2) {
+			trolleyFrame = 1;
+			trolleyFrameDelta = -trolleyFrameDelta;
+		}
+
+		if (delay(100)) {
+			skip = true;
+			break;
+		}
+	}
+
+	if (skip) {
+		clearAboutDialog(window);
+		window->update();
+		skip = false;
+	}
+
+	_windowManager->pushCursor(Graphics::kMacCursorArrow, nullptr);
 
 	bool shouldQuit = false;
 	bool shouldQuitEngine = false;
@@ -1800,6 +1920,15 @@ void MacIndy3Gui::showAboutDialog() {
 		debug("Quit everything");
 }
 
+void MacIndy3Gui::clearAboutDialog(SimpleWindow *window) {
+	Graphics::Surface *s = window->innerSurface();
+
+	window->fillPattern(Common::Rect(2, 2, s->w - 2, 132), 0x8020);
+	window->fillPattern(Common::Rect(2, 130, s->w - 2, 133), 0xA5A5);
+	window->fillPattern(Common::Rect(2, 133, s->w - 2, 136), 0xFFFF);
+	window->fillPattern(Common::Rect(2, 136, s->w - 2, s->h - 4), 0xA5A5);
+}
+
 // Before the GUI rewrite, the scroll offset was saved in variable 67. Let's
 // continue that tradition, just in case. If nothing else, it gives us an easy
 // way to still store it in savegames.
@@ -2058,15 +2187,6 @@ void MacIndy3Gui::copyDirtyRectsToScreen() {
 	_dirtyRects.clear();
 }
 
-void MacIndy3Gui::fillPattern(Graphics::Surface *s, Common::Rect r, uint16 pattern) {
-	for (int y = r.top; y < r.bottom; y++) {
-		for (int x = r.left; x < r.right; x++) {
-			int bit = 0x8000 >> (4 * (y % 4) + (x % 4));
-			s->setPixel(x, y, (pattern & bit) ? kBlack : kWhite);
-		}
-	}
-}
-
 void MacIndy3Gui::fill(Common::Rect r) const {
 	int pitch = _surface->pitch;
 
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 13fb1e6296e..1f425cacd2c 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -80,18 +80,30 @@ public:
 	private:
 		OSystem *_system;
 		Common::Rect _bounds;
+		int _margin;
+
 		Graphics::Surface *_from = nullptr;
 		Graphics::Surface *_backup = nullptr;
 		Graphics::Surface _surface;
 		Graphics::Surface _innerSurface;
 
+		Common::Array<Common::Rect> _dirtyRects;
+
 		void copyToScreen(Graphics::Surface *s = nullptr);
 	public:
+		SimpleWindow(OSystem *system, Graphics::Surface *from, Common::Rect bounds, SimpleWindowStyle style = kStyleNormal);
+		~SimpleWindow();
+
 		Graphics::Surface *surface() { return &_surface; }
 		Graphics::Surface *innerSurface() { return &_innerSurface; }
+
 		void show();
-		SimpleWindow(OSystem *system, Graphics::Surface *from, Common::Rect bounds, SimpleWindowStyle style = kStyleNormal);
-		~SimpleWindow();
+
+		void markRectAsDirty(Common::Rect r);
+		void update();
+
+		void fillPattern(Common::Rect r, uint16 pattern);
+		void drawSprite(Graphics::Surface *sprite, int x, int y, Common::Rect clipRect);
 	};
 
 	enum FontId {
@@ -180,6 +192,7 @@ public:
 	bool handleMenu(int id, Common::String &name);
 
 	void showAboutDialog();
+	void clearAboutDialog(SimpleWindow *window);
 
 	// There is a distinction between the GUI being allowed and being
 	// active. Allowed means that it's allowed to draw verbs, but not that
@@ -398,8 +411,6 @@ private:
 	void show();
 	void hide();
 
-	void fillPattern(Graphics::Surface *s, Common::Rect r, uint16 pattern);
-
 	void fill(Common::Rect r) const;
 	void drawBitmap(Common::Rect r, const uint16 *bitmap, Color color) const;
 


Commit: a61b982691bff715f2a3bdd459c3f10bfe946a92
    https://github.com/scummvm/scummvm/commit/a61b982691bff715f2a3bdd459c3f10bfe946a92
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Mac GUI cleanups

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index d46df1a4c8c..580a7fc66a7 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -289,12 +289,6 @@ Common::KeyState ScummEngine::mac_showOldStyleBannerAndPause(const char *msg, in
 	return ks;
 }
 
-static void plotPixel(int x, int y, int color, void *data) {
-	MacGui::SimpleWindow *window = (MacGui::SimpleWindow *)data;
-	Graphics::Surface *s = window->innerSurface();
-	s->setPixel(x, y, color);
-}
-
 // Very simple window class
 
 MacGui::SimpleWindow::SimpleWindow(OSystem *system, Graphics::Surface *from, Common::Rect bounds, SimpleWindowStyle style) : _system(system), _from(from), _bounds(bounds) {
@@ -416,6 +410,18 @@ void MacGui::SimpleWindow::drawSprite(Graphics::Surface *sprite, int x, int y, C
 	}
 }
 
+void MacGui::SimpleWindow::plotPixel(int x, int y, int color, void *data) {
+	MacGui::SimpleWindow *window = (MacGui::SimpleWindow *)data;
+	Graphics::Surface *s = window->innerSurface();
+	s->setPixel(x, y, color);
+}
+
+void MacGui::SimpleWindow::drawTextBox(Common::Rect r) {
+	Graphics::drawRoundRect(r, 9, kWhite, true, plotPixel, this);
+	Graphics::drawRoundRect(r, 9, kBlack, false, plotPixel, this);
+	markRectAsDirty(r);
+}
+
 // ===========================================================================
 // Macintosh user interface for the Macintosh versions of Loom and Indiana
 // Jones and the Last Crusade.
@@ -1804,7 +1810,12 @@ void MacIndy3Gui::showAboutDialog() {
 	SimpleWindow *window = openWindow(bounds);
 	const Graphics::Surface *pict = loadPICT(2000);
 
-	Graphics::Surface train = pict->getSubArea(Common::Rect(0, 0, 241, 93));
+	// For the background of the sprites to match the background of the
+	// window, we have to move them at multiples of 4 pixels each step. We
+	// cut out enough of the background so that each time they are drawn,
+	// the visible remains of the previous frame is overdrawn.
+
+	Graphics::Surface train = pict->getSubArea(Common::Rect(0, 0, 249, 93));
 
 	Graphics::Surface trolley[3];
 
@@ -1819,23 +1830,17 @@ void MacIndy3Gui::showAboutDialog() {
 	Graphics::Surface *s = window->innerSurface();
 	Common::Rect clipRect(2, 2, s->w - 4, s->h - 4);
 
-	// For the background of the sprite to match the background of the
-	// window, we have to move it a multiple of 4 pixels each step. The
-	// original seems to move it by 12 pixels at a time. When recorded at
-	// 30 fps, the train moves every 3 frames.
+	// Page 1 - The train
 
-	// The trolley moves 4 pixels at a time every three frames, and the
-	// animation frame changes every time it moves. But the animation cycle
-	// seems semi-random to me. I think this is a case of not caring too
-	// deeply about pixel accuracy as long as everything stops and starts
-	// at the right time, moves at the right speed, and stops and starts
-	// on the correct animation frame.
+	// The train seems to move at 12 pixels at a time. When recorded at
+	// 30 fps, it moves every 3 frames. I'm going to assume that's the
+	// intended frame rate.
 
-	for (x = 0; x < train.w; x += 4) {
+	for (x = 0; x < train.w; x += 12) {
 		window->drawSprite(&train, 2 - x, 40, clipRect);
 		window->update();
 
-		if (delay(33)) {
+		if (delay(100)) {
 			skip = true;
 			break;
 		}
@@ -1847,18 +1852,25 @@ void MacIndy3Gui::showAboutDialog() {
 		skip = false;
 	}
 
-	Common::Rect r(20, 4, 380, 100);
+	// Page 2 - Text box
 
-	Graphics::drawRoundRect(r, 8, kWhite, true, plotPixel, window);
-	Graphics::drawRoundRect(r, 8, kBlack, false, plotPixel, window);
-	window->markRectAsDirty(r);
+	window->drawTextBox(Common::Rect(22, 6, 382, 102));
 	window->update();
 
-	if (delay(2000))
+	if (delay(5000))
 		skip = true;
 
 	clearAboutDialog(window);
 
+	// Page 3 - Trolley to the middle
+
+	// The trolley moves 4 pixels at a time every three frames, and the
+	// animation frame changes every time it moves. But the animation cycle
+	// seems semi-random to me. I think this is a case of not caring too
+	// deeply about pixel accuracy as long as everything stops and starts
+	// at the right time, moves at the right speed, and stops and starts
+	// on the correct animation frame.
+
 	int trolleyFrame = 0;
 	int trolleyFrameDelta = 1;
 
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 1f425cacd2c..b861c1e7ed2 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -102,8 +102,11 @@ public:
 		void markRectAsDirty(Common::Rect r);
 		void update();
 
+		static void plotPixel(int x, int y, int color, void *data);
+
 		void fillPattern(Common::Rect r, uint16 pattern);
 		void drawSprite(Graphics::Surface *sprite, int x, int y, Common::Rect clipRect);
+		void drawTextBox(Common::Rect r);
 	};
 
 	enum FontId {


Commit: 1a1d3c1a2955d8bb0df0a0f278bf83f8fe31574c
    https://github.com/scummvm/scummvm/commit/1a1d3c1a2955d8bb0df0a0f278bf83f8fe31574c
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Mock up first text screen of Mac Indy 3 About dialog

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 580a7fc66a7..c2ce37a96ca 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -1755,6 +1755,24 @@ void MacIndy3Gui::getFontParams(FontId fontId, int &id, int &size, int &slant) {
 	// headline. The rest of the Indy 3 verb GUI uses Geneva.
 
 	switch (fontId) {
+	case FontId::kIndy3AboutFontHeader:
+		id = _gameFontId;
+		size = 12;
+		slant = Graphics::kMacFontItalic | Graphics::kMacFontShadow;
+		break;
+
+	case FontId::kIndy3AboutFontBold:
+		id = _gameFontId;
+		size = 9;
+		slant = Graphics::kMacFontRegular;
+		break;
+
+	case FontId::kIndy3AboutFontRegular:
+		id = Graphics::kMacFontGeneva;
+		size = 9;
+		slant = Graphics::kMacFontRegular;
+		break;
+
 	case FontId::kIndy3FontSmall:
 		id = _gameFontId;
 		size = 9;
@@ -1854,7 +1872,22 @@ void MacIndy3Gui::showAboutDialog() {
 
 	// Page 2 - Text box
 
-	window->drawTextBox(Common::Rect(22, 6, 382, 102));
+	Common::Rect r(22, 6, 382, 102);
+
+	window->drawTextBox(r);
+
+	// These strings are part of the STRS resource, but I don't know how to
+	// safely read them from there.
+
+	const Graphics::Font *fontHeader = getFont(kIndy3AboutFontHeader);
+	const Graphics::Font *fontBold = getFont(kIndy3AboutFontBold);
+	const Graphics::Font *fontRegular = getFont(kIndy3AboutFontRegular);
+
+	fontHeader->drawString(s, "Indiana Jones and the Last Crusade", r.left, 10, r.width(), kBlack, Graphics::kTextAlignCenter);
+	fontBold->drawString(s, "The Graphic Adventure", r.left, 28, r.width(), kBlack, Graphics::kTextAlignCenter);
+	fontBold->drawString(s, "Mac 1.7 8/17/90, Interpreter version 5.1.6", r.left, 55, r.width(), kBlack, Graphics::kTextAlignCenter);
+	fontRegular->drawString(s, "TM & \xA9 1990 LucasArts Entertainment Company.  All rights reserved.", r.left, 88, r.width(), kBlack, Graphics::kTextAlignCenter);
+
 	window->update();
 
 	if (delay(5000))
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index b861c1e7ed2..af594d648d6 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -112,6 +112,10 @@ public:
 	enum FontId {
 		kSystemFont,
 
+		kIndy3AboutFontRegular,
+		kIndy3AboutFontBold,
+		kIndy3AboutFontHeader,
+
 		kIndy3FontSmall,
 		kIndy3FontMedium,
 		kIndy3VerbFontRegular,


Commit: 41ddad4bc3d6a0a91631fa12ce9b6b0b41b3f2ae
    https://github.com/scummvm/scummvm/commit/41ddad4bc3d6a0a91631fa12ce9b6b0b41b3f2ae
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: More Indy 3 Mac About dialog mock-ups

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index c2ce37a96ca..8cb8ddc401e 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -1758,7 +1758,7 @@ void MacIndy3Gui::getFontParams(FontId fontId, int &id, int &size, int &slant) {
 	case FontId::kIndy3AboutFontHeader:
 		id = _gameFontId;
 		size = 12;
-		slant = Graphics::kMacFontItalic | Graphics::kMacFontShadow;
+		slant = Graphics::kMacFontItalic | Graphics::kMacFontBold | Graphics::kMacFontShadow | Graphics::kMacFontCondense;
 		break;
 
 	case FontId::kIndy3AboutFontBold:
@@ -1848,6 +1848,9 @@ void MacIndy3Gui::showAboutDialog() {
 	Graphics::Surface *s = window->innerSurface();
 	Common::Rect clipRect(2, 2, s->w - 4, s->h - 4);
 
+	Common::Rect r1(22, 6, 382, 102);
+	Common::Rect r2(22, 6, 382, 70);
+
 	// Page 1 - The train
 
 	// The train seems to move at 12 pixels at a time. When recorded at
@@ -1870,11 +1873,9 @@ void MacIndy3Gui::showAboutDialog() {
 		skip = false;
 	}
 
-	// Page 2 - Text box
-
-	Common::Rect r(22, 6, 382, 102);
+	// Page 2 - First text box
 
-	window->drawTextBox(r);
+	window->drawTextBox(r1);
 
 	// These strings are part of the STRS resource, but I don't know how to
 	// safely read them from there.
@@ -1883,19 +1884,23 @@ void MacIndy3Gui::showAboutDialog() {
 	const Graphics::Font *fontBold = getFont(kIndy3AboutFontBold);
 	const Graphics::Font *fontRegular = getFont(kIndy3AboutFontRegular);
 
-	fontHeader->drawString(s, "Indiana Jones and the Last Crusade", r.left, 10, r.width(), kBlack, Graphics::kTextAlignCenter);
-	fontBold->drawString(s, "The Graphic Adventure", r.left, 28, r.width(), kBlack, Graphics::kTextAlignCenter);
-	fontBold->drawString(s, "Mac 1.7 8/17/90, Interpreter version 5.1.6", r.left, 55, r.width(), kBlack, Graphics::kTextAlignCenter);
-	fontRegular->drawString(s, "TM & \xA9 1990 LucasArts Entertainment Company.  All rights reserved.", r.left, 88, r.width(), kBlack, Graphics::kTextAlignCenter);
+	// This isn't pixel perfect (though perhaps pixel good?). The header
+	// text in particular is rendered differently. It should stay on screen
+	// for 160 frames, or 5.33 seconds.
+
+	fontHeader->drawString(s, "Indiana Jones and the Last Crusade", r1.left - 2, 10, r1.width(), kBlack, Graphics::kTextAlignCenter);
+	fontBold->drawString(s, "The Graphic Adventure", r1.left, 28, r1.width(), kBlack, Graphics::kTextAlignCenter);
+	fontBold->drawString(s, "Mac 1.7 8/17/90, Interpreter version 5.1.6", r1.left, 55, r1.width(), kBlack, Graphics::kTextAlignCenter);
+	fontRegular->drawString(s, "TM & \xA9 1990 LucasArts Entertainment Company.  All rights reserved.", r1.left + 1, 88, r1.width(), kBlack, Graphics::kTextAlignCenter);
 
 	window->update();
 
-	if (delay(5000))
+	if (delay(5333))
 		skip = true;
 
 	clearAboutDialog(window);
 
-	// Page 3 - Trolley to the middle
+	// Page 3 - The trolley
 
 	// The trolley moves 4 pixels at a time every three frames, and the
 	// animation frame changes every time it moves. But the animation cycle
@@ -1904,13 +1909,24 @@ void MacIndy3Gui::showAboutDialog() {
 	// at the right time, moves at the right speed, and stops and starts
 	// on the correct animation frame.
 
-	int trolleyFrame = 0;
+	window->drawTextBox(r2);
+
+	int trolleyFrame = 1;
 	int trolleyFrameDelta = 1;
 
 	for (x = width + 1; !skip && x >= -85; x -= 4) {
 		window->drawSprite(&trolley[trolleyFrame], x, 78, clipRect);
 		window->update();
 
+		if (x == 161) {
+			if (delay(5000)) {
+				skip = true;
+				break;
+			}
+
+			window->drawTextBox(r2);
+		}
+
 		trolleyFrame += trolleyFrameDelta;
 		if (trolleyFrame < 0 || trolleyFrame > 2) {
 			trolleyFrame = 1;
@@ -1923,6 +1939,12 @@ void MacIndy3Gui::showAboutDialog() {
 		}
 	}
 
+	if (delay(5333))
+		skip = true;
+
+	window->drawTextBox(r1);
+	window->update();
+
 	if (skip) {
 		clearAboutDialog(window);
 		window->update();


Commit: e7e4f6fcc25d634dc12f835da0d6edac971afa5a
    https://github.com/scummvm/scummvm/commit/e7e4f6fcc25d634dc12f835da0d6edac971afa5a
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Began rewriting the animation loop for Indy 3 Mac About dialog

This will make it possible to skip to the next part, like the original
does, while cutting down on code duplication.

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 8cb8ddc401e..ba952fd79b6 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -721,7 +721,7 @@ MacGui::SimpleWindow *MacGui::drawBanner(char *message) {
 	return window;
 }
 
-bool MacGui::delay(uint32 ms) {
+int MacGui::delay(uint32 ms) {
 	uint32 to = _system->getMillis() + ms;
 
 	while (_system->getMillis() < to) {
@@ -730,8 +730,10 @@ bool MacGui::delay(uint32 ms) {
 		while (_system->getEventManager()->pollEvent(event)) {
 			switch (event.type) {
 			case Common::EVENT_QUIT:
+				return 2;
+
 			case Common::EVENT_LBUTTONDOWN:
-				return true;
+				return 1;
 
 			default:
 				break;
@@ -746,7 +748,7 @@ bool MacGui::delay(uint32 ms) {
 		}
 	}
 
-	return false;
+	return 0;
 }
 
 MacGui::SimpleWindow *MacGui::openWindow(Common::Rect bounds, SimpleWindowStyle style) {
@@ -1851,8 +1853,118 @@ void MacIndy3Gui::showAboutDialog() {
 	Common::Rect r1(22, 6, 382, 102);
 	Common::Rect r2(22, 6, 382, 70);
 
-	// Page 1 - The train
+	// Judging by recordings of Basilisk II, the internal frame rate is
+	// 10 fps.
+
+	int scene = 0;
+
+	int trainX = -2;
+	int trolleyX = width + 1;
+	int trolleyFrame = 1;
+	int trolleyFrameDelta = 1;
+	int trolleyWaitFrames = 50;
+	int textWaitFrames = 53;
+
+	// Header texts aren't rendered correctly. Apparently the original does
+	// its own shadowing by drawing the text twice, but that ends up
+	// looking even worse. Perhaps I have to draw the text three times
+	// once to fill it), or maybe our Mac text rendering just isn't as good
+	// as it needs to be yet.
+
+	const Graphics::Font *fontHeader = getFont(kIndy3AboutFontHeader);
+	const Graphics::Font *fontBold = getFont(kIndy3AboutFontBold);
+	const Graphics::Font *fontRegular = getFont(kIndy3AboutFontRegular);
+
+	bool changeScene = false;
+
+	while (true && !_vm->shouldQuit()) {
+		switch (scene) {
+		case 0:
+			window->drawSprite(&train, trainX, 40, clipRect);
+			trainX -= 12;
+
+			if (trainX < -train.w)
+				changeScene = true;
+
+			break;
+
+		case 1:
+			if (--textWaitFrames == 0)
+				changeScene = true;
+			break;
+
+		case 2:
+		case 3:
+			window->drawSprite(&trolley[trolleyFrame], trolleyX, 78, clipRect);
+
+			if (scene == 2 && trolleyX == 161 && trolleyWaitFrames > 0) {
+				trolleyWaitFrames--;
+			} else {
+				trolleyX -= 4;
+				trolleyFrame += trolleyFrameDelta;
+				if (trolleyFrame < 0 || trolleyFrame > 2) {
+					trolleyFrame = 1;
+					trolleyFrameDelta = -trolleyFrameDelta;
+				}
+
+				if (trolleyX < -85)
+					changeScene = true;
+			}
+
+			break;
+		}
+
+		window->update();
+
+		int status = delay(100);
+		if (status == 2)
+			break;
+
+		if (status == 1 || changeScene) {
+			changeScene = false;
+
+			scene++;
+
+			switch (scene) {
+			case 1:
+				clearAboutDialog(window);
+				window->drawTextBox(r1);
+
+				// These strings are part of the STRS resource,
+				// but I don't know how to safely read them
+				// from there.
+
+				fontHeader->drawString(s, "Indiana Jones and the Last Crusade", r1.left - 2, 10, r1.width(), kBlack, Graphics::kTextAlignCenter);
+				fontBold->drawString(s, "The Graphic Adventure", r1.left, 28, r1.width(), kBlack, Graphics::kTextAlignCenter);
+				fontBold->drawString(s, "Mac 1.7 8/17/90, Interpreter version 5.1.6", r1.left, 55, r1.width(), kBlack, Graphics::kTextAlignCenter);
+				fontRegular->drawString(s, "TM & \xA9 1990 LucasArts Entertainment Company.  All rights reserved.", r1.left + 1, 88, r1.width(), kBlack, Graphics::kTextAlignCenter);
+				break;
+
+			case 2:
+				clearAboutDialog(window);
+				window->drawTextBox(r2);
+				break;
+
+			case 3:
+				// Don't clear. The trolley is still on screen
+				// and only the text changes.
+				window->drawTextBox(r2);
+				break;
+
+			case 4:
+				clearAboutDialog(window);
+				window->drawTextBox(r1);
+				break;
+			}
+
+			window->update();
+
+			if (scene > 7)
+				break;
+		}
+	}
 
+#if 0
 	// The train seems to move at 12 pixels at a time. When recorded at
 	// 30 fps, it moves every 3 frames. I'm going to assume that's the
 	// intended frame rate.
@@ -1877,22 +1989,6 @@ void MacIndy3Gui::showAboutDialog() {
 
 	window->drawTextBox(r1);
 
-	// These strings are part of the STRS resource, but I don't know how to
-	// safely read them from there.
-
-	const Graphics::Font *fontHeader = getFont(kIndy3AboutFontHeader);
-	const Graphics::Font *fontBold = getFont(kIndy3AboutFontBold);
-	const Graphics::Font *fontRegular = getFont(kIndy3AboutFontRegular);
-
-	// This isn't pixel perfect (though perhaps pixel good?). The header
-	// text in particular is rendered differently. It should stay on screen
-	// for 160 frames, or 5.33 seconds.
-
-	fontHeader->drawString(s, "Indiana Jones and the Last Crusade", r1.left - 2, 10, r1.width(), kBlack, Graphics::kTextAlignCenter);
-	fontBold->drawString(s, "The Graphic Adventure", r1.left, 28, r1.width(), kBlack, Graphics::kTextAlignCenter);
-	fontBold->drawString(s, "Mac 1.7 8/17/90, Interpreter version 5.1.6", r1.left, 55, r1.width(), kBlack, Graphics::kTextAlignCenter);
-	fontRegular->drawString(s, "TM & \xA9 1990 LucasArts Entertainment Company.  All rights reserved.", r1.left + 1, 88, r1.width(), kBlack, Graphics::kTextAlignCenter);
-
 	window->update();
 
 	if (delay(5333))
@@ -1950,6 +2046,7 @@ void MacIndy3Gui::showAboutDialog() {
 		window->update();
 		skip = false;
 	}
+#endif
 
 	_windowManager->pushCursor(Graphics::kMacCursorArrow, nullptr);
 
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index af594d648d6..2cfe7e2493d 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -158,7 +158,7 @@ public:
 
 	SimpleWindow *drawBanner(char *message);
 
-	bool delay(uint32 ms);
+	int delay(uint32 ms);
 	SimpleWindow *openWindow(Common::Rect bounds, SimpleWindowStyle style = kStyleNormal);
 };
 


Commit: 4a5b7a471c4d46a12a77c31ccf99aeaaf56ba840
    https://github.com/scummvm/scummvm/commit/4a5b7a471c4d46a12a77c31ccf99aeaaf56ba840
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Indy 3 Mac About cleanups

The first text box is now drawn almost almost correctly, and the whole
text box is handled by a single function. Something's off about the
outline font, but that's a separate issue.

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index ba952fd79b6..7ee30b5cdef 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -291,7 +291,7 @@ Common::KeyState ScummEngine::mac_showOldStyleBannerAndPause(const char *msg, in
 
 // Very simple window class
 
-MacGui::SimpleWindow::SimpleWindow(OSystem *system, Graphics::Surface *from, Common::Rect bounds, SimpleWindowStyle style) : _system(system), _from(from), _bounds(bounds) {
+MacGui::SimpleWindow::SimpleWindow(MacGui *gui, OSystem *system, Graphics::Surface *from, Common::Rect bounds, SimpleWindowStyle style) : _gui(gui), _system(system), _from(from), _bounds(bounds) {
 	_backup = new Graphics::Surface();
 	_backup->create(bounds.width(), bounds.height(), Graphics::PixelFormat::createFormatCLUT8());
 	_backup->copyRectToSurface(*_from, 0, 0, bounds);
@@ -416,10 +416,48 @@ void MacGui::SimpleWindow::plotPixel(int x, int y, int color, void *data) {
 	s->setPixel(x, y, color);
 }
 
-void MacGui::SimpleWindow::drawTextBox(Common::Rect r) {
+void MacGui::SimpleWindow::drawTextBox(Common::Rect r, TextLine *lines) {
 	Graphics::drawRoundRect(r, 9, kWhite, true, plotPixel, this);
 	Graphics::drawRoundRect(r, 9, kBlack, false, plotPixel, this);
 	markRectAsDirty(r);
+
+	Graphics::Surface *s = innerSurface();
+
+	if (!lines)
+		return;
+
+	for (int i = 0; lines[i].str; i++) {
+		const Graphics::Font *f1 = nullptr;
+		const Graphics::Font *f2 = nullptr;
+
+		switch (lines[i].style) {
+		case kStyleHeader:
+			f1 = _gui->getFont(kIndy3AboutFontHeaderOutside);
+			f2 = _gui->getFont(kIndy3AboutFontHeaderInside);
+			break;
+		case kStyleBold:
+			f1 = _gui->getFont(kIndy3AboutFontBold);
+			break;
+		case kStyleRegular:
+			f1 = _gui->getFont(kIndy3AboutFontRegular);
+			break;
+		}
+
+		const char *msg = lines[i].str;
+		int x = r.left + lines[i].x;
+		int y = r.top + lines[i].y;
+		Graphics::TextAlign align = lines[i].align;
+		int width = r.right - x;
+
+		if (lines[i].style == kStyleHeader) {
+			f1->drawString(s, msg, x + 1, y + 1, width - 1, kBlack, align);
+			f2->drawString(s, msg, x + 3, y + 1, width - 2, kBlack, align);
+			f1->drawString(s, msg, x, y, width, kBlack, align);
+			f2->drawString(s, msg, x + 2, y, width, kWhite, align);
+		} else {
+			f1->drawString(s, msg, x, y, width, kBlack, align);
+		}
+	}
 }
 
 // ===========================================================================
@@ -752,7 +790,7 @@ int MacGui::delay(uint32 ms) {
 }
 
 MacGui::SimpleWindow *MacGui::openWindow(Common::Rect bounds, SimpleWindowStyle style) {
-	return new SimpleWindow(_system, _surface, bounds, style);
+	return new SimpleWindow(this, _system, _surface, bounds, style);
 }
 
 // ===========================================================================
@@ -1757,10 +1795,16 @@ void MacIndy3Gui::getFontParams(FontId fontId, int &id, int &size, int &slant) {
 	// headline. The rest of the Indy 3 verb GUI uses Geneva.
 
 	switch (fontId) {
-	case FontId::kIndy3AboutFontHeader:
+	case FontId::kIndy3AboutFontHeaderOutside:
 		id = _gameFontId;
 		size = 12;
-		slant = Graphics::kMacFontItalic | Graphics::kMacFontBold | Graphics::kMacFontShadow | Graphics::kMacFontCondense;
+		slant = Graphics::kMacFontItalic | Graphics::kMacFontBold | Graphics::kMacFontOutline;
+		break;
+
+	case FontId::kIndy3AboutFontHeaderInside:
+		id = _gameFontId;
+		size = 12;
+		slant = Graphics::kMacFontItalic | Graphics::kMacFontBold | Graphics::kMacFontExtend;
 		break;
 
 	case FontId::kIndy3AboutFontBold:
@@ -1865,16 +1909,24 @@ void MacIndy3Gui::showAboutDialog() {
 	int trolleyWaitFrames = 50;
 	int textWaitFrames = 53;
 
+
+	// These strings are part of the STRS resource, but I don't know how to
+	// safely read them from there yet. So hard-coded it is for now.
+
+	TextLine page1[] = {
+		{ 21, 4, kStyleHeader, Graphics::kTextAlignLeft, "Indiana Jones and the Last Crusade" },
+		{ 0, 22, kStyleBold, Graphics::kTextAlignCenter, "The Graphic Adventure" },
+		{ 0, 49, kStyleBold, Graphics::kTextAlignCenter, "Mac 1.7 8/17/90, Interpreter version 5.1.6" },
+		{ 1, 82, kStyleRegular, Graphics::kTextAlignCenter, "TM & \xA9 1990 LucasArts Entertainment Company.  All rights reserved." },
+		{ 0, 0, kStyleRegular, Graphics::kTextAlignLeft, nullptr }
+	};
+
 	// Header texts aren't rendered correctly. Apparently the original does
 	// its own shadowing by drawing the text twice, but that ends up
 	// looking even worse. Perhaps I have to draw the text three times
 	// once to fill it), or maybe our Mac text rendering just isn't as good
 	// as it needs to be yet.
 
-	const Graphics::Font *fontHeader = getFont(kIndy3AboutFontHeader);
-	const Graphics::Font *fontBold = getFont(kIndy3AboutFontBold);
-	const Graphics::Font *fontRegular = getFont(kIndy3AboutFontRegular);
-
 	bool changeScene = false;
 
 	while (true && !_vm->shouldQuit()) {
@@ -1928,32 +1980,23 @@ void MacIndy3Gui::showAboutDialog() {
 			switch (scene) {
 			case 1:
 				clearAboutDialog(window);
-				window->drawTextBox(r1);
-
-				// These strings are part of the STRS resource,
-				// but I don't know how to safely read them
-				// from there.
-
-				fontHeader->drawString(s, "Indiana Jones and the Last Crusade", r1.left - 2, 10, r1.width(), kBlack, Graphics::kTextAlignCenter);
-				fontBold->drawString(s, "The Graphic Adventure", r1.left, 28, r1.width(), kBlack, Graphics::kTextAlignCenter);
-				fontBold->drawString(s, "Mac 1.7 8/17/90, Interpreter version 5.1.6", r1.left, 55, r1.width(), kBlack, Graphics::kTextAlignCenter);
-				fontRegular->drawString(s, "TM & \xA9 1990 LucasArts Entertainment Company.  All rights reserved.", r1.left + 1, 88, r1.width(), kBlack, Graphics::kTextAlignCenter);
+				window->drawTextBox(r1, page1);
 				break;
 
 			case 2:
 				clearAboutDialog(window);
-				window->drawTextBox(r2);
+				window->drawTextBox(r2, nullptr);
 				break;
 
 			case 3:
 				// Don't clear. The trolley is still on screen
 				// and only the text changes.
-				window->drawTextBox(r2);
+				window->drawTextBox(r2, nullptr);
 				break;
 
 			case 4:
 				clearAboutDialog(window);
-				window->drawTextBox(r1);
+				window->drawTextBox(r1, nullptr);
 				break;
 			}
 
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 2cfe7e2493d..1a4555420f3 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -22,11 +22,12 @@
 #ifndef SCUMM_GFX_MAC_H
 #define SCUMM_GFX_MAC_H
 
+#include "graphics/font.h"
+
 class OSystem;
 
 namespace Graphics {
 struct Surface;
-class Font;
 class MacWindowManager;
 }
 
@@ -71,6 +72,20 @@ protected:
 	};
 
 public:
+	enum TextStyle {
+		kStyleHeader,
+		kStyleBold,
+		kStyleRegular
+	};
+
+	struct TextLine {
+		int x;
+		int y;
+		TextStyle style;
+		Graphics::TextAlign align;
+		const char *str;
+	};
+
 	enum SimpleWindowStyle {
 		kStyleNormal,
 		kStyleRounded
@@ -78,6 +93,7 @@ public:
 
 	class SimpleWindow {
 	private:
+		MacGui *_gui;
 		OSystem *_system;
 		Common::Rect _bounds;
 		int _margin;
@@ -91,7 +107,7 @@ public:
 
 		void copyToScreen(Graphics::Surface *s = nullptr);
 	public:
-		SimpleWindow(OSystem *system, Graphics::Surface *from, Common::Rect bounds, SimpleWindowStyle style = kStyleNormal);
+		SimpleWindow(MacGui *gui, OSystem *system, Graphics::Surface *from, Common::Rect bounds, SimpleWindowStyle style = kStyleNormal);
 		~SimpleWindow();
 
 		Graphics::Surface *surface() { return &_surface; }
@@ -106,7 +122,7 @@ public:
 
 		void fillPattern(Common::Rect r, uint16 pattern);
 		void drawSprite(Graphics::Surface *sprite, int x, int y, Common::Rect clipRect);
-		void drawTextBox(Common::Rect r);
+		void drawTextBox(Common::Rect r, TextLine *lines);
 	};
 
 	enum FontId {
@@ -114,7 +130,8 @@ public:
 
 		kIndy3AboutFontRegular,
 		kIndy3AboutFontBold,
-		kIndy3AboutFontHeader,
+		kIndy3AboutFontHeaderInside,
+		kIndy3AboutFontHeaderOutside,
 
 		kIndy3FontSmall,
 		kIndy3FontMedium,


Commit: c685c32d7621fe128519ebc5d6430775e0840fbc
    https://github.com/scummvm/scummvm/commit/c685c32d7621fe128519ebc5d6430775e0840fbc
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Add three more Indy 3 Mac About text pages

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 7ee30b5cdef..f4b18acf1cf 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -416,7 +416,7 @@ void MacGui::SimpleWindow::plotPixel(int x, int y, int color, void *data) {
 	s->setPixel(x, y, color);
 }
 
-void MacGui::SimpleWindow::drawTextBox(Common::Rect r, TextLine *lines) {
+void MacGui::SimpleWindow::drawTextBox(Common::Rect r, const TextLine *lines) {
 	Graphics::drawRoundRect(r, 9, kWhite, true, plotPixel, this);
 	Graphics::drawRoundRect(r, 9, kBlack, false, plotPixel, this);
 	markRectAsDirty(r);
@@ -1913,14 +1913,42 @@ void MacIndy3Gui::showAboutDialog() {
 	// These strings are part of the STRS resource, but I don't know how to
 	// safely read them from there yet. So hard-coded it is for now.
 
-	TextLine page1[] = {
+	#define TEXT_END_MARKER { 0, 0, kStyleRegular, Graphics::kTextAlignLeft, nullptr }
+
+	const TextLine page1[] = {
 		{ 21, 4, kStyleHeader, Graphics::kTextAlignLeft, "Indiana Jones and the Last Crusade" },
 		{ 0, 22, kStyleBold, Graphics::kTextAlignCenter, "The Graphic Adventure" },
 		{ 0, 49, kStyleBold, Graphics::kTextAlignCenter, "Mac 1.7 8/17/90, Interpreter version 5.1.6" },
 		{ 1, 82, kStyleRegular, Graphics::kTextAlignCenter, "TM & \xA9 1990 LucasArts Entertainment Company.  All rights reserved." },
-		{ 0, 0, kStyleRegular, Graphics::kTextAlignLeft, nullptr }
+		TEXT_END_MARKER
+	};
+
+	const TextLine page2[] = {
+		{ 1, 7, kStyleBold, Graphics::kTextAlignCenter, "Macintosh version by" },
+		{ 68, 21, kStyleHeader, Graphics::kTextAlignLeft, "Eric Johnston" },
+		{ 194, 32, kStyleBold, Graphics::kTextAlignLeft, "and" },
+		{ 214, 41, kStyleHeader, Graphics::kTextAlignLeft, "Dan Filner" },
+		TEXT_END_MARKER
+	};
+
+	const TextLine page3[] = {
+		{ 1, 7, kStyleBold, Graphics::kTextAlignCenter, "Macintosh scripting by" },
+		{ 72, 21, kStyleHeader, Graphics::kTextAlignLeft, "Ron Baldwin" },
+		{ 186, 32, kStyleBold, Graphics::kTextAlignLeft, "and" },
+		{ 211, 41, kStyleHeader, Graphics::kTextAlignLeft, "David Fox" },
+		TEXT_END_MARKER
+	};
+
+	const TextLine page4[] = {
+		{ 1, 7, kStyleBold, Graphics::kTextAlignCenter, "Designed and scripted by" },
+		{ 75, 24, kStyleHeader, Graphics::kTextAlignLeft, "Noah Falstein" },
+		{ 132, 44, kStyleHeader, Graphics::kTextAlignLeft, "David Fox" },
+		{ 165, 64, kStyleHeader, Graphics::kTextAlignLeft, "Ron Gilbert" },
+		TEXT_END_MARKER
 	};
 
+	#undef TEXT_END_MARKER
+
 	// Header texts aren't rendered correctly. Apparently the original does
 	// its own shadowing by drawing the text twice, but that ends up
 	// looking even worse. Perhaps I have to draw the text three times
@@ -1985,18 +2013,18 @@ void MacIndy3Gui::showAboutDialog() {
 
 			case 2:
 				clearAboutDialog(window);
-				window->drawTextBox(r2, nullptr);
+				window->drawTextBox(r2, page2);
 				break;
 
 			case 3:
 				// Don't clear. The trolley is still on screen
 				// and only the text changes.
-				window->drawTextBox(r2, nullptr);
+				window->drawTextBox(r2, page3);
 				break;
 
 			case 4:
 				clearAboutDialog(window);
-				window->drawTextBox(r1, nullptr);
+				window->drawTextBox(r1, page4);
 				break;
 			}
 
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 1a4555420f3..d15f7886fb5 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -122,7 +122,7 @@ public:
 
 		void fillPattern(Common::Rect r, uint16 pattern);
 		void drawSprite(Graphics::Surface *sprite, int x, int y, Common::Rect clipRect);
-		void drawTextBox(Common::Rect r, TextLine *lines);
+		void drawTextBox(Common::Rect r, const TextLine *lines);
 	};
 
 	enum FontId {


Commit: bf6ca7ea6f87eaf0daf6b5a03794115f64e2e7fa
    https://github.com/scummvm/scummvm/commit/bf6ca7ea6f87eaf0daf6b5a03794115f64e2e7fa
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Add remaining Mac Indy 3 GUI text pages and remove old code

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index f4b18acf1cf..7fd06517f61 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -760,7 +760,12 @@ MacGui::SimpleWindow *MacGui::drawBanner(char *message) {
 }
 
 int MacGui::delay(uint32 ms) {
-	uint32 to = _system->getMillis() + ms;
+	uint32 to;
+
+	if (ms == (uint32)-1)
+		to = 0xFFFFFFFF;
+	else
+		to = _system->getMillis() + ms;
 
 	while (_system->getMillis() < to) {
 		Common::Event event;
@@ -1889,8 +1894,6 @@ void MacIndy3Gui::showAboutDialog() {
 	clearAboutDialog(window);
 	window->show();
 
-	bool skip = false;
-
 	Graphics::Surface *s = window->innerSurface();
 	Common::Rect clipRect(2, 2, s->w - 4, s->h - 4);
 
@@ -1901,14 +1904,14 @@ void MacIndy3Gui::showAboutDialog() {
 	// 10 fps.
 
 	int scene = 0;
+	int status;
 
 	int trainX = -2;
 	int trolleyX = width + 1;
 	int trolleyFrame = 1;
 	int trolleyFrameDelta = 1;
 	int trolleyWaitFrames = 50;
-	int textWaitFrames = 53;
-
+	int waitFrames = 53;
 
 	// These strings are part of the STRS resource, but I don't know how to
 	// safely read them from there yet. So hard-coded it is for now.
@@ -1947,6 +1950,35 @@ void MacIndy3Gui::showAboutDialog() {
 		TEXT_END_MARKER
 	};
 
+	const TextLine page5[] = {
+		{ 1, 7, kStyleBold, Graphics::kTextAlignCenter, "SCUMM Story System" },
+		{ 1, 17, kStyleBold, Graphics::kTextAlignCenter, "created by" },
+		{ 105, 36, kStyleHeader, Graphics::kTextAlignLeft, "Ron Gilbert" },
+		{ 170, 52, kStyleBold, Graphics::kTextAlignLeft, "and" },
+		{ 130, 66, kStyleHeader, Graphics::kTextAlignLeft, "Aric Wilmunder" },
+		TEXT_END_MARKER
+	};
+
+	const TextLine page6[] = {
+		{ 1, 19, kStyleBold, Graphics::kTextAlignCenter, "Stumped?  Indy hint books are available!" },
+		{ 86, 36, kStyleRegular, Graphics::kTextAlignLeft, "In the U.S. call" },
+		{ 160, 37, kStyleBold, Graphics::kTextAlignLeft, "1 (800) STAR-WARS" },
+		{ 160, 46, kStyleRegular, Graphics::kTextAlignLeft, "that\xD5s  1 (800) 782-7927" },
+		{ 90, 66, kStyleRegular, Graphics::kTextAlignLeft, "In Canada call" },
+		{ 160, 67, kStyleBold, Graphics::kTextAlignLeft, "1 (800) 828-7927" },
+		TEXT_END_MARKER
+	};
+
+	const TextLine page7[] = {
+		{ 1, 17, kStyleBold, Graphics::kTextAlignCenter, "Need a hint NOW?  Having problems?" },
+		{ 53, 31, kStyleRegular, Graphics::kTextAlignLeft, "For hints or technical support call" },
+		{ 215, 32, kStyleBold, Graphics::kTextAlignLeft, "1 (900) 740-JEDI" },
+		{ 1, 46, kStyleRegular, Graphics::kTextAlignCenter, "The charge is 75\xA2 per minute." },
+		{ 1, 56, kStyleRegular, Graphics::kTextAlignCenter, "(You must have your parents\xD5 permission to" },
+		{ 1, 66, kStyleRegular, Graphics::kTextAlignCenter, "call this number if you are under 18.)" },
+		TEXT_END_MARKER
+	};
+
 	#undef TEXT_END_MARKER
 
 	// Header texts aren't rendered correctly. Apparently the original does
@@ -1969,7 +2001,10 @@ void MacIndy3Gui::showAboutDialog() {
 			break;
 
 		case 1:
-			if (--textWaitFrames == 0)
+		case 4:
+		case 5:
+		case 6:
+			if (--waitFrames == 0)
 				changeScene = true;
 			break;
 
@@ -1995,15 +2030,15 @@ void MacIndy3Gui::showAboutDialog() {
 		}
 
 		window->update();
+		status = delay(100);
 
-		int status = delay(100);
 		if (status == 2)
 			break;
 
 		if (status == 1 || changeScene) {
 			changeScene = false;
-
 			scene++;
+			waitFrames = 53;
 
 			switch (scene) {
 			case 1:
@@ -2026,132 +2061,36 @@ void MacIndy3Gui::showAboutDialog() {
 				clearAboutDialog(window);
 				window->drawTextBox(r1, page4);
 				break;
-			}
 
-			window->update();
-
-			if (scene > 7)
+			case 5:
+				window->drawTextBox(r1, page5);
 				break;
-		}
-	}
-
-#if 0
-	// The train seems to move at 12 pixels at a time. When recorded at
-	// 30 fps, it moves every 3 frames. I'm going to assume that's the
-	// intended frame rate.
-
-	for (x = 0; x < train.w; x += 12) {
-		window->drawSprite(&train, 2 - x, 40, clipRect);
-		window->update();
-
-		if (delay(100)) {
-			skip = true;
-			break;
-		}
-	}
-
-	if (skip) {
-		clearAboutDialog(window);
-		window->update();
-		skip = false;
-	}
-
-	// Page 2 - First text box
-
-	window->drawTextBox(r1);
-
-	window->update();
-
-	if (delay(5333))
-		skip = true;
-
-	clearAboutDialog(window);
-
-	// Page 3 - The trolley
 
-	// The trolley moves 4 pixels at a time every three frames, and the
-	// animation frame changes every time it moves. But the animation cycle
-	// seems semi-random to me. I think this is a case of not caring too
-	// deeply about pixel accuracy as long as everything stops and starts
-	// at the right time, moves at the right speed, and stops and starts
-	// on the correct animation frame.
-
-	window->drawTextBox(r2);
-
-	int trolleyFrame = 1;
-	int trolleyFrameDelta = 1;
-
-	for (x = width + 1; !skip && x >= -85; x -= 4) {
-		window->drawSprite(&trolley[trolleyFrame], x, 78, clipRect);
-		window->update();
+			case 6:
+				window->drawTextBox(r1, page6);
+				break;
 
-		if (x == 161) {
-			if (delay(5000)) {
-				skip = true;
+			case 7:
+				window->drawTextBox(r1, page7);
 				break;
 			}
 
-			window->drawTextBox(r2);
-		}
-
-		trolleyFrame += trolleyFrameDelta;
-		if (trolleyFrame < 0 || trolleyFrame > 2) {
-			trolleyFrame = 1;
-			trolleyFrameDelta = -trolleyFrameDelta;
-		}
+			window->update();
 
-		if (delay(100)) {
-			skip = true;
-			break;
+			if (scene > 7)
+				break;
 		}
 	}
 
-	if (delay(5333))
-		skip = true;
-
-	window->drawTextBox(r1);
-	window->update();
-
-	if (skip) {
-		clearAboutDialog(window);
-		window->update();
-		skip = false;
-	}
-#endif
-
 	_windowManager->pushCursor(Graphics::kMacCursorArrow, nullptr);
 
-	bool shouldQuit = false;
-	bool shouldQuitEngine = false;
-
-	while (!shouldQuit) {
-		Common::Event event;
-
-		while (_system->getEventManager()->pollEvent(event)) {
-			switch (event.type) {
-			case Common::EVENT_QUIT:
-				shouldQuit = true;
-				shouldQuitEngine = true;
-				break;
-
-			case Common::EVENT_LBUTTONDOWN:
-				shouldQuit = true;
-				break;
-
-			default:
-				break;
-			}
-		}
-
-		_system->updateScreen();
-		_system->delayMillis(10);
-	}
+	status = delay(-1);
 
 	_windowManager->popCursor();
 
 	delete window;
 
-	if (shouldQuitEngine)
+	if (status == 2)
 		debug("Quit everything");
 }
 


Commit: 2798b124d17c47631201855a1c74e40f52c27469
    https://github.com/scummvm/scummvm/commit/2798b124d17c47631201855a1c74e40f52c27469
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Mac Indy 3 About animation fixes

Added the final button, which is actually just another text box.

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 7fd06517f61..d6a2d202388 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -416,9 +416,9 @@ void MacGui::SimpleWindow::plotPixel(int x, int y, int color, void *data) {
 	s->setPixel(x, y, color);
 }
 
-void MacGui::SimpleWindow::drawTextBox(Common::Rect r, const TextLine *lines) {
-	Graphics::drawRoundRect(r, 9, kWhite, true, plotPixel, this);
-	Graphics::drawRoundRect(r, 9, kBlack, false, plotPixel, this);
+void MacGui::SimpleWindow::drawTextBox(Common::Rect r, const TextLine *lines, int arc) {
+	Graphics::drawRoundRect(r, arc, kWhite, true, plotPixel, this);
+	Graphics::drawRoundRect(r, arc, kBlack, false, plotPixel, this);
 	markRectAsDirty(r);
 
 	Graphics::Surface *s = innerSurface();
@@ -1911,7 +1911,7 @@ void MacIndy3Gui::showAboutDialog() {
 	int trolleyFrame = 1;
 	int trolleyFrameDelta = 1;
 	int trolleyWaitFrames = 50;
-	int waitFrames = 53;
+	int waitFrames;
 
 	// These strings are part of the STRS resource, but I don't know how to
 	// safely read them from there yet. So hard-coded it is for now.
@@ -1979,6 +1979,11 @@ void MacIndy3Gui::showAboutDialog() {
 		TEXT_END_MARKER
 	};
 
+	const TextLine page8[] = {
+		{ 1, 1, kStyleBold, Graphics::kTextAlignCenter, "Click to continue" },
+		TEXT_END_MARKER
+	};
+
 	#undef TEXT_END_MARKER
 
 	// Header texts aren't rendered correctly. Apparently the original does
@@ -2004,6 +2009,7 @@ void MacIndy3Gui::showAboutDialog() {
 		case 4:
 		case 5:
 		case 6:
+		case 7:
 			if (--waitFrames == 0)
 				changeScene = true;
 			break;
@@ -2013,7 +2019,8 @@ void MacIndy3Gui::showAboutDialog() {
 			window->drawSprite(&trolley[trolleyFrame], trolleyX, 78, clipRect);
 
 			if (scene == 2 && trolleyX == 161 && trolleyWaitFrames > 0) {
-				trolleyWaitFrames--;
+				if (--trolleyWaitFrames == 0)
+					changeScene = true;
 			} else {
 				trolleyX -= 4;
 				trolleyFrame += trolleyFrameDelta;
@@ -2067,24 +2074,29 @@ void MacIndy3Gui::showAboutDialog() {
 				break;
 
 			case 6:
+				waitFrames = 106;
 				window->drawTextBox(r1, page6);
 				break;
 
 			case 7:
+				waitFrames = 33;
 				window->drawTextBox(r1, page7);
 				break;
+
+			case 8:
+				window->drawTextBox(Common::Rect(142, 106, 262, 119), page8, 3);
+				break;
 			}
 
 			window->update();
 
-			if (scene > 7)
+			if (scene >= 8)
 				break;
 		}
 	}
 
-	_windowManager->pushCursor(Graphics::kMacCursorArrow, nullptr);
-
-	status = delay(-1);
+	if (status != 2)
+		status = delay(-1);
 
 	_windowManager->popCursor();
 
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index d15f7886fb5..a8575a1b602 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -122,7 +122,7 @@ public:
 
 		void fillPattern(Common::Rect r, uint16 pattern);
 		void drawSprite(Graphics::Surface *sprite, int x, int y, Common::Rect clipRect);
-		void drawTextBox(Common::Rect r, const TextLine *lines);
+		void drawTextBox(Common::Rect r, const TextLine *lines, int arc = 9);
 	};
 
 	enum FontId {


Commit: 16ac86cdeb5d1c4b70beaa02ae136aa93c5ce6ad
    https://github.com/scummvm/scummvm/commit/16ac86cdeb5d1c4b70beaa02ae136aa93c5ce6ad
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
GRAPHICS: Calculate bitmapOffset correctly. Again.

Add, don't set, the extra bitmap offset for italics to keep glyphs from
overlapping in memory.

Changed paths:
    graphics/fonts/macfont.cpp


diff --git a/graphics/fonts/macfont.cpp b/graphics/fonts/macfont.cpp
index 3111b42dd41..aa078ad44ff 100644
--- a/graphics/fonts/macfont.cpp
+++ b/graphics/fonts/macfont.cpp
@@ -566,7 +566,7 @@ MacFONTFont *MacFONTFont::scaleFont(const MacFONTFont *src, int newSize, int sla
 	if (slant & kMacFontOutline)
 		bitmapOffset += 2;
 	if (slant & kMacFontItalic)
-		bitmapOffset = (data._fRectHeight - 1) / SLANTDEEP;
+		bitmapOffset += (data._fRectHeight - 1) / SLANTDEEP;
 	if (slant & kMacFontShadow)
 		bitmapOffset++;
 


Commit: 67d79cd5d0d90f4c9e57283d70f8834c25fdbb0b
    https://github.com/scummvm/scummvm/commit/67d79cd5d0d90f4c9e57283d70f8834c25fdbb0b
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Pause sounds during Mac dialogs and try to fix cursor regression

The cursor regression was something I introduced when moving Mac cursor
handling into MacGui. Hopefully it works more like it used to again now.

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index d6a2d202388..685aadd918b 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -292,6 +292,8 @@ Common::KeyState ScummEngine::mac_showOldStyleBannerAndPause(const char *msg, in
 // Very simple window class
 
 MacGui::SimpleWindow::SimpleWindow(MacGui *gui, OSystem *system, Graphics::Surface *from, Common::Rect bounds, SimpleWindowStyle style) : _gui(gui), _system(system), _from(from), _bounds(bounds) {
+	_pauseToken = _gui->_vm->pauseEngine();
+
 	_backup = new Graphics::Surface();
 	_backup->create(bounds.width(), bounds.height(), Graphics::PixelFormat::createFormatCLUT8());
 	_backup->copyRectToSurface(*_from, 0, 0, bounds);
@@ -342,6 +344,7 @@ MacGui::SimpleWindow::~SimpleWindow() {
 	copyToScreen(_backup);
 	_backup->free();
 	delete _backup;
+	_pauseToken.clear();
 }
 
 void MacGui::SimpleWindow::copyToScreen(Graphics::Surface *s) {
@@ -496,7 +499,8 @@ void MacGui::initialize() {
 		{ 0, NULL, 0, 0, false }
 	};
 
-	Common::String aboutMenuDef = "About " + name() + "...<B";
+	// TODO: This can be found in the STRS resource
+	Common::String aboutMenuDef = "About " + name() + "...<B;(-";
 
 	menu->addStaticMenus(menuSubItems);
 	menu->createSubMenuFromString(0, aboutMenuDef.c_str(), 0);
@@ -739,7 +743,8 @@ void MacGui::updateWindowManager() {
 		}
 	} else {
 		if (_menuIsActive) {
-			_windowManager->popCursor();
+			if (_windowManager->getCursorType() == Graphics::kMacCursorArrow)
+				_windowManager->popCursor();
 			CursorMan.showMouse(_cursorWasVisible);
 		}
 	}
@@ -864,7 +869,7 @@ void MacLoomGui::setupCursor(int &width, int &height, int &hotspotX, int &hotspo
 		hotspotY = macCursor.getHotspotY();
 		animate = 0;
 
-		_windowManager->pushCustomCursor(&macCursor);
+		_windowManager->replaceCursor(Graphics::kMacCursorCustom, &macCursor);
 	}
 
 	delete curs;
@@ -1757,9 +1762,6 @@ MacIndy3Gui::~MacIndy3Gui() {
 }
 
 void MacIndy3Gui::setupCursor(int &width, int &height, int &hotspotX, int &hotspotY, int &animate) {
-	if (_windowManager->getCursorType() == Graphics::kMacCursorCustom)
-		return;
-
 	const byte buf[15 * 15] = {
 		3, 3, 3, 3, 3, 3, 0, 1, 0, 3, 3, 3, 3, 3, 3,
 		3, 3, 3, 3, 3, 3, 0, 1, 0, 3, 3, 3, 3, 3, 3,
@@ -1782,7 +1784,7 @@ void MacIndy3Gui::setupCursor(int &width, int &height, int &hotspotX, int &hotsp
 	hotspotX = hotspotY = 7;
 	animate = 0;
 
-	_windowManager->pushCustomCursor(buf, width, height, hotspotX, hotspotY, 3);
+	_windowManager->replaceCustomCursor(buf, width, height, hotspotX, hotspotY, 3);
 }
 
 const Graphics::Font *MacIndy3Gui::getFontByScummId(int32 id) {
@@ -1913,8 +1915,8 @@ void MacIndy3Gui::showAboutDialog() {
 	int trolleyWaitFrames = 50;
 	int waitFrames;
 
-	// These strings are part of the STRS resource, but I don't know how to
-	// safely read them from there yet. So hard-coded it is for now.
+	// TODO: These strings are part of the STRS resource, but I don't know
+	// how to safely read them from there yet. So hard-coded it is for now.
 
 	#define TEXT_END_MARKER { 0, 0, kStyleRegular, Graphics::kTextAlignLeft, nullptr }
 
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index a8575a1b602..4955bd7f6b6 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -98,6 +98,8 @@ public:
 		Common::Rect _bounds;
 		int _margin;
 
+		PauseToken _pauseToken;
+
 		Graphics::Surface *_from = nullptr;
 		Graphics::Surface *_backup = nullptr;
 		Graphics::Surface _surface;


Commit: bb6860de1b2e8256c5f9fa5279a9ff07ebb8b295
    https://github.com/scummvm/scummvm/commit/bb6860de1b2e8256c5f9fa5279a9ff07ebb8b295
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Early Mac Loom About experimentation

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 685aadd918b..abacf358e1d 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -419,6 +419,17 @@ void MacGui::SimpleWindow::plotPixel(int x, int y, int color, void *data) {
 	s->setPixel(x, y, color);
 }
 
+void MacGui::SimpleWindow::plotPattern(int x, int y, int pattern, void *data) {
+	const uint16 patterns[] = {
+		0x0000, 0x8282, 0x5A5A, 0x7D7D, 0xFFFF
+	};
+
+	MacGui::SimpleWindow *window = (MacGui::SimpleWindow *)data;
+	Graphics::Surface *s = window->innerSurface();
+	int bit = 0x8000 >> (4 * (y % 4) + (x % 4));
+	s->setPixel(x, y, (patterns[pattern] & bit) ? kBlack : kWhite);
+}
+
 void MacGui::SimpleWindow::drawTextBox(Common::Rect r, const TextLine *lines, int arc) {
 	Graphics::drawRoundRect(r, arc, kWhite, true, plotPixel, this);
 	Graphics::drawRoundRect(r, arc, kBlack, false, plotPixel, this);
@@ -882,6 +893,36 @@ bool MacLoomGui::handleMenu(int id, Common::String &name) {
 }
 
 void MacLoomGui::showAboutDialog() {
+	// The About window is not a a dialog resource. Its size appears to be
+	// hard-coded (416x166), and it's drawn centered. The graphics are in
+	// ...
+
+	int width = 416;
+	int height = 166;
+	int x = (640 - width) / 2;
+	int y = (400 - height) / 2;
+
+	Common::Rect bounds(x, y, x + width, y + height);
+	SimpleWindow *window = openWindow(bounds);
+
+	Common::Rect r(0, 0, 400, 150);
+
+	int pattern = 0;
+	int patternDelta = 1;
+
+	for (int i = 0; i <= 30; i++) {
+		Graphics::drawRoundRect(r, pattern, pattern, true, SimpleWindow::plotPattern, window);
+		pattern += patternDelta;
+		if (pattern >= 4 || pattern <= 0)
+			patternDelta = -patternDelta;
+		r.grow(-2);
+	}
+
+	Graphics::drawRoundRect(r, 9, 7, true, SimpleWindow::plotPattern, window);
+
+	window->show();
+
+	delay(-1);
 }
 
 // ===========================================================================
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 4955bd7f6b6..87f1be6f6c4 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -121,6 +121,7 @@ public:
 		void update();
 
 		static void plotPixel(int x, int y, int color, void *data);
+		static void plotPattern(int x, int y, int pattern, void *data);
 
 		void fillPattern(Common::Rect r, uint16 pattern);
 		void drawSprite(Graphics::Surface *sprite, int x, int y, Common::Rect clipRect);


Commit: ac57efc01b96acc43e4e58864eb8fdf51df9d3b8
    https://github.com/scummvm/scummvm/commit/ac57efc01b96acc43e4e58864eb8fdf51df9d3b8
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: More work on animating the Mac Loom About dialog

Something isn't right about the way it draws the borders. It works for
the normal cases, but not when it draws over other things. I need to
think about that, but there are other thigns I need to fix first.

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index abacf358e1d..b2e8c6eac49 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -36,6 +36,8 @@
 #include "scumm/usage_bits.h"
 #include "scumm/verbs.h"
 
+#define TEXT_END_MARKER { 0, 0, kStyleRegular, Graphics::kTextAlignLeft, nullptr }
+
 namespace Scumm {
 
 void ScummEngine::mac_markScreenAsDirty(int x, int y, int w, int h) {
@@ -347,7 +349,7 @@ MacGui::SimpleWindow::~SimpleWindow() {
 	_pauseToken.clear();
 }
 
-void MacGui::SimpleWindow::copyToScreen(Graphics::Surface *s) {
+void MacGui::SimpleWindow::copyToScreen(Graphics::Surface *s) const {
 	if (s) {
 		_from->copyRectToSurface(*s, _bounds.left, _bounds.top, Common::Rect(0, 0, _bounds.width(), _bounds.height()));
 	}
@@ -363,7 +365,13 @@ void MacGui::SimpleWindow::markRectAsDirty(Common::Rect r) {
 	_dirtyRects.push_back(r);
 }
 
-void MacGui::SimpleWindow::update() {
+void MacGui::SimpleWindow::update(bool fullRedraw) {
+	if (fullRedraw) {
+debug("Full redraw");
+		_dirtyRects.clear();
+		markRectAsDirty(Common::Rect(0, 0, _innerSurface.w, _innerSurface.h));
+	}
+
 	for (uint i = 0; i < _dirtyRects.size(); i++) {
 		_system->copyRectToScreen(
 			_innerSurface.getBasePtr(_dirtyRects[i].left, _dirtyRects[i].top),
@@ -386,7 +394,12 @@ void MacGui::SimpleWindow::fillPattern(Common::Rect r, uint16 pattern) {
 	markRectAsDirty(r);
 }
 
-void MacGui::SimpleWindow::drawSprite(Graphics::Surface *sprite, int x, int y, Common::Rect(clipRect)) {
+void MacGui::SimpleWindow::drawSprite(const Graphics::Surface *sprite, int x, int y) {
+	_innerSurface.copyRectToSurface(*sprite, x, y, Common::Rect(0, 0, sprite->w, sprite->h));
+	markRectAsDirty(Common::Rect(x, y, x + sprite->w, y + sprite->h));
+}
+
+void MacGui::SimpleWindow::drawSprite(const Graphics::Surface *sprite, int x, int y, Common::Rect(clipRect)) {
 	Common::Rect subRect(0, 0, sprite->w, sprite->h);
 
 	if (x < clipRect.left) {
@@ -421,13 +434,17 @@ void MacGui::SimpleWindow::plotPixel(int x, int y, int color, void *data) {
 
 void MacGui::SimpleWindow::plotPattern(int x, int y, int pattern, void *data) {
 	const uint16 patterns[] = {
-		0x0000, 0x8282, 0x5A5A, 0x7D7D, 0xFFFF
+		0x0000, 0x2828, 0xA5A5, 0xD7D7,
+		0xFFFF,	0xD7D7, 0xA5A5, 0x2828
 	};
 
 	MacGui::SimpleWindow *window = (MacGui::SimpleWindow *)data;
 	Graphics::Surface *s = window->innerSurface();
 	int bit = 0x8000 >> (4 * (y % 4) + (x % 4));
-	s->setPixel(x, y, (patterns[pattern] & bit) ? kBlack : kWhite);
+	if (patterns[pattern] & bit)
+		s->setPixel(x, y, kBlack);
+	else
+		s->setPixel(x, y, kWhite);
 }
 
 void MacGui::SimpleWindow::drawTextBox(Common::Rect r, const TextLine *lines, int arc) {
@@ -446,14 +463,14 @@ void MacGui::SimpleWindow::drawTextBox(Common::Rect r, const TextLine *lines, in
 
 		switch (lines[i].style) {
 		case kStyleHeader:
-			f1 = _gui->getFont(kIndy3AboutFontHeaderOutside);
-			f2 = _gui->getFont(kIndy3AboutFontHeaderInside);
+			f1 = _gui->getFont(kAboutFontHeaderOutside);
+			f2 = _gui->getFont(kAboutFontHeaderInside);
 			break;
 		case kStyleBold:
-			f1 = _gui->getFont(kIndy3AboutFontBold);
+			f1 = _gui->getFont(kAboutFontBold);
 			break;
 		case kStyleRegular:
-			f1 = _gui->getFont(kIndy3AboutFontRegular);
+			f1 = _gui->getFont(kAboutFontRegular);
 			break;
 		}
 
@@ -479,10 +496,6 @@ void MacGui::SimpleWindow::drawTextBox(Common::Rect r, const TextLine *lines, in
 // Jones and the Last Crusade.
 // ===========================================================================
 
-static void menuCallback(int id, Common::String &name, void *data) {
-	((MacGui *)data)->handleMenu(id, name);
-}
-
 MacGui::MacGui(ScummEngine *vm, Common::String resourceFile) : _vm(vm), _system(_vm->_system), _surface(_vm->_macScreen), _resourceFile(resourceFile) {
 	_fonts.clear();
 }
@@ -491,6 +504,10 @@ MacGui::~MacGui() {
 	delete _windowManager;
 }
 
+void MacGui::menuCallback(int id, Common::String &name, void *data) {
+	((MacGui *)data)->handleMenu(id, name);
+}
+
 void MacGui::initialize() {
 	_windowManager = new Graphics::MacWindowManager(Graphics::kWMModeNoDesktop | Graphics::kWMModeAutohideMenu | Graphics::kWMModalMenuMode | Graphics::kWMModeNoCursorOverride);
 	_windowManager->setEngine(_vm);
@@ -596,7 +613,38 @@ const Graphics::Font *MacGui::getFont(FontId fontId) {
 	return font;
 }
 
-const Graphics::Surface *MacGui::loadPICT(int id) {
+bool MacGui::getFontParams(FontId fontId, int &id, int &size, int &slant) {
+	switch (fontId) {
+	case FontId::kAboutFontHeaderOutside:
+		id = _gameFontId;
+		size = 12;
+		slant = Graphics::kMacFontItalic | Graphics::kMacFontBold | Graphics::kMacFontOutline;
+		return true;
+
+	case FontId::kAboutFontHeaderInside:
+		id = _gameFontId;
+		size = 12;
+		slant = Graphics::kMacFontItalic | Graphics::kMacFontBold | Graphics::kMacFontExtend;
+		return true;
+
+	case FontId::kAboutFontBold:
+		id = _gameFontId;
+		size = 9;
+		slant = Graphics::kMacFontRegular;
+		return true;
+
+	case FontId::kAboutFontRegular:
+		id = Graphics::kMacFontGeneva;
+		size = 9;
+		slant = Graphics::kMacFontRegular;
+		return true;
+
+	default:
+		return false;
+	}
+}
+
+Graphics::Surface *MacGui::loadPict(int id) {
 	Common::MacResManager resource;
 
 	resource.open(_resourceFile);
@@ -614,6 +662,9 @@ const Graphics::Surface *MacGui::loadPICT(int id) {
 	uint16 bottom = res->readUint16BE();
 	uint16 right = res->readUint16BE();
 
+	int width = right - left;
+	int height = bottom - top;
+
 	Graphics::Surface *s = new Graphics::Surface();
 	s->create(right - left, bottom - top, Graphics::PixelFormat::createFormatCLUT8());
 
@@ -624,7 +675,6 @@ const Graphics::Surface *MacGui::loadPICT(int id) {
 		byte value;
 		int x1, x2, y1, y2;
 		int rowBytes;
-		int mode;
 
 		int x = 0;
 		int y = 0;
@@ -653,7 +703,7 @@ const Graphics::Surface *MacGui::loadPICT(int id) {
 			res->skip(2);	// Skip mode
 			res->skip(res->readUint16BE() - 2);	// Skip maskRgn
 
-			for (y = y1; y < y2; y++) {
+			for (y = y1; y < y2 && y < height; y++) {
 				x = x1;
 				size = res->readByte();
 
@@ -679,7 +729,7 @@ const Graphics::Surface *MacGui::loadPICT(int id) {
 							value = res->readByte();
 							size--;
 						}
-						for (int k = 7; k >= 0 && x < x2; k--, x++) {
+						for (int k = 7; k >= 0 && x < x2 && x < width; k--, x++) {
 							if (value & (1 << k))
 								s->setPixel(x, y, kBlack);
 							else
@@ -827,7 +877,10 @@ const Graphics::Font *MacLoomGui::getFontByScummId(int32 id) {
 	}
 }
 
-void MacLoomGui::getFontParams(FontId fontId, int &id, int &size, int &slant) {
+bool MacLoomGui::getFontParams(FontId fontId, int &id, int &size, int &slant) {
+	if (MacGui::getFontParams(fontId, id, size, slant))
+		return true;
+
 	// Loom uses only font size 13 for in-game text, but size 12 is used
 	// for system messages, e.g. the original pause dialog.
 	//
@@ -842,23 +895,25 @@ void MacLoomGui::getFontParams(FontId fontId, int &id, int &size, int &slant) {
 		id = _gameFontId;
 		size = 9;
 		slant = Graphics::kMacFontRegular;
-		break;
+		return true;
 
 	case FontId::kLoomFontMedium:
 		id = _gameFontId;
 		size = 12;
 		slant = Graphics::kMacFontRegular;
-		break;
+		return true;
 
 	case FontId::kLoomFontLarge:
 		id = _gameFontId;
 		size = 13;
 		slant = Graphics::kMacFontRegular;
-		break;
+		return true;
 
 	default:
 		error("MacLoomGui: getFontParams: Unknown font id %d", (int)fontId);
 	}
+
+	return false;
 }
 
 void MacLoomGui::setupCursor(int &width, int &height, int &hotspotX, int &hotspotY, int &animate) {
@@ -895,7 +950,7 @@ bool MacLoomGui::handleMenu(int id, Common::String &name) {
 void MacLoomGui::showAboutDialog() {
 	// The About window is not a a dialog resource. Its size appears to be
 	// hard-coded (416x166), and it's drawn centered. The graphics are in
-	// ...
+	// PICT 5000 and 5001.
 
 	int width = 416;
 	int height = 166;
@@ -904,25 +959,198 @@ void MacLoomGui::showAboutDialog() {
 
 	Common::Rect bounds(x, y, x + width, y + height);
 	SimpleWindow *window = openWindow(bounds);
+	Graphics::Surface *lucasFilm = loadPict(5000);
+	Graphics::Surface *loom = loadPict(5001);
 
-	Common::Rect r(0, 0, 400, 150);
+	// TODO: These strings are part of the STRS resource, but I don't know
+	// how to safely read them from there yet. So hard-coded it is for now.
 
-	int pattern = 0;
-	int patternDelta = 1;
+	const TextLine page1[] = {
+		{ 0, 0, kStyleBold, Graphics::kTextAlignCenter, "PRESENTS" },
+		TEXT_END_MARKER
+	};
 
-	for (int i = 0; i <= 30; i++) {
-		Graphics::drawRoundRect(r, pattern, pattern, true, SimpleWindow::plotPattern, window);
-		pattern += patternDelta;
-		if (pattern >= 4 || pattern <= 0)
-			patternDelta = -patternDelta;
-		r.grow(-2);
-	}
+	const TextLine page2[] = {
+		{ 0, 0, kStyleRegular, Graphics::kTextAlignCenter, "TM & \xA9 1990 LucasArts Entertainment Company.  All rights reserved." },
+		{ 0, 0, kStyleRegular, Graphics::kTextAlignCenter, "Release Version 1.2 25-JAN-91 Interpreter version 5.1.6" },
+		TEXT_END_MARKER
+	};
 
-	Graphics::drawRoundRect(r, 9, 7, true, SimpleWindow::plotPattern, window);
+	const TextLine page3[] = {
+		{ 0, 0, kStyleBold, Graphics::kTextAlignCenter, "Macintosh version by" },
+		{ 0, 0, kStyleHeader, Graphics::kTextAlignCenter, "Eric Johnston" },
+		{ 0, 0, kStyleBold, Graphics::kTextAlignCenter, "Macintosh scripting by" },
+		{ 0, 0, kStyleHeader, Graphics::kTextAlignCenter, "Ron Baldwin" },
+		TEXT_END_MARKER
+	};
+
+	const TextLine page4[] = {
+		{ 0, 0, kStyleBold, Graphics::kTextAlignCenter, "Original game created by" },
+		{ 0, 0, kStyleHeader, Graphics::kTextAlignCenter, "Brian Moriarty" },
+		TEXT_END_MARKER
+	};
+
+	const TextLine page5[] = {
+		{ 0, 0, kStyleBold, Graphics::kTextAlignCenter, "Produced by" },
+		{ 0, 0, kStyleHeader, Graphics::kTextAlignCenter, "Gregory D. Hammond" },
+		{ 0, 0, kStyleBold, Graphics::kTextAlignCenter, "Macintosh Version Produced by" },
+		{ 0, 0, kStyleHeader, Graphics::kTextAlignCenter, "David Fox" },
+		TEXT_END_MARKER
+	};
+
+	const TextLine page6[] = {
+		{ 0, 0, kStyleBold, Graphics::kTextAlignCenter, "SCUMM Story Stystem" },
+		{ 0, 0, kStyleBold, Graphics::kTextAlignCenter, "created by" },
+		{ 0, 0, kStyleHeader, Graphics::kTextAlignCenter, "Ron Gilbert" },
+		{ 0, 0, kStyleBold, Graphics::kTextAlignCenter, "and" },
+		{ 0, 0, kStyleHeader, Graphics::kTextAlignCenter, "Aric Wilmunder" },
+		TEXT_END_MARKER
+	};
+
+	const TextLine page7[] = {
+		{ 0, 0, kStyleBold, Graphics::kTextAlignCenter, "SCUMM Story Stystem" },
+		{ 0, 0, kStyleBold, Graphics::kTextAlignCenter, "created by" },
+		{ 0, 0, kStyleHeader, Graphics::kTextAlignCenter, "Ron Gilbert" },
+		{ 0, 0, kStyleBold, Graphics::kTextAlignCenter, "and" },
+		{ 0, 0, kStyleHeader, Graphics::kTextAlignCenter, "Aric Wilmunder" },
+		TEXT_END_MARKER
+	};
+
+	const TextLine page8[] = {
+		{ 1, 19, kStyleBold, Graphics::kTextAlignCenter, "Stumped?  Loom hint books are available!" },
+		{ 86, 36, kStyleRegular, Graphics::kTextAlignLeft, "In the U.S. call" },
+		{ 160, 37, kStyleBold, Graphics::kTextAlignLeft, "1 (800) STAR-WARS" },
+		{ 160, 46, kStyleRegular, Graphics::kTextAlignLeft, "that\xD5s  1 (800) 782-7927" },
+		{ 90, 66, kStyleRegular, Graphics::kTextAlignLeft, "In Canada call" },
+		{ 160, 67, kStyleBold, Graphics::kTextAlignLeft, "1 (800) 828-7927" },
+		TEXT_END_MARKER
+	};
+
+	const TextLine page9[] = {
+		{ 1, 17, kStyleBold, Graphics::kTextAlignCenter, "Need a hint NOW?  Having problems?" },
+		{ 53, 31, kStyleRegular, Graphics::kTextAlignLeft, "For technical support call" },
+		{ 0, 0, kStyleBold, Graphics::kTextAlignLeft, "1 (415) 721-3333" },
+		{ 0, 0, kStyleRegular, Graphics::kTextAlignLeft, "For hints call" },
+
+		{ 215, 32, kStyleBold, Graphics::kTextAlignLeft, "1 (900) 740-JEDI" },
+		{ 1, 46, kStyleRegular, Graphics::kTextAlignCenter, "The charge for the hint line is 75\xA2 per minute." },
+		{ 1, 56, kStyleRegular, Graphics::kTextAlignCenter, "(You must have your parents\xD5 permission to" },
+		{ 1, 66, kStyleRegular, Graphics::kTextAlignCenter, "call this number if you are under 18.)" },
+		TEXT_END_MARKER
+	};
+
+	// I've based the animation speed on what it looks like when Mini vMac
+	// emulates an old black-and-white Mac at normal speed. It looks a bit
+	// different in Basilisk II, but that's probably because it emulates a
+	// much faster Mac.
 
 	window->show();
 
-	delay(-1);
+	int scene = 0;
+	int status;
+
+	Common::Rect r(0, 0, 404, 154);
+	int growth = -2;
+	int growSteps = 37;
+	int pattern;
+	int waitFrames;
+
+	bool changeScene = false;
+	bool fastForward = false;
+
+	while (!_vm->shouldQuit()) {
+		switch (scene) {
+		case 0:
+		case 1:
+		case 3:
+			// This appears to be pixel perfect or at least nearly
+			// so for the outer layers, but breaks down slightly
+			// near the middle.
+
+			pattern = (r.top / 2) % 8;
+
+			Graphics::drawRoundRect(r, 7, pattern, true, SimpleWindow::plotPattern, window);
+
+			if (!fastForward)
+				window->markRectAsDirty(r);
+
+			r.grow(growth);
+
+			if (r.height() <= 6 || r.height() >= 154)
+				growth = -growth;
+
+			if (--growSteps == 0)
+				changeScene = true;
+
+			break;
+
+		case 2:
+			if (--waitFrames == 0)
+				changeScene = true;
+			break;
+		}
+
+		if (!fastForward) {
+			window->update();
+			status = delay(50);
+		}
+
+		// We can't actually skip to the end of a scene, because the
+		// animation has to be drawn.
+
+		if (status == 1)
+			fastForward = true;
+
+		if (status == 2)
+			break;
+
+		if (changeScene) {
+			changeScene = false;
+			scene++;
+
+			switch (scene) {
+			case 1:
+				r.grow(16);
+				growth = -2;
+				growSteps = 25;
+				break;
+
+			case 2:
+				window->drawSprite(lucasFilm, 120, 65);
+				waitFrames = 50;
+				break;
+
+			case 3:
+				growth = -2;
+				growSteps = 23;
+				break;
+			}
+
+			if (fastForward) {
+				fastForward = false;
+				window->update(true);
+			}
+
+			if (scene >= 4)
+				break;
+		}
+	}
+
+	if (status != 2)
+		status = delay(-1);
+
+	_windowManager->popCursor();
+
+	lucasFilm->free();
+	delete lucasFilm;
+
+	loom->free();
+	delete loom;
+
+	delete window;
+
+	if (status == 2)
+		debug("Quit everything");
 }
 
 // ===========================================================================
@@ -1837,69 +2065,50 @@ const Graphics::Font *MacIndy3Gui::getFontByScummId(int32 id) {
 	}
 }
 
-void MacIndy3Gui::getFontParams(FontId fontId, int &id, int &size, int &slant) {
+bool MacIndy3Gui::getFontParams(FontId fontId, int &id, int &size, int &slant) {
+	if (MacGui::getFontParams(fontId, id, size, slant))
+		return true;
+
 	// Indy 3 provides an "Indy" font in two sizes, 9 and 12, which are
 	// used for the text boxes. The smaller font can be used for a
 	// headline. The rest of the Indy 3 verb GUI uses Geneva.
 
 	switch (fontId) {
-	case FontId::kIndy3AboutFontHeaderOutside:
-		id = _gameFontId;
-		size = 12;
-		slant = Graphics::kMacFontItalic | Graphics::kMacFontBold | Graphics::kMacFontOutline;
-		break;
-
-	case FontId::kIndy3AboutFontHeaderInside:
-		id = _gameFontId;
-		size = 12;
-		slant = Graphics::kMacFontItalic | Graphics::kMacFontBold | Graphics::kMacFontExtend;
-		break;
-
-	case FontId::kIndy3AboutFontBold:
-		id = _gameFontId;
-		size = 9;
-		slant = Graphics::kMacFontRegular;
-		break;
-
-	case FontId::kIndy3AboutFontRegular:
-		id = Graphics::kMacFontGeneva;
-		size = 9;
-		slant = Graphics::kMacFontRegular;
-		break;
-
 	case FontId::kIndy3FontSmall:
 		id = _gameFontId;
 		size = 9;
 		slant = Graphics::kMacFontRegular;
-		break;
+		return true;
 
 	case FontId::kIndy3FontMedium:
 		id = _gameFontId;
 		size = 12;
 		slant = Graphics::kMacFontRegular;
-		break;
+		return true;
 
 	case FontId::kIndy3VerbFontRegular:
 		id = Graphics::kMacFontGeneva;
 		size = 9;
 		slant = Graphics::kMacFontRegular;
-		break;
+		return true;
 
 	case FontId::kIndy3VerbFontBold:
 		id = Graphics::kMacFontGeneva;
 		size = 9;
 		slant = Graphics::kMacFontBold;
-		break;
+		return true;
 
 	case FontId::kIndy3VerbFontOutline:
 		id = Graphics::kMacFontGeneva;
 		size = 9;
 		slant = Graphics::kMacFontBold | Graphics::kMacFontOutline | Graphics::kMacFontCondense;
-		break;
+		return true;
 
 	default:
 		error("MacIndy3Gui: getFontParams: Unknown font id %d", (int)fontId);
 	}
+
+	return false;
 }
 
 bool MacIndy3Gui::handleMenu(int id, Common::String &name) {
@@ -1920,7 +2129,7 @@ void MacIndy3Gui::showAboutDialog() {
 
 	Common::Rect bounds(x, y, x + width, y + height);
 	SimpleWindow *window = openWindow(bounds);
-	const Graphics::Surface *pict = loadPICT(2000);
+	Graphics::Surface *pict = loadPict(2000);
 
 	// For the background of the sprites to match the background of the
 	// window, we have to move them at multiples of 4 pixels each step. We
@@ -1959,8 +2168,6 @@ void MacIndy3Gui::showAboutDialog() {
 	// TODO: These strings are part of the STRS resource, but I don't know
 	// how to safely read them from there yet. So hard-coded it is for now.
 
-	#define TEXT_END_MARKER { 0, 0, kStyleRegular, Graphics::kTextAlignLeft, nullptr }
-
 	const TextLine page1[] = {
 		{ 21, 4, kStyleHeader, Graphics::kTextAlignLeft, "Indiana Jones and the Last Crusade" },
 		{ 0, 22, kStyleBold, Graphics::kTextAlignCenter, "The Graphic Adventure" },
@@ -2027,8 +2234,6 @@ void MacIndy3Gui::showAboutDialog() {
 		TEXT_END_MARKER
 	};
 
-	#undef TEXT_END_MARKER
-
 	// Header texts aren't rendered correctly. Apparently the original does
 	// its own shadowing by drawing the text twice, but that ends up
 	// looking even worse. Perhaps I have to draw the text three times
@@ -2037,7 +2242,7 @@ void MacIndy3Gui::showAboutDialog() {
 
 	bool changeScene = false;
 
-	while (true && !_vm->shouldQuit()) {
+	while (!_vm->shouldQuit()) {
 		switch (scene) {
 		case 0:
 			window->drawSprite(&train, trainX, 40, clipRect);
@@ -2143,6 +2348,8 @@ void MacIndy3Gui::showAboutDialog() {
 
 	_windowManager->popCursor();
 
+	pict->free();
+	delete pict;
 	delete window;
 
 	if (status == 2)
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 87f1be6f6c4..cf8ebdfdc56 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -107,7 +107,7 @@ public:
 
 		Common::Array<Common::Rect> _dirtyRects;
 
-		void copyToScreen(Graphics::Surface *s = nullptr);
+		void copyToScreen(Graphics::Surface *s = nullptr) const;
 	public:
 		SimpleWindow(MacGui *gui, OSystem *system, Graphics::Surface *from, Common::Rect bounds, SimpleWindowStyle style = kStyleNormal);
 		~SimpleWindow();
@@ -118,23 +118,24 @@ public:
 		void show();
 
 		void markRectAsDirty(Common::Rect r);
-		void update();
+		void update(bool fullRedraw = false);
 
 		static void plotPixel(int x, int y, int color, void *data);
 		static void plotPattern(int x, int y, int pattern, void *data);
 
 		void fillPattern(Common::Rect r, uint16 pattern);
-		void drawSprite(Graphics::Surface *sprite, int x, int y, Common::Rect clipRect);
+		void drawSprite(const Graphics::Surface *sprite, int x, int y);
+		void drawSprite(const Graphics::Surface *sprite, int x, int y, Common::Rect clipRect);
 		void drawTextBox(Common::Rect r, const TextLine *lines, int arc = 9);
 	};
 
 	enum FontId {
 		kSystemFont,
 
-		kIndy3AboutFontRegular,
-		kIndy3AboutFontBold,
-		kIndy3AboutFontHeaderInside,
-		kIndy3AboutFontHeaderOutside,
+		kAboutFontRegular,
+		kAboutFontBold,
+		kAboutFontHeaderInside,
+		kAboutFontHeaderOutside,
 
 		kIndy3FontSmall,
 		kIndy3FontMedium,
@@ -152,13 +153,15 @@ public:
 
 	virtual const Common::String name() const = 0;
 
+	static void menuCallback(int id, Common::String &name, void *data);
+
 	virtual void initialize();
 
 	const Graphics::Font *getFont(FontId fontId);
 	virtual const Graphics::Font *getFontByScummId(int32 id) = 0;
-	virtual void getFontParams(FontId fontId, int &id, int &size, int &slant) = 0;
+	virtual bool getFontParams(FontId fontId, int &id, int &size, int &slant);
 
-	const Graphics::Surface *loadPICT(int id);
+	Graphics::Surface *loadPict(int id);
 
 	virtual bool handleMenu(int id, Common::String &name);
 
@@ -190,7 +193,7 @@ public:
 	const Common::String name() const { return "Loom"; }
 
 	const Graphics::Font *getFontByScummId(int32 id);
-	void getFontParams(FontId fontId, int &id, int &size, int &slant);
+	bool getFontParams(FontId fontId, int &id, int &size, int &slant);
 
 	void setupCursor(int &width, int &height, int &hotspotX, int &hotspotY, int &animate);
 
@@ -212,7 +215,7 @@ public:
 	const Common::String name() const { return "Indy"; }
 
 	const Graphics::Font *getFontByScummId(int32 id);
-	void getFontParams(FontId fontId, int &id, int &size, int &slant);
+	bool getFontParams(FontId fontId, int &id, int &size, int &slant);
 
 	void setupCursor(int &width, int &height, int &hotspotX, int &hotspotY, int &animate);
 


Commit: 102080ecd86478546f7b8ad1e27d7aec625c4783
    https://github.com/scummvm/scummvm/commit/102080ecd86478546f7b8ad1e27d7aec625c4783
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Fix Mac Loom cursor regression. I hope.

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index b2e8c6eac49..a0abb41a763 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -917,9 +917,6 @@ bool MacLoomGui::getFontParams(FontId fontId, int &id, int &size, int &slant) {
 }
 
 void MacLoomGui::setupCursor(int &width, int &height, int &hotspotX, int &hotspotY, int &animate) {
-	if (_windowManager->getCursorType() == Graphics::kMacCursorCustom)
-		return;
-
 	Common::MacResManager resource;
 
 	resource.open(_resourceFile);


Commit: 5659f1b7369fe857b41be327d041f744062311f7
    https://github.com/scummvm/scummvm/commit/5659f1b7369fe857b41be327d041f744062311f7
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Clean up Mac Loom About dialog animation a bit

This doesn't match the original perfectly. At this point, I don't think
I care enough to try and fix it, as long as it looks decent.

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index a0abb41a763..01faa8b6c9a 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -367,7 +367,6 @@ void MacGui::SimpleWindow::markRectAsDirty(Common::Rect r) {
 
 void MacGui::SimpleWindow::update(bool fullRedraw) {
 	if (fullRedraw) {
-debug("Full redraw");
 		_dirtyRects.clear();
 		markRectAsDirty(Common::Rect(0, 0, _innerSurface.w, _innerSurface.h));
 	}
@@ -1048,21 +1047,36 @@ void MacLoomGui::showAboutDialog() {
 
 	Common::Rect r(0, 0, 404, 154);
 	int growth = -2;
-	int growSteps = 37;
 	int pattern;
 	int waitFrames;
 
+	int innerBounce = 72;
+	int outerBounce = 0;
+
+	int targetTop = 48;
+	int targetGrowth = 2;
+
 	bool changeScene = false;
 	bool fastForward = false;
+	bool holdScene = false;
 
 	while (!_vm->shouldQuit()) {
 		switch (scene) {
 		case 0:
-		case 1:
-		case 3:
+		case 2:
+		case 4:
 			// This appears to be pixel perfect or at least nearly
 			// so for the outer layers, but breaks down slightly
 			// near the middle.
+			//
+			// It doesn't erase the text screens correctly, though.
+			//
+			// Also, the original does an inexplicable skip in the
+			// first animation that I haven't bothered to
+			// implement. I don't know if it was intentional or
+			// not, but I think it looks awkward. And I wasn't able
+			// to get it quite right, perhaps for the same reason
+			// as the aforementioned text erasure problem.
 
 			pattern = (r.top / 2) % 8;
 
@@ -1071,17 +1085,19 @@ void MacLoomGui::showAboutDialog() {
 			if (!fastForward)
 				window->markRectAsDirty(r);
 
+			if (r.top == targetTop && growth == targetGrowth) {
+				changeScene = true;
+				break;
+			}
+
 			r.grow(growth);
 
-			if (r.height() <= 6 || r.height() >= 154)
+			if ((growth < 0 && r.top >= innerBounce) || (growth > 0 && r.top <= outerBounce))
 				growth = -growth;
-
-			if (--growSteps == 0)
-				changeScene = true;
-
 			break;
 
-		case 2:
+		case 1:
+		case 3:
 			if (--waitFrames == 0)
 				changeScene = true;
 			break;
@@ -1107,19 +1123,26 @@ void MacLoomGui::showAboutDialog() {
 
 			switch (scene) {
 			case 1:
-				r.grow(16);
-				growth = -2;
-				growSteps = 25;
+				holdScene = true;
+				window->drawSprite(lucasFilm, 120, 65);
+				waitFrames = 50;
 				break;
 
 			case 2:
-				window->drawSprite(lucasFilm, 120, 65);
-				waitFrames = 50;
+				holdScene = false;
+				growth = -2;
 				break;
 
 			case 3:
+				holdScene = true;
+				waitFrames = 50;
+				break;
+
+			case 4:
+				holdScene = false;
 				growth = -2;
-				growSteps = 23;
+				innerBounce -= 8;
+				targetTop -= 16;
 				break;
 			}
 
@@ -1128,7 +1151,7 @@ void MacLoomGui::showAboutDialog() {
 				window->update(true);
 			}
 
-			if (scene >= 4)
+			if (scene >= 5)
 				break;
 		}
 	}


Commit: 90cea288877d3b4004f988dc9d5a1cd142af4e02
    https://github.com/scummvm/scummvm/commit/90cea288877d3b4004f988dc9d5a1cd142af4e02
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Better fast forward for Mac Loom About dialog animation

It should stop on the next text screen.

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 01faa8b6c9a..7e5e8cc0b67 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -1058,7 +1058,6 @@ void MacLoomGui::showAboutDialog() {
 
 	bool changeScene = false;
 	bool fastForward = false;
-	bool holdScene = false;
 
 	while (!_vm->shouldQuit()) {
 		switch (scene) {
@@ -1123,33 +1122,28 @@ void MacLoomGui::showAboutDialog() {
 
 			switch (scene) {
 			case 1:
-				holdScene = true;
+				fastForward = false;
 				window->drawSprite(lucasFilm, 120, 65);
 				waitFrames = 50;
 				break;
 
 			case 2:
-				holdScene = false;
 				growth = -2;
 				break;
 
 			case 3:
-				holdScene = true;
+				fastForward = false;
 				waitFrames = 50;
 				break;
 
 			case 4:
-				holdScene = false;
 				growth = -2;
 				innerBounce -= 8;
 				targetTop -= 16;
 				break;
 			}
 
-			if (fastForward) {
-				fastForward = false;
-				window->update(true);
-			}
+			window->update(true);
 
 			if (scene >= 5)
 				break;


Commit: 6e6dcc8796443ec4d94c84e651f56390775db4f2
    https://github.com/scummvm/scummvm/commit/6e6dcc8796443ec4d94c84e651f56390775db4f2
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Mac Loom About fixes

Added alternate pattern drawing to darken text boxes, rather than
drawing completely over them. I have a feeling that the original did
something more clever, possibly with XOR, but the effect is similar.

Fixed positioning on the first two About screens. There is now an "extra
bold" option for text to match the "PRESENTS" text.

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 7e5e8cc0b67..aa0866c9b8b 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -431,6 +431,11 @@ void MacGui::SimpleWindow::plotPixel(int x, int y, int color, void *data) {
 	s->setPixel(x, y, color);
 }
 
+// I don't know if the original actually used two different plot functions, one
+// to fill and one to darken (used to draw over the text screens). It's such a
+// subtle effect that I suspect it was just doing some different magic, maybe
+// with XOR, but I couldn't get that to work by eye only.
+
 void MacGui::SimpleWindow::plotPattern(int x, int y, int pattern, void *data) {
 	const uint16 patterns[] = {
 		0x0000, 0x2828, 0xA5A5, 0xD7D7,
@@ -446,16 +451,24 @@ void MacGui::SimpleWindow::plotPattern(int x, int y, int pattern, void *data) {
 		s->setPixel(x, y, kWhite);
 }
 
-void MacGui::SimpleWindow::drawTextBox(Common::Rect r, const TextLine *lines, int arc) {
-	Graphics::drawRoundRect(r, arc, kWhite, true, plotPixel, this);
-	Graphics::drawRoundRect(r, arc, kBlack, false, plotPixel, this);
-	markRectAsDirty(r);
+void MacGui::SimpleWindow::plotPatternDarkenOnly(int x, int y, int pattern, void *data) {
+	const uint16 patterns[] = {
+		0x0000, 0x2828, 0xA5A5, 0xD7D7, 0xFFFF
+	};
 
-	Graphics::Surface *s = innerSurface();
+	MacGui::SimpleWindow *window = (MacGui::SimpleWindow *)data;
+	Graphics::Surface *s = window->innerSurface();
+	int bit = 0x8000 >> (4 * (y % 4) + (x % 4));
+	if (patterns[pattern] & bit)
+		s->setPixel(x, y, kBlack);
+}
 
+void MacGui::SimpleWindow::drawText(Common::Rect r, const TextLine *lines) {
 	if (!lines)
 		return;
 
+	Graphics::Surface *s = innerSurface();
+
 	for (int i = 0; lines[i].str; i++) {
 		const Graphics::Font *f1 = nullptr;
 		const Graphics::Font *f2 = nullptr;
@@ -468,6 +481,9 @@ void MacGui::SimpleWindow::drawTextBox(Common::Rect r, const TextLine *lines, in
 		case kStyleBold:
 			f1 = _gui->getFont(kAboutFontBold);
 			break;
+		case kStyleExtraBold:
+			f1 = _gui->getFont(kAboutFontExtraBold);
+			break;
 		case kStyleRegular:
 			f1 = _gui->getFont(kAboutFontRegular);
 			break;
@@ -486,10 +502,21 @@ void MacGui::SimpleWindow::drawTextBox(Common::Rect r, const TextLine *lines, in
 			f2->drawString(s, msg, x + 2, y, width, kWhite, align);
 		} else {
 			f1->drawString(s, msg, x, y, width, kBlack, align);
+
+			if (lines[i].style == kStyleExtraBold)
+				f1->drawString(s, msg, x + 1, y, width, kBlack, align);
 		}
 	}
 }
 
+void MacGui::SimpleWindow::drawTextBox(Common::Rect r, const TextLine *lines, int arc) {
+	Graphics::drawRoundRect(r, arc, kWhite, true, plotPixel, this);
+	Graphics::drawRoundRect(r, arc, kBlack, false, plotPixel, this);
+	markRectAsDirty(r);
+
+	drawText(r, lines);
+}
+
 // ===========================================================================
 // Macintosh user interface for the Macintosh versions of Loom and Indiana
 // Jones and the Last Crusade.
@@ -632,6 +659,12 @@ bool MacGui::getFontParams(FontId fontId, int &id, int &size, int &slant) {
 		slant = Graphics::kMacFontRegular;
 		return true;
 
+	case FontId::kAboutFontExtraBold:
+		id = _gameFontId;
+		size = 9;
+		slant = Graphics::kMacFontRegular | Graphics::kMacFontExtend;
+		return true;
+
 	case FontId::kAboutFontRegular:
 		id = Graphics::kMacFontGeneva;
 		size = 9;
@@ -962,7 +995,7 @@ void MacLoomGui::showAboutDialog() {
 	// how to safely read them from there yet. So hard-coded it is for now.
 
 	const TextLine page1[] = {
-		{ 0, 0, kStyleBold, Graphics::kTextAlignCenter, "PRESENTS" },
+		{ 0, 23, kStyleExtraBold, Graphics::kTextAlignCenter, "PRESENTS" },
 		TEXT_END_MARKER
 	};
 
@@ -1048,11 +1081,10 @@ void MacLoomGui::showAboutDialog() {
 	Common::Rect r(0, 0, 404, 154);
 	int growth = -2;
 	int pattern;
+	bool darkenOnly = false;
 	int waitFrames;
 
 	int innerBounce = 72;
-	int outerBounce = 0;
-
 	int targetTop = 48;
 	int targetGrowth = 2;
 
@@ -1079,7 +1111,10 @@ void MacLoomGui::showAboutDialog() {
 
 			pattern = (r.top / 2) % 8;
 
-			Graphics::drawRoundRect(r, 7, pattern, true, SimpleWindow::plotPattern, window);
+			if (pattern > 4)
+				darkenOnly = false;
+
+			Graphics::drawRoundRect(r, 7, pattern, true, darkenOnly ? SimpleWindow::plotPatternDarkenOnly : SimpleWindow::plotPattern, window);
 
 			if (!fastForward)
 				window->markRectAsDirty(r);
@@ -1091,7 +1126,7 @@ void MacLoomGui::showAboutDialog() {
 
 			r.grow(growth);
 
-			if ((growth < 0 && r.top >= innerBounce) || (growth > 0 && r.top <= outerBounce))
+			if (growth < 0 && r.top >= innerBounce)
 				growth = -growth;
 			break;
 
@@ -1123,8 +1158,9 @@ void MacLoomGui::showAboutDialog() {
 			switch (scene) {
 			case 1:
 				fastForward = false;
-				window->drawSprite(lucasFilm, 120, 65);
-				waitFrames = 50;
+				window->drawSprite(lucasFilm, 134, 61);
+				waitFrames = 67;
+				darkenOnly = true;
 				break;
 
 			case 2:
@@ -1133,6 +1169,7 @@ void MacLoomGui::showAboutDialog() {
 
 			case 3:
 				fastForward = false;
+				window->drawText(r, page1);
 				waitFrames = 50;
 				break;
 
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index cf8ebdfdc56..cfe39e2bb01 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -75,6 +75,7 @@ public:
 	enum TextStyle {
 		kStyleHeader,
 		kStyleBold,
+		kStyleExtraBold,
 		kStyleRegular
 	};
 
@@ -122,10 +123,12 @@ public:
 
 		static void plotPixel(int x, int y, int color, void *data);
 		static void plotPattern(int x, int y, int pattern, void *data);
+		static void plotPatternDarkenOnly(int x, int y, int pattern, void *data);
 
 		void fillPattern(Common::Rect r, uint16 pattern);
 		void drawSprite(const Graphics::Surface *sprite, int x, int y);
 		void drawSprite(const Graphics::Surface *sprite, int x, int y, Common::Rect clipRect);
+		void drawText(Common::Rect r, const TextLine *lines);
 		void drawTextBox(Common::Rect r, const TextLine *lines, int arc = 9);
 	};
 
@@ -134,6 +137,7 @@ public:
 
 		kAboutFontRegular,
 		kAboutFontBold,
+		kAboutFontExtraBold,
 		kAboutFontHeaderInside,
 		kAboutFontHeaderOutside,
 


Commit: 51267fe4f6f4ed320167a199d0d788e254901e91
    https://github.com/scummvm/scummvm/commit/51267fe4f6f4ed320167a199d0d788e254901e91
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Add remaning Mac Loom About screens

But I need to adjust the text positioning on most of them...

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index aa0866c9b8b..7910ad1f6b3 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -496,8 +496,8 @@ void MacGui::SimpleWindow::drawText(Common::Rect r, const TextLine *lines) {
 		int width = r.right - x;
 
 		if (lines[i].style == kStyleHeader) {
-			f1->drawString(s, msg, x + 1, y + 1, width - 1, kBlack, align);
-			f2->drawString(s, msg, x + 3, y + 1, width - 2, kBlack, align);
+			f1->drawString(s, msg, x + 1, y + 1, width, kBlack, align);
+			f2->drawString(s, msg, x + 3, y + 1, width, kBlack, align);
 			f1->drawString(s, msg, x, y, width, kBlack, align);
 			f2->drawString(s, msg, x + 2, y, width, kWhite, align);
 		} else {
@@ -1000,52 +1000,43 @@ void MacLoomGui::showAboutDialog() {
 	};
 
 	const TextLine page2[] = {
-		{ 0, 0, kStyleRegular, Graphics::kTextAlignCenter, "TM & \xA9 1990 LucasArts Entertainment Company.  All rights reserved." },
-		{ 0, 0, kStyleRegular, Graphics::kTextAlignCenter, "Release Version 1.2 25-JAN-91 Interpreter version 5.1.6" },
+		{ 1, 59, kStyleRegular, Graphics::kTextAlignCenter, "TM & \xA9 1990 LucasArts Entertainment Company.  All rights reserved." },
+		{ 0, 70, kStyleRegular, Graphics::kTextAlignCenter, "Release Version 1.2  25-JAN-91 Interpreter version 5.1.6" },
 		TEXT_END_MARKER
 	};
 
 	const TextLine page3[] = {
-		{ 0, 0, kStyleBold, Graphics::kTextAlignCenter, "Macintosh version by" },
-		{ 0, 0, kStyleHeader, Graphics::kTextAlignCenter, "Eric Johnston" },
-		{ 0, 0, kStyleBold, Graphics::kTextAlignCenter, "Macintosh scripting by" },
-		{ 0, 0, kStyleHeader, Graphics::kTextAlignCenter, "Ron Baldwin" },
+		{ 1, 11, kStyleBold, Graphics::kTextAlignCenter, "Macintosh version by" },
+		{ -2, 25, kStyleHeader, Graphics::kTextAlignCenter, "Eric Johnston" },
+		{ 0, 49, kStyleBold, Graphics::kTextAlignCenter, "Macintosh scripting by" },
+		{ -1, 63, kStyleHeader, Graphics::kTextAlignCenter, "Ron Baldwin" },
 		TEXT_END_MARKER
 	};
 
 	const TextLine page4[] = {
-		{ 0, 0, kStyleBold, Graphics::kTextAlignCenter, "Original game created by" },
-		{ 0, 0, kStyleHeader, Graphics::kTextAlignCenter, "Brian Moriarty" },
+		{ 0, 25, kStyleBold, Graphics::kTextAlignCenter, "Original game created by" },
+		{ 0, 35, kStyleHeader, Graphics::kTextAlignCenter, "Brian Moriarty" },
 		TEXT_END_MARKER
 	};
 
 	const TextLine page5[] = {
-		{ 0, 0, kStyleBold, Graphics::kTextAlignCenter, "Produced by" },
-		{ 0, 0, kStyleHeader, Graphics::kTextAlignCenter, "Gregory D. Hammond" },
-		{ 0, 0, kStyleBold, Graphics::kTextAlignCenter, "Macintosh Version Produced by" },
-		{ 0, 0, kStyleHeader, Graphics::kTextAlignCenter, "David Fox" },
+		{ 0, 10, kStyleBold, Graphics::kTextAlignCenter, "Produced by" },
+		{ 0, 20, kStyleHeader, Graphics::kTextAlignCenter, "Gregory D. Hammond" },
+		{ 0, 30, kStyleBold, Graphics::kTextAlignCenter, "Macintosh Version Produced by" },
+		{ 0, 40, kStyleHeader, Graphics::kTextAlignCenter, "David Fox" },
 		TEXT_END_MARKER
 	};
 
 	const TextLine page6[] = {
-		{ 0, 0, kStyleBold, Graphics::kTextAlignCenter, "SCUMM Story Stystem" },
-		{ 0, 0, kStyleBold, Graphics::kTextAlignCenter, "created by" },
-		{ 0, 0, kStyleHeader, Graphics::kTextAlignCenter, "Ron Gilbert" },
-		{ 0, 0, kStyleBold, Graphics::kTextAlignCenter, "and" },
-		{ 0, 0, kStyleHeader, Graphics::kTextAlignCenter, "Aric Wilmunder" },
+		{ 0, 10, kStyleBold, Graphics::kTextAlignCenter, "SCUMM Story Stystem" },
+		{ 0, 20, kStyleBold, Graphics::kTextAlignCenter, "created by" },
+		{ 0, 30, kStyleHeader, Graphics::kTextAlignCenter, "Ron Gilbert" },
+		{ 0, 40, kStyleBold, Graphics::kTextAlignCenter, "and" },
+		{ 0, 50, kStyleHeader, Graphics::kTextAlignCenter, "Aric Wilmunder" },
 		TEXT_END_MARKER
 	};
 
 	const TextLine page7[] = {
-		{ 0, 0, kStyleBold, Graphics::kTextAlignCenter, "SCUMM Story Stystem" },
-		{ 0, 0, kStyleBold, Graphics::kTextAlignCenter, "created by" },
-		{ 0, 0, kStyleHeader, Graphics::kTextAlignCenter, "Ron Gilbert" },
-		{ 0, 0, kStyleBold, Graphics::kTextAlignCenter, "and" },
-		{ 0, 0, kStyleHeader, Graphics::kTextAlignCenter, "Aric Wilmunder" },
-		TEXT_END_MARKER
-	};
-
-	const TextLine page8[] = {
 		{ 1, 19, kStyleBold, Graphics::kTextAlignCenter, "Stumped?  Loom hint books are available!" },
 		{ 86, 36, kStyleRegular, Graphics::kTextAlignLeft, "In the U.S. call" },
 		{ 160, 37, kStyleBold, Graphics::kTextAlignLeft, "1 (800) STAR-WARS" },
@@ -1055,16 +1046,16 @@ void MacLoomGui::showAboutDialog() {
 		TEXT_END_MARKER
 	};
 
-	const TextLine page9[] = {
+	const TextLine page8[] = {
 		{ 1, 17, kStyleBold, Graphics::kTextAlignCenter, "Need a hint NOW?  Having problems?" },
 		{ 53, 31, kStyleRegular, Graphics::kTextAlignLeft, "For technical support call" },
-		{ 0, 0, kStyleBold, Graphics::kTextAlignLeft, "1 (415) 721-3333" },
-		{ 0, 0, kStyleRegular, Graphics::kTextAlignLeft, "For hints call" },
+		{ 0, 31, kStyleBold, Graphics::kTextAlignLeft, "1 (415) 721-3333" },
+		{ 0, 50, kStyleRegular, Graphics::kTextAlignLeft, "For hints call" },
 
-		{ 215, 32, kStyleBold, Graphics::kTextAlignLeft, "1 (900) 740-JEDI" },
-		{ 1, 46, kStyleRegular, Graphics::kTextAlignCenter, "The charge for the hint line is 75\xA2 per minute." },
-		{ 1, 56, kStyleRegular, Graphics::kTextAlignCenter, "(You must have your parents\xD5 permission to" },
-		{ 1, 66, kStyleRegular, Graphics::kTextAlignCenter, "call this number if you are under 18.)" },
+		{ 215, 50, kStyleBold, Graphics::kTextAlignLeft, "1 (900) 740-JEDI" },
+		{ 1, 60, kStyleRegular, Graphics::kTextAlignCenter, "The charge for the hint line is 75\xA2 per minute." },
+		{ 1, 70, kStyleRegular, Graphics::kTextAlignCenter, "(You must have your parents\xD5 permission to" },
+		{ 1, 80, kStyleRegular, Graphics::kTextAlignCenter, "call this number if you are under 18.)" },
 		TEXT_END_MARKER
 	};
 
@@ -1092,10 +1083,7 @@ void MacLoomGui::showAboutDialog() {
 	bool fastForward = false;
 
 	while (!_vm->shouldQuit()) {
-		switch (scene) {
-		case 0:
-		case 2:
-		case 4:
+		if ((scene % 2) == 0) {
 			// This appears to be pixel perfect or at least nearly
 			// so for the outer layers, but breaks down slightly
 			// near the middle.
@@ -1121,20 +1109,15 @@ void MacLoomGui::showAboutDialog() {
 
 			if (r.top == targetTop && growth == targetGrowth) {
 				changeScene = true;
-				break;
-			}
-
-			r.grow(growth);
-
-			if (growth < 0 && r.top >= innerBounce)
-				growth = -growth;
-			break;
+			} else {
+				r.grow(growth);
 
-		case 1:
-		case 3:
+				if (growth < 0 && r.top >= innerBounce)
+					growth = -growth;
+			}
+		} else {
 			if (--waitFrames == 0)
 				changeScene = true;
-			break;
 		}
 
 		if (!fastForward) {
@@ -1158,19 +1141,26 @@ void MacLoomGui::showAboutDialog() {
 			switch (scene) {
 			case 1:
 				fastForward = false;
-				window->drawSprite(lucasFilm, 134, 61);
-				waitFrames = 67;
+				waitFrames = 60;	// ~3 seconds
 				darkenOnly = true;
+				window->drawSprite(lucasFilm, 134, 61);
 				break;
 
 			case 2:
+			case 6:
+			case 8:
+			case 10:
+			case 12:
+			case 14:
+			case 16:
 				growth = -2;
 				break;
 
 			case 3:
 				fastForward = false;
+				darkenOnly = true;
+				waitFrames = 40;	// ~2 seconds
 				window->drawText(r, page1);
-				waitFrames = 50;
 				break;
 
 			case 4:
@@ -1178,11 +1168,57 @@ void MacLoomGui::showAboutDialog() {
 				innerBounce -= 8;
 				targetTop -= 16;
 				break;
+
+			case 5:
+				fastForward = false;
+				waitFrames = 130;	// ~6.5 seconds
+				window->drawSprite(loom, 95, 38);
+				window->drawText(r, page2);
+				break;
+
+				growth = -2;
+				break;
+
+			case 7:
+				fastForward = false;
+				waitFrames = 80;	// ~4 seconds
+				window->drawText(r, page3);
+				break;
+
+			case 9:
+				fastForward = false;
+				waitFrames = 80;
+				window->drawText(r, page4);
+				break;
+
+			case 11:
+				fastForward = false;
+				waitFrames = 80;
+				window->drawText(r, page5);
+				break;
+
+			case 13:
+				fastForward = false;
+				waitFrames = 80;
+				window->drawText(r, page6);
+				break;
+
+			case 15:
+				fastForward = false;
+				waitFrames = 260;	// ~13 seconds
+				window->drawText(r, page7);
+				break;
+
+			case 17:
+				fastForward = false;
+				window->drawText(r, page8);
+				break;
 			}
 
+
 			window->update(true);
 
-			if (scene >= 5)
+			if (scene >= 17)
 				break;
 		}
 	}


Commit: 53d149b45be71552c1957b65118ff7ece7cb87ad
    https://github.com/scummvm/scummvm/commit/53d149b45be71552c1957b65118ff7ece7cb87ad
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
GRAPHICS: Make Mac font rendering more like the original

By applying italics before outline, not after, and slanting italics
characters from the bottom up instead of top down, I now get pixel
perfect rendering for my experimental About dialogs for Loom and Indiana
Jones and the Last Crusade.

Changed paths:
    graphics/fonts/macfont.cpp


diff --git a/graphics/fonts/macfont.cpp b/graphics/fonts/macfont.cpp
index aa078ad44ff..fa0ee96c734 100644
--- a/graphics/fonts/macfont.cpp
+++ b/graphics/fonts/macfont.cpp
@@ -628,12 +628,6 @@ MacFONTFont *MacFONTFont::scaleFont(const MacFONTFont *src, int newSize, int sla
 			}
 		}
 
-		if (slant & kMacFontOutline) {
-			tmpSurf.fillRect(Common::Rect(tmpSurf.w, tmpSurf.h), 0);
-			makeOutline(&srcSurf, &tmpSurf, glyph, data._fRectHeight);
-			srcSurf.copyFrom(tmpSurf);
-		}
-
 		if (slant & kMacFontItalic) {
 			tmpSurf.fillRect(Common::Rect(tmpSurf.w, tmpSurf.h), 0);
 			makeItalic(&srcSurf, &tmpSurf, glyph, data._fRectHeight);
@@ -643,6 +637,12 @@ MacFONTFont *MacFONTFont::scaleFont(const MacFONTFont *src, int newSize, int sla
 		if (slant & kMacFontUnderline)
 			makeUnderLine(&srcSurf, glyph, data._ascent);
 
+		if (slant & kMacFontOutline) {
+			tmpSurf.fillRect(Common::Rect(tmpSurf.w, tmpSurf.h), 0);
+			makeOutline(&srcSurf, &tmpSurf, glyph, data._fRectHeight);
+			srcSurf.copyFrom(tmpSurf);
+		}
+
 		if (slant & kMacFontShadow) {
 			tmpSurf.fillRect(Common::Rect(tmpSurf.w, tmpSurf.h), 0);
 			makeShadow(&srcSurf, &tmpSurf, glyph, data._fRectHeight);
@@ -772,8 +772,8 @@ static void makeOutline(Surface *src, Surface *dst, MacGlyph *glyph, int height)
 static void makeItalic(Surface *src, Surface *dst, MacGlyph *glyph, int height) {
 	int dw = (height - 1) / SLANTDEEP;
 
-	for (uint16 y = 0; y < height; y++) {
-		int dx = dw - y / SLANTDEEP;
+	for (int16 y = height - 1; y >= 0; y--) {
+		int dx = (height - 1 - y) / SLANTDEEP;
 		byte *srcPtr = (byte *)src->getBasePtr(0, y);
 		byte *dstPtr = (byte *)dst->getBasePtr(dx, y);
 


Commit: dddda5c497e054f7ca48d3d378e86826cc5ddbd9
    https://github.com/scummvm/scummvm/commit/dddda5c497e054f7ca48d3d378e86826cc5ddbd9
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: More Mac GUI fixes

All text screens for the Indy 3 and Loom About dialogs should be pixel
perfect now. The animations are not, but I don't consider that quite as
important.

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 7910ad1f6b3..0195b7e4790 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -496,10 +496,10 @@ void MacGui::SimpleWindow::drawText(Common::Rect r, const TextLine *lines) {
 		int width = r.right - x;
 
 		if (lines[i].style == kStyleHeader) {
-			f1->drawString(s, msg, x + 1, y + 1, width, kBlack, align);
-			f2->drawString(s, msg, x + 3, y + 1, width, kBlack, align);
-			f1->drawString(s, msg, x, y, width, kBlack, align);
-			f2->drawString(s, msg, x + 2, y, width, kWhite, align);
+			f1->drawString(s, msg, x - 1, y + 1, width, kBlack, align);
+			f2->drawString(s, msg, x + 1, y + 1, width, kBlack, align);
+			f1->drawString(s, msg, x - 2, y, width, kBlack, align);
+			f2->drawString(s, msg, x, y, width, kWhite, align);
 		} else {
 			f1->drawString(s, msg, x, y, width, kBlack, align);
 
@@ -1007,55 +1007,55 @@ void MacLoomGui::showAboutDialog() {
 
 	const TextLine page3[] = {
 		{ 1, 11, kStyleBold, Graphics::kTextAlignCenter, "Macintosh version by" },
-		{ -2, 25, kStyleHeader, Graphics::kTextAlignCenter, "Eric Johnston" },
+		{ 0, 25, kStyleHeader, Graphics::kTextAlignCenter, "Eric Johnston" },
 		{ 0, 49, kStyleBold, Graphics::kTextAlignCenter, "Macintosh scripting by" },
-		{ -1, 63, kStyleHeader, Graphics::kTextAlignCenter, "Ron Baldwin" },
+		{ 1, 63, kStyleHeader, Graphics::kTextAlignCenter, "Ron Baldwin" },
 		TEXT_END_MARKER
 	};
 
 	const TextLine page4[] = {
-		{ 0, 25, kStyleBold, Graphics::kTextAlignCenter, "Original game created by" },
-		{ 0, 35, kStyleHeader, Graphics::kTextAlignCenter, "Brian Moriarty" },
+		{ 0, 26, kStyleBold, Graphics::kTextAlignCenter, "Original game created by" },
+		{ 1, 40, kStyleHeader, Graphics::kTextAlignCenter, "Brian Moriarty" },
 		TEXT_END_MARKER
 	};
 
 	const TextLine page5[] = {
-		{ 0, 10, kStyleBold, Graphics::kTextAlignCenter, "Produced by" },
-		{ 0, 20, kStyleHeader, Graphics::kTextAlignCenter, "Gregory D. Hammond" },
-		{ 0, 30, kStyleBold, Graphics::kTextAlignCenter, "Macintosh Version Produced by" },
-		{ 0, 40, kStyleHeader, Graphics::kTextAlignCenter, "David Fox" },
+		{ 1, 11, kStyleBold, Graphics::kTextAlignCenter, "Produced by" },
+		{ 0, 25, kStyleHeader, Graphics::kTextAlignCenter, "Gregory D. Hammond" },
+		{ 0, 49, kStyleBold, Graphics::kTextAlignCenter, "Macintosh Version Produced by" },
+		{ 1, 63, kStyleHeader, Graphics::kTextAlignCenter, "David Fox" },
 		TEXT_END_MARKER
 	};
 
 	const TextLine page6[] = {
-		{ 0, 10, kStyleBold, Graphics::kTextAlignCenter, "SCUMM Story Stystem" },
-		{ 0, 20, kStyleBold, Graphics::kTextAlignCenter, "created by" },
-		{ 0, 30, kStyleHeader, Graphics::kTextAlignCenter, "Ron Gilbert" },
-		{ 0, 40, kStyleBold, Graphics::kTextAlignCenter, "and" },
-		{ 0, 50, kStyleHeader, Graphics::kTextAlignCenter, "Aric Wilmunder" },
+		{ 1, 6, kStyleBold, Graphics::kTextAlignCenter, "SCUMM Story System" },
+		{ 1, 16, kStyleBold, Graphics::kTextAlignCenter, "created by" },
+		{ 97, 35, kStyleHeader, Graphics::kTextAlignLeft, "Ron Gilbert" },
+		{ 1, 51, kStyleBold, Graphics::kTextAlignCenter, "and" },
+		{ 122, 65, kStyleHeader, Graphics::kTextAlignLeft, "Aric Wilmunder" },
 		TEXT_END_MARKER
 	};
 
 	const TextLine page7[] = {
-		{ 1, 19, kStyleBold, Graphics::kTextAlignCenter, "Stumped?  Loom hint books are available!" },
-		{ 86, 36, kStyleRegular, Graphics::kTextAlignLeft, "In the U.S. call" },
-		{ 160, 37, kStyleBold, Graphics::kTextAlignLeft, "1 (800) STAR-WARS" },
-		{ 160, 46, kStyleRegular, Graphics::kTextAlignLeft, "that\xD5s  1 (800) 782-7927" },
-		{ 90, 66, kStyleRegular, Graphics::kTextAlignLeft, "In Canada call" },
-		{ 160, 67, kStyleBold, Graphics::kTextAlignLeft, "1 (800) 828-7927" },
+		{ 1, 16, kStyleBold, Graphics::kTextAlignCenter, "Stumped?  Loom hint books are available!" },
+		{ 76, 33, kStyleRegular, Graphics::kTextAlignLeft, "In the U.S. call" },
+		{ 150, 34, kStyleBold, Graphics::kTextAlignLeft, "1 (800) STAR-WARS" },
+		{ 150, 43, kStyleRegular, Graphics::kTextAlignLeft, "that\xD5s  1 (800) 782-7927" },
+		{ 80, 63, kStyleRegular, Graphics::kTextAlignLeft, "In Canada call" },
+		{ 150, 64, kStyleBold, Graphics::kTextAlignLeft, "1 (800) 828-7927" },
 		TEXT_END_MARKER
 	};
 
 	const TextLine page8[] = {
-		{ 1, 17, kStyleBold, Graphics::kTextAlignCenter, "Need a hint NOW?  Having problems?" },
-		{ 53, 31, kStyleRegular, Graphics::kTextAlignLeft, "For technical support call" },
-		{ 0, 31, kStyleBold, Graphics::kTextAlignLeft, "1 (415) 721-3333" },
-		{ 0, 50, kStyleRegular, Graphics::kTextAlignLeft, "For hints call" },
-
-		{ 215, 50, kStyleBold, Graphics::kTextAlignLeft, "1 (900) 740-JEDI" },
-		{ 1, 60, kStyleRegular, Graphics::kTextAlignCenter, "The charge for the hint line is 75\xA2 per minute." },
-		{ 1, 70, kStyleRegular, Graphics::kTextAlignCenter, "(You must have your parents\xD5 permission to" },
-		{ 1, 80, kStyleRegular, Graphics::kTextAlignCenter, "call this number if you are under 18.)" },
+		{ 1, 11, kStyleBold, Graphics::kTextAlignCenter, "Need a hint NOW?  Having problems?" },
+		{ 81, 25, kStyleRegular, Graphics::kTextAlignLeft, "For technical support call" },
+		{ 205, 26, kStyleBold, Graphics::kTextAlignLeft, "1 (415) 721-3333" },
+		{ 137, 35, kStyleRegular, Graphics::kTextAlignLeft, "For hints call" },
+
+		{ 205, 36, kStyleBold, Graphics::kTextAlignLeft, "1 (900) 740-JEDI" },
+		{ 1, 50, kStyleRegular, Graphics::kTextAlignCenter, "The charge for the hint line is 75\xA2 per minute." },
+		{ 1, 60, kStyleRegular, Graphics::kTextAlignCenter, "(You must have your parents\xD5 permission to" },
+		{ 1, 70, kStyleRegular, Graphics::kTextAlignCenter, "call this number if you are under 18.)" },
 		TEXT_END_MARKER
 	};
 
@@ -1088,14 +1088,11 @@ void MacLoomGui::showAboutDialog() {
 			// so for the outer layers, but breaks down slightly
 			// near the middle.
 			//
-			// It doesn't erase the text screens correctly, though.
-			//
 			// Also, the original does an inexplicable skip in the
 			// first animation that I haven't bothered to
 			// implement. I don't know if it was intentional or
 			// not, but I think it looks awkward. And I wasn't able
-			// to get it quite right, perhaps for the same reason
-			// as the aforementioned text erasure problem.
+			// to get it quite right anyway.
 
 			pattern = (r.top / 2) % 8;
 
@@ -1171,6 +1168,7 @@ void MacLoomGui::showAboutDialog() {
 
 			case 5:
 				fastForward = false;
+				darkenOnly = true;
 				waitFrames = 130;	// ~6.5 seconds
 				window->drawSprite(loom, 95, 38);
 				window->drawText(r, page2);
@@ -1181,30 +1179,35 @@ void MacLoomGui::showAboutDialog() {
 
 			case 7:
 				fastForward = false;
+				darkenOnly = true;
 				waitFrames = 80;	// ~4 seconds
 				window->drawText(r, page3);
 				break;
 
 			case 9:
 				fastForward = false;
+				darkenOnly = true;
 				waitFrames = 80;
 				window->drawText(r, page4);
 				break;
 
 			case 11:
 				fastForward = false;
+				darkenOnly = true;
 				waitFrames = 80;
 				window->drawText(r, page5);
 				break;
 
 			case 13:
 				fastForward = false;
+				darkenOnly = true;
 				waitFrames = 80;
 				window->drawText(r, page6);
 				break;
 
 			case 15:
 				fastForward = false;
+				darkenOnly = true;
 				waitFrames = 260;	// ~13 seconds
 				window->drawText(r, page7);
 				break;
@@ -1235,9 +1238,6 @@ void MacLoomGui::showAboutDialog() {
 	delete loom;
 
 	delete window;
-
-	if (status == 2)
-		debug("Quit everything");
 }
 
 // ===========================================================================
@@ -2256,7 +2256,7 @@ void MacIndy3Gui::showAboutDialog() {
 	// how to safely read them from there yet. So hard-coded it is for now.
 
 	const TextLine page1[] = {
-		{ 21, 4, kStyleHeader, Graphics::kTextAlignLeft, "Indiana Jones and the Last Crusade" },
+		{ 0, 4, kStyleHeader, Graphics::kTextAlignCenter, "Indiana Jones and the Last Crusade" },
 		{ 0, 22, kStyleBold, Graphics::kTextAlignCenter, "The Graphic Adventure" },
 		{ 0, 49, kStyleBold, Graphics::kTextAlignCenter, "Mac 1.7 8/17/90, Interpreter version 5.1.6" },
 		{ 1, 82, kStyleRegular, Graphics::kTextAlignCenter, "TM & \xA9 1990 LucasArts Entertainment Company.  All rights reserved." },
@@ -2265,34 +2265,34 @@ void MacIndy3Gui::showAboutDialog() {
 
 	const TextLine page2[] = {
 		{ 1, 7, kStyleBold, Graphics::kTextAlignCenter, "Macintosh version by" },
-		{ 68, 21, kStyleHeader, Graphics::kTextAlignLeft, "Eric Johnston" },
+		{ 70, 21, kStyleHeader, Graphics::kTextAlignLeft, "Eric Johnston" },
 		{ 194, 32, kStyleBold, Graphics::kTextAlignLeft, "and" },
-		{ 214, 41, kStyleHeader, Graphics::kTextAlignLeft, "Dan Filner" },
+		{ 216, 41, kStyleHeader, Graphics::kTextAlignLeft, "Dan Filner" },
 		TEXT_END_MARKER
 	};
 
 	const TextLine page3[] = {
 		{ 1, 7, kStyleBold, Graphics::kTextAlignCenter, "Macintosh scripting by" },
-		{ 72, 21, kStyleHeader, Graphics::kTextAlignLeft, "Ron Baldwin" },
+		{ 75, 21, kStyleHeader, Graphics::kTextAlignLeft, "Ron Baldwin" },
 		{ 186, 32, kStyleBold, Graphics::kTextAlignLeft, "and" },
-		{ 211, 41, kStyleHeader, Graphics::kTextAlignLeft, "David Fox" },
+		{ 214, 41, kStyleHeader, Graphics::kTextAlignLeft, "David Fox" },
 		TEXT_END_MARKER
 	};
 
 	const TextLine page4[] = {
 		{ 1, 7, kStyleBold, Graphics::kTextAlignCenter, "Designed and scripted by" },
-		{ 75, 24, kStyleHeader, Graphics::kTextAlignLeft, "Noah Falstein" },
-		{ 132, 44, kStyleHeader, Graphics::kTextAlignLeft, "David Fox" },
-		{ 165, 64, kStyleHeader, Graphics::kTextAlignLeft, "Ron Gilbert" },
+		{ 77, 24, kStyleHeader, Graphics::kTextAlignLeft, "Noah Falstein" },
+		{ 134, 44, kStyleHeader, Graphics::kTextAlignLeft, "David Fox" },
+		{ 167, 64, kStyleHeader, Graphics::kTextAlignLeft, "Ron Gilbert" },
 		TEXT_END_MARKER
 	};
 
 	const TextLine page5[] = {
 		{ 1, 7, kStyleBold, Graphics::kTextAlignCenter, "SCUMM Story System" },
 		{ 1, 17, kStyleBold, Graphics::kTextAlignCenter, "created by" },
-		{ 105, 36, kStyleHeader, Graphics::kTextAlignLeft, "Ron Gilbert" },
+		{ 107, 36, kStyleHeader, Graphics::kTextAlignLeft, "Ron Gilbert" },
 		{ 170, 52, kStyleBold, Graphics::kTextAlignLeft, "and" },
-		{ 130, 66, kStyleHeader, Graphics::kTextAlignLeft, "Aric Wilmunder" },
+		{ 132, 66, kStyleHeader, Graphics::kTextAlignLeft, "Aric Wilmunder" },
 		TEXT_END_MARKER
 	};
 
@@ -2438,9 +2438,6 @@ void MacIndy3Gui::showAboutDialog() {
 	pict->free();
 	delete pict;
 	delete window;
-
-	if (status == 2)
-		debug("Quit everything");
 }
 
 void MacIndy3Gui::clearAboutDialog(SimpleWindow *window) {


Commit: 1fb0070ef88934a1b71ede16ed3eb10f0c94bb1e
    https://github.com/scummvm/scummvm/commit/1fb0070ef88934a1b71ede16ed3eb10f0c94bb1e
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Updated timing for Indy 3 Mac About dialog

Based on Mini vMac, running at regular speed, since I assume that's the
most accurate one I have access to. This also gave me an excuse to make
the train move at 4 pixels increments, instead of 12. Removed obsolete
comment.

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 0195b7e4790..bc867e4e2f6 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -2249,7 +2249,7 @@ void MacIndy3Gui::showAboutDialog() {
 	int trolleyX = width + 1;
 	int trolleyFrame = 1;
 	int trolleyFrameDelta = 1;
-	int trolleyWaitFrames = 50;
+	int trolleyWaitFrames = 20;	// ~2 seconds
 	int waitFrames;
 
 	// TODO: These strings are part of the STRS resource, but I don't know
@@ -2321,19 +2321,13 @@ void MacIndy3Gui::showAboutDialog() {
 		TEXT_END_MARKER
 	};
 
-	// Header texts aren't rendered correctly. Apparently the original does
-	// its own shadowing by drawing the text twice, but that ends up
-	// looking even worse. Perhaps I have to draw the text three times
-	// once to fill it), or maybe our Mac text rendering just isn't as good
-	// as it needs to be yet.
-
 	bool changeScene = false;
 
 	while (!_vm->shouldQuit()) {
 		switch (scene) {
 		case 0:
 			window->drawSprite(&train, trainX, 40, clipRect);
-			trainX -= 12;
+			trainX -= 4;
 
 			if (trainX < -train.w)
 				changeScene = true;
@@ -2372,7 +2366,7 @@ void MacIndy3Gui::showAboutDialog() {
 		}
 
 		window->update();
-		status = delay(100);
+		status = delay((scene == 0) ? 33 : 100);
 
 		if (status == 2)
 			break;
@@ -2380,7 +2374,7 @@ void MacIndy3Gui::showAboutDialog() {
 		if (status == 1 || changeScene) {
 			changeScene = false;
 			scene++;
-			waitFrames = 53;
+			waitFrames = 50;	// ~5 seconds
 
 			switch (scene) {
 			case 1:
@@ -2409,12 +2403,12 @@ void MacIndy3Gui::showAboutDialog() {
 				break;
 
 			case 6:
-				waitFrames = 106;
+				waitFrames = 100;	// ~10 seconds
 				window->drawTextBox(r1, page6);
 				break;
 
 			case 7:
-				waitFrames = 33;
+				waitFrames = 30;	// ~3 seconds
 				window->drawTextBox(r1, page7);
 				break;
 


Commit: 5f51200454ba77d98a707b03ea23464bf2c4e45d
    https://github.com/scummvm/scummvm/commit/5f51200454ba77d98a707b03ea23464bf2c4e45d
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Move Indy 3 Mac text box drawing into MacIndy3Gui

Changed paths:
    engines/scumm/charset.cpp
    engines/scumm/charset.h
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h
    engines/scumm/scumm.cpp
    engines/scumm/scumm.h
    engines/scumm/string.cpp


diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp
index d2a44fbaa01..48f361edd7a 100644
--- a/engines/scumm/charset.cpp
+++ b/engines/scumm/charset.cpp
@@ -1777,7 +1777,7 @@ void CharsetRendererMac::printChar(int chr, bool ignoreCharsetMask) {
 	bool drawToTextBox = (vs->number == kTextVirtScreen && _vm->_game.id == GID_INDY3);
 
 	if (drawToTextBox)
-		printCharToTextBox(chr, color, macLeft, macTop);
+		((MacIndy3Gui *)_vm->_macGui)->printCharToTextArea(chr, macLeft, macTop, color);
 	else
 		printCharInternal(chr, color, enableShadow, macLeft, macTop);
 
@@ -1956,27 +1956,6 @@ void CharsetRendererMac::printCharInternal(int chr, int color, bool shadow, int
 	}
 }
 
-void CharsetRendererMac::printCharToTextBox(int chr, int color, int x, int y) {
-	// This function handles printing most of the text in the text boxes
-	// in Indiana Jones and the last crusade. In black and white mode, all
-	// text is white. Text is never disabled.
-
-	if (_vm->_renderMode == Common::kRenderMacintoshBW)
-		color = 15;
-
-	// Since we're working with unscaled coordinates most of the time, the
-	// lines of the text box weren't spaced quite as much as in the
-	// original. I thought no one would notice, but I was wrong. This is
-	// the best way I can think of to fix that.
-
-	if (y > 0)
-		y = 17;
-
-	const Graphics::Font *font = _vm->_macGui->getFontByScummId(_curId);
-
-	font->drawChar(_vm->_macIndy3TextBox, chr, x + 5, y + 11, color);
-}
-
 void CharsetRendererMac::drawChar(int chr, Graphics::Surface &s, int x, int y) {
 	// This function is used for drawing most of the text outside of what
 	// the game scripts request. It's used for the text box captions in
diff --git a/engines/scumm/charset.h b/engines/scumm/charset.h
index 2e84495bbcb..46f24367b88 100644
--- a/engines/scumm/charset.h
+++ b/engines/scumm/charset.h
@@ -295,7 +295,6 @@ protected:
 	int getDrawWidthIntern(uint16 chr) const;
 
 	void printCharInternal(int chr, int color, bool shadow, int x, int y);
-	void printCharToTextBox(int chr, int color, int x, int y);
 
 	byte getTextColor();
 	byte getTextShadowColor();
diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index bc867e4e2f6..d92ed1398ea 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -180,56 +180,23 @@ void ScummEngine::mac_drawLoomPracticeMode() {
 	_system->copyRectToScreen(ptr, pitch, x, y, width, height);
 }
 
-void ScummEngine::mac_createIndy3TextBox(Actor *a) {
-	int width = _macIndy3TextBox->w;
-	int height = _macIndy3TextBox->h;
-
-	_macIndy3TextBox->fillRect(Common::Rect(width, height), 0);
-
-	int nameWidth = 0;
-	byte color = _charset->getColor();
-
-	if (a) {
-		const Graphics::Font *font = _macGui->getFont(MacGui::kIndy3FontSmall);
-
-		const char *name = (const char *)a->getActorName();
-		int charX = 25;
-
-		for (int i = 0; name[i] && nameWidth < width - 50; i++) {
-			font->drawChar(_macIndy3TextBox, name[i], charX, 0, color);
-			nameWidth += font->getCharWidth(name[i]);
-			charX += font->getCharWidth(name[i]);
-		}
-
-		font->drawChar(_macIndy3TextBox, ':', charX, 0, color);
-	}
-
-	if (nameWidth) {
-		_macIndy3TextBox->hLine(2, 3, 20, 15);
-		_macIndy3TextBox->hLine(32 + nameWidth, 3, width - 3, 15);
-	} else
-		_macIndy3TextBox->hLine(2, 3, width - 3, 15);
-
-	_macIndy3TextBox->vLine(1, 4, height - 3, 15);
-	_macIndy3TextBox->vLine(width - 2, 4, height - 3, 15);
-	_macIndy3TextBox->hLine(2, height - 2, width - 3, 15);
-}
-
 void ScummEngine::mac_drawIndy3TextBox() {
+	Graphics::Surface *s = ((MacIndy3Gui *)_macGui)->textArea();
+
 	// The first two rows of the text box are padding for font rendering.
 	// They are not drawn to the screen.
 
 	int x = 96;
 	int y = 32;
-	int w = _macIndy3TextBox->w;
-	int h = _macIndy3TextBox->h - 2;
+	int w = s->w;
+	int h = s->h - 2;
 
 	// The text box is drawn to the Mac screen and text surface, as if it
 	// had been one giant glyph. Note that it will be drawn on the main
 	// virtual screen, but we still pretend it's on the text one.
 
-	byte *ptr = (byte *)_macIndy3TextBox->getBasePtr(0, 2);
-	int pitch = _macIndy3TextBox->pitch;
+	byte *ptr = (byte *)s->getBasePtr(0, 2);
+	int pitch = s->pitch;
 
 	_macScreen->copyRectToSurface(ptr, pitch, x, y, w, h);
 	_textSurface.fillRect(Common::Rect(x, y, x + w, y + h), 0);
@@ -238,10 +205,12 @@ void ScummEngine::mac_drawIndy3TextBox() {
 }
 
 void ScummEngine::mac_undrawIndy3TextBox() {
+	Graphics::Surface *s = ((MacIndy3Gui *)_macGui)->textArea();
+
 	int x = 96;
 	int y = 32;
-	int w = _macIndy3TextBox->w;
-	int h = _macIndy3TextBox->h - 2;
+	int w = s->w;
+	int h = s->h - 2;
 
 	_macScreen->fillRect(Common::Rect(x, y, x + w, y + h), 0);
 	_textSurface.fillRect(Common::Rect(x, y, x + w, y + h), CHARSET_MASK_TRANSPARENCY);
@@ -2110,11 +2079,13 @@ MacIndy3Gui::MacIndy3Gui(ScummEngine *vm, Common::String resourceFile) :
 		it._value->setVerbid(it._key);
 
 	_dirtyRects.clear();
+	_textArea.create(448, 47, Graphics::PixelFormat::createFormatCLUT8());
 }
 
 MacIndy3Gui::~MacIndy3Gui() {
 	for (auto &it: _widgets)
 		delete it._value;
+	_textArea.free();
 }
 
 void MacIndy3Gui::setupCursor(int &width, int &height, int &hotspotX, int &hotspotY, int &animate) {
@@ -2198,6 +2169,59 @@ bool MacIndy3Gui::getFontParams(FontId fontId, int &id, int &size, int &slant) {
 	return false;
 }
 
+void MacIndy3Gui::initTextAreaForActor(Actor *a, byte color) {
+	int width = _textArea.w;
+	int height = _textArea.h;
+
+	_textArea.fillRect(Common::Rect(width, height), kBlack);
+
+	int nameWidth = 0;
+//	byte color = _charset->getColor();
+
+	if (a) {
+		const Graphics::Font *font = getFont(kIndy3FontSmall);
+
+		const char *name = (const char *)a->getActorName();
+		int charX = 25;
+
+		for (int i = 0; name[i] && nameWidth < width - 50; i++) {
+			font->drawChar(&_textArea, name[i], charX, 0, color);
+			nameWidth += font->getCharWidth(name[i]);
+			charX += font->getCharWidth(name[i]);
+		}
+
+		font->drawChar(&_textArea, ':', charX, 0, color);
+	}
+
+	if (nameWidth) {
+		_textArea.hLine(2, 3, 20, 15);
+		_textArea.hLine(32 + nameWidth, 3, width - 3, 15);
+	} else
+		_textArea.hLine(2, 3, width - 3, 15);
+
+	_textArea.vLine(1, 4, height - 3, 15);
+	_textArea.vLine(width - 2, 4, height - 3, 15);
+	_textArea.hLine(2, height - 2, width - 3, 15);
+}
+
+void MacIndy3Gui::printCharToTextArea(int chr, int x, int y, int color) {
+	// In black and white mode, all text is white. Text is never disabled.
+	if (_vm->_renderMode == Common::kRenderMacintoshBW)
+		color = 15;
+
+	// Since we're working with unscaled coordinates most of the time, the
+	// lines of the text box weren't spaced quite as much as in the
+	// original. I thought no one would notice, but I was wrong. This is
+	// the best way I can think of to fix that.
+
+	if (y > 0)
+		y = 17;
+
+	const Graphics::Font *font = getFont(kIndy3FontMedium);
+
+	font->drawChar(&_textArea, chr, x + 5, y + 11, color);
+}
+
 bool MacIndy3Gui::handleMenu(int id, Common::String &name) {
 	if (MacGui::handleMenu(id, name))
 		return true;
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index cfe39e2bb01..b7e29e85007 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -34,6 +34,7 @@ class MacWindowManager;
 namespace Scumm {
 
 class ScummEngine;
+class Actor;
 
 class MacGui {
 protected:
@@ -218,11 +219,20 @@ public:
 
 	const Common::String name() const { return "Indy"; }
 
+	Graphics::Surface _textArea;
+
 	const Graphics::Font *getFontByScummId(int32 id);
 	bool getFontParams(FontId fontId, int &id, int &size, int &slant);
 
 	void setupCursor(int &width, int &height, int &hotspotX, int &hotspotY, int &animate);
 
+	Graphics::Surface *textArea() { return &_textArea; }
+	void clearTextArea() {
+		_textArea.fillRect(Common::Rect(_textArea.w, _textArea.h), 0);
+	}
+	void initTextAreaForActor(Actor *a, byte color);
+	void printCharToTextArea(int chr, int x, int y, int color);
+
 	bool handleMenu(int id, Common::String &name);
 
 	void showAboutDialog();
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index d6316b41473..ed4619ee72b 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -475,11 +475,6 @@ ScummEngine::~ScummEngine() {
 		delete _macScreen;
 	}
 
-	if (_macIndy3TextBox) {
-		_macIndy3TextBox->free();
-		delete _macIndy3TextBox;
-	}
-
 	delete _macGui;
 
 #ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
@@ -1170,8 +1165,6 @@ Common::Error ScummEngine::init() {
 					_macScreen = new Graphics::Surface();
 					_macScreen->create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
 
-					_macIndy3TextBox = new Graphics::Surface();
-					_macIndy3TextBox->create(448, 47, Graphics::PixelFormat::createFormatCLUT8());
 					_macGui = new MacIndy3Gui(this, macResourceFile);
 					break;
 				}
@@ -1707,7 +1700,7 @@ void ScummEngine::resetScumm() {
 
 	if (_macGui) {
 		if (_game.id == GID_INDY3)
-			_macIndy3TextBox->fillRect(Common::Rect(_macIndy3TextBox->w, _macIndy3TextBox->h), 0);
+			((MacIndy3Gui *)_macGui)->clearTextArea();
 		_macGui->reset();
 	}
 
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index 92398aa50d5..cbec61d0c5b 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -1403,7 +1403,6 @@ protected:
 	void mac_markScreenAsDirty(int x, int y, int w, int h);
 	void mac_drawStripToScreen(VirtScreen *vs, int top, int x, int y, int width, int height);
 	void mac_drawLoomPracticeMode();
-	void mac_createIndy3TextBox(Actor *a);
 	void mac_drawIndy3TextBox();
 	void mac_undrawIndy3TextBox();
 	void mac_undrawIndy3CreditsText();
@@ -1575,7 +1574,6 @@ public:
 
 	Graphics::MacFontManager *_macFontManager = nullptr;
 	Graphics::Surface *_macScreen = nullptr;
-	Graphics::Surface *_macIndy3TextBox = nullptr;
 	MacGui *_macGui = nullptr;
 
 protected:
diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp
index 4aac24a5fa1..0c152ffb92f 100644
--- a/engines/scumm/string.cpp
+++ b/engines/scumm/string.cpp
@@ -29,6 +29,7 @@
 #include "scumm/charset.h"
 #include "scumm/dialogs.h"
 #include "scumm/file.h"
+#include "scumm/gfx_mac.h"
 #include "scumm/imuse_digi/dimuse_engine.h"
 #ifdef ENABLE_HE
 #include "scumm/he/intern_he.h"
@@ -1140,7 +1141,7 @@ void ScummEngine::CHARSET_1() {
 
 		if (createTextBox) {
 			if (!_keepText)
-				mac_createIndy3TextBox(a);
+				((MacIndy3Gui *)_macGui)->initTextAreaForActor(a, _charset->getColor());
 			createTextBox = false;
 			drawTextBox = true;
 		}


Commit: 38942ff71928011d64ee5e94a4deb215c8947e69
    https://github.com/scummvm/scummvm/commit/38942ff71928011d64ee5e94a4deb215c8947e69
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Move Mac Loom's practice box drawing into MacLoomGui

Also, make the box draggable like in the original. As an enhancement,
allow the box to be dragged to any X coordinate, not just ones on
16-pixel boundaries. The original allowed you to drag it all the way to
the top of the game area, but this interfers with the auto-appearing
menu so don't allow that.

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h
    engines/scumm/script.cpp
    engines/scumm/scumm.cpp
    engines/scumm/scumm.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index d92ed1398ea..3d65a308d3b 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -126,60 +126,6 @@ void ScummEngine::mac_drawStripToScreen(VirtScreen *vs, int top, int x, int y, i
 	_system->copyRectToScreen(_macScreen->getBasePtr(x * 2, y * 2), _macScreen->pitch, x * 2, y * 2, width * 2, height * 2);
 }
 
-void ScummEngine::mac_drawLoomPracticeMode() {
-	// In practice mode, the game shows the notes as they are being played.
-	// In the DOS version, this is drawn by script 27 but the Mac version
-	// just sets variables 50 and 54. The box is actually a verb, and it
-	// seems that setting variable 50 is pretty much equal to turning verb
-	// 53 on or off. I'm not sure what the purpose of variable 54 is.
-
-	int x = 216;
-	int y = 377;
-	int width = 62;
-	int height = 22;
-	int var = 50;
-
-	byte *ptr = (byte *)_macScreen->getBasePtr(x,  y);
-	int pitch = _macScreen->pitch;
-
-	int slot = getVerbSlot(53, 0);
-	VerbSlot *vs = &_verbs[slot];
-
-	vs->curmode = (VAR(var) != 0);
-	vs->curRect.left = x / 2;
-	vs->curRect.right = (x + width) / 2;
-	vs->curRect.top = y / 22;
-	vs->curRect.bottom = (y + height) / 2;
-
-	_macScreen->fillRect(Common::Rect(x, y, x + width, y + height), 0);
-
-	if (VAR(var)) {
-		for (int w = 1; w < width - 1; w++) {
-			ptr[w] = 7;
-			ptr[w + pitch * (height - 1)] = 7;
-		}
-
-		for (int h = 1; h < height - 1; h++) {
-			ptr[h * pitch] = 7;
-			ptr[h * pitch + width - 1] = 7;
-		}
-
-		// Draw the notes
-		int colors[] = { 4, 12, 14, 10, 11, 3, 9, 15 };
-
-		for (int i = 0; i < 4; i++) {
-			int note = (VAR(var) >> (4 * i)) & 0x0F;
-
-			if (note >= 2 && note <= 9) {
-				_charset->setColor(colors[note - 2]);
-				_charset->drawChar(14 + note, *_macScreen, i * 13 + x + 8, y + 4);
-			}
-		}
-	}
-
-	_system->copyRectToScreen(ptr, pitch, x, y, width, height);
-}
-
 void ScummEngine::mac_drawIndy3TextBox() {
 	Graphics::Surface *s = ((MacIndy3Gui *)_macGui)->textArea();
 
@@ -869,6 +815,21 @@ MacGui::SimpleWindow *MacGui::openWindow(Common::Rect bounds, SimpleWindowStyle
 // The Mac Loom GUI. This one is pretty simple.
 // ===========================================================================
 
+MacLoomGui::MacLoomGui(ScummEngine *vm, Common::String resourceFile) : MacGui(vm, resourceFile) {
+	// The practice box can be moved, but this is its default position on
+	// a large screen, and it's not saved.
+
+	_practiceBoxX = 215;
+	_practiceBoxY = 376;
+}
+
+MacLoomGui::~MacLoomGui() {
+	if (_practiceBox) {
+		_practiceBox->free();
+		delete _practiceBox;
+	}
+}
+
 const Graphics::Font *MacLoomGui::getFontByScummId(int32 id) {
 	switch (id) {
 	case 0:
@@ -1209,6 +1170,208 @@ void MacLoomGui::showAboutDialog() {
 	delete window;
 }
 
+void MacLoomGui::resetAfterLoad() {
+	reset();
+
+	// We used to use verb 53 for the Loom practice box, and while it's
+	// still the verb we pretend to use when clicking on it we no longer
+	// use the actual verb slot.
+	//
+	// Apparently the practice box isn't restored on saving, so it seems
+	// that savegame compatibility isn't broken. And if it is, it happened
+	// shortly after the savegame version was increased for other reasons,
+	// so the damage would be very limited.
+
+	for (int i = 0; i < _vm->_numVerbs; i++) {
+		if (_vm->_verbs[i].verbid == 53)
+			_vm->killVerb(i);
+	}
+}
+
+void MacLoomGui::update(int delta) {
+	// Unlike the PC version, the Macintosh version of Loom appears to
+	// hard-code the drawing of the practice mode box. This is handled by
+	// script 27 in both versions, but whereas the PC version draws the
+	// notes, the Mac version just sets variables 50 and 54.
+	//
+	// In this script, the variables are set to the same value but it
+	// appears that only variable 50 is cleared when the box is supposed to
+	// disappear. I don't know what the purpose of variable 54 is.
+	//
+	// Variable 128 is the game difficulty:
+	//
+	// 0 - Practice
+	// 1 - Standard
+	// 2 - Expert
+	//
+	// Note that the practice mode box is never inscribed on the "Mac
+	// screen" surface. It's drawn last on every update, so it floats
+	// above everything else.
+
+	int notes = _vm->VAR(50);
+
+	if (_vm->VAR(128) == 0) {
+		if (notes) {
+			int w = 64;
+			int h = 24;
+
+			if (!_practiceBox) {
+				debug(1, "MacLoomGui: Creating practice mode box");
+
+				_practiceBox = new Graphics::Surface();
+				_practiceBox->create(w, h, Graphics::PixelFormat
+::createFormatCLUT8());
+
+				_practiceBox->fillRect(Common::Rect(0, 0, 62, 22), kBlack);
+
+				_practiceBox->hLine(2, 1, w - 3, kLightGray);
+				_practiceBox->hLine(2, h - 2, w - 3, kLightGray);
+				_practiceBox->vLine(1, 2, h - 3, kLightGray);
+				_practiceBox->vLine(w - 2, 2, h - 3, kLightGray);
+				_practiceBoxNotes = 0;
+			}
+
+			if (notes != _practiceBoxNotes) {
+				debug(1, "MacLoomGui: Drawing practice mode notes");
+
+				_practiceBoxNotes = notes;
+
+				const Graphics::Font *font = getFont(kLoomFontLarge);
+				Color colors[] = { kRed, kBrightRed, kBrightYellow, kBrightGreen, kBrightCyan, kCyan, kBrightBlue, kWhite };
+
+				for (int i = 0; i < 4; i++) {
+					int note = (notes >> (4 * i)) & 0x0F;
+
+					if (note >= 2 && note <= 9)
+						font->drawChar(_practiceBox, 14 + note, 9 + i * 13, 5, colors[note - 2]);
+				}
+			}
+
+			_system->copyRectToScreen(_practiceBox->getBasePtr(0, 0), _practiceBox->pitch, _practiceBoxX, _practiceBoxY, w, h);
+		} else {
+			if (_practiceBox) {
+				debug(1, "MacLoomGui: Deleting practice mode box");
+
+				int w = _practiceBox->w;
+				int h = _practiceBox->h;
+
+				_practiceBox->free();
+				delete _practiceBox;
+				_practiceBox = nullptr;
+
+				_system->copyRectToScreen(_surface->getBasePtr(_practiceBoxX, _practiceBoxY), _surface->pitch, _practiceBoxX, _practiceBoxY, w, h);
+			}
+		}
+	}
+}
+
+bool MacLoomGui::handleEvent(Common::Event &event) {
+	if (MacGui::handleEvent(event))
+		return true;
+
+	if (!_practiceBox || _vm->_userPut <= 0)
+		return false;
+
+	// Perhaps the silliest feature in Mac Loom, that literally only one
+	// person has ever asked for: You can drag the Loom practice box.
+	//
+	// The game will freeze while the button is held down, but that's how
+	// the original acted as well. Should sounds keep playing? I don't know
+	// if that situation can even occur. I think it's nicer to let them
+	// play if it does.
+
+	if (event.type != Common::EVENT_LBUTTONDOWN)
+		return false;
+
+	Common::Rect bounds;
+
+	bounds.left = _practiceBoxX;
+	bounds.top = _practiceBoxY;
+	bounds.right = _practiceBoxX + _practiceBox->w;
+	bounds.bottom = _practiceBoxY + _practiceBox->h;
+
+	if (!bounds.contains(event.mouse))
+		return false;
+
+	int clickX = event.mouse.x;
+	int clickY = event.mouse.y;
+	bool dragMode = false;
+
+	while (!_vm->shouldQuit()) {
+		bool dragging = false;
+		int dragX;
+		int dragY;
+
+		while (_system->getEventManager()->pollEvent(event)) {
+			switch (event.type) {
+			case Common::EVENT_LBUTTONUP:
+				if (!dragMode)
+					_vm->runInputScript(kVerbClickArea, 53, 1);
+				return true;
+
+			case Common::EVENT_MOUSEMOVE:
+				if (ABS(event.mouse.x - clickX) >= 3 || ABS(event.mouse.y - clickY) >= 3)
+					dragMode = true;
+
+				if (dragMode) {
+					dragging = true;
+					dragX = event.mouse.x;
+					dragY = event.mouse.y;
+				}
+
+				break;
+
+			default:
+				break;
+			}
+		}
+
+		if (dragging) {
+			// How much has the mouse moved since the initial
+			// click? Calculate new position from that.
+
+			int newX = bounds.left + (dragX - clickX);
+			int newY = bounds.top + (dragY - clickY);
+
+			// The box has to stay completely inside the screen.
+			// Also, things get weird if you move the box into the
+			// menu hotzone, so don't allow that.
+
+			newX = CLIP(newX, 0, _surface->w - _practiceBox->w);
+			newY = CLIP(newY, 23, _surface->h - _practiceBox->h);
+
+			// For some reason, X coordinates can only change in
+			// increments of 16 pixels.
+
+			if (!_vm->_enableEnhancements)
+				newX &= ~0xF;
+
+			if (newX != _practiceBoxX || newY != _practiceBoxY) {
+				int w = _practiceBox->w;
+				int h = _practiceBox->h;
+
+				// The old and new rect will almost certainly
+				// overlap, so it's possible to optimize this.
+				// But increasing the delay in the event loop
+				// was a better optimization than removing one
+				// of the copyRectToScreen() calls completely,
+				// so I doubt it's worth it.
+
+				_system->copyRectToScreen(_surface->getBasePtr(_practiceBoxX, _practiceBoxY), _surface->pitch, _practiceBoxX, _practiceBoxY, w, h);
+				_system->copyRectToScreen(_practiceBox->getBasePtr(0, 0), _practiceBox->pitch, newX, newY, w, h);
+
+				_practiceBoxX = newX;
+				_practiceBoxY = newY;
+			}
+
+			_system->delayMillis(25);
+			_system->updateScreen();
+		}
+	}
+
+	return false;
+}
+
 // ===========================================================================
 // The Mac GUI for Indiana Jones and the Last Crusade, including the infamous
 // verb GUI.
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index b7e29e85007..dbbd940500a 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -174,8 +174,8 @@ public:
 
 	virtual bool isVerbGuiActive() const { return false; }
 	virtual void reset() {}
-	virtual void resetAfterLoad() {}
-	virtual void update(int delta) {}
+	virtual void resetAfterLoad() = 0;
+	virtual void update(int delta) = 0;
 
 	void updateWindowManager();
 
@@ -191,9 +191,15 @@ public:
 };
 
 class MacLoomGui : public MacGui {
+private:
+	Graphics::Surface *_practiceBox = nullptr;
+	int _practiceBoxX;
+	int _practiceBoxY;
+	int _practiceBoxNotes;
+
 public:
-	MacLoomGui(ScummEngine *vm, Common::String resourceFile) : MacGui(vm, resourceFile) {}
-	~MacLoomGui() {}
+	MacLoomGui(ScummEngine *vm, Common::String resourceFile);
+	~MacLoomGui();
 
 	const Common::String name() const { return "Loom"; }
 
@@ -205,6 +211,10 @@ public:
 	bool handleMenu(int id, Common::String &name);
 
 	void showAboutDialog();
+
+	void resetAfterLoad();
+	void update(int delta);
+	bool handleEvent(Common::Event &event);
 };
 
 class MacIndy3Gui : public MacGui {
diff --git a/engines/scumm/script.cpp b/engines/scumm/script.cpp
index fb8a286810b..29e58a0061b 100644
--- a/engines/scumm/script.cpp
+++ b/engines/scumm/script.cpp
@@ -742,23 +742,6 @@ void ScummEngine::writeVar(uint var, int value) {
 
 		_scummVars[var] = value;
 
-		// Unlike the PC version, the Macintosh version of Loom appears
-		// to hard-code the drawing of the practice mode box. This is
-		// handled by script 27 in both versions, but whereas the PC
-		// version draws the notes, the Mac version just sets
-		// variables 50 and 54.
-		//
-		// In this script, the variables are set to the same value but
-		// it appears that only variable 50 is cleared when the box is
-		// supposed to disappear. I don't know what the purpose of
-		// variable 54 is.
-
-		if (_game.id == GID_LOOM && _game.platform == Common::kPlatformMacintosh) {
-			if (VAR(128) == 0 && var == 50) {
-				mac_drawLoomPracticeMode();
-			}
-		}
-
 		if ((_varwatch == (int)var || _varwatch == 0) && _currentScript < NUM_SCRIPT_SLOT) {
 			if (vm.slot[_currentScript].number < 100)
 				debug(1, "vars[%d] = %d (via script-%d)", var, value, vm.slot[_currentScript].number);
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index ed4619ee72b..c1f0b755e99 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -2450,6 +2450,10 @@ Common::Error ScummEngine::go() {
 		if (!isPaused()) {
 			scummLoop(delta);
 
+			// The Mac GUI is updated after the engine has had a
+			// chance to update the screen. That way, it can draw
+			// things over the regular graphics, if needed.
+
 			if (_macGui)
 				_macGui->update(delta);
 
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index cbec61d0c5b..2bc646c74a3 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -523,6 +523,7 @@ class ScummEngine : public Engine, public Common::Serializable {
 	friend class CharsetRendererTownsClassic;
 	friend class ResourceManager;
 	friend class MacIndy3Gui;
+	friend class MacLoomGui;
 
 public:
 	/* Put often used variables at the top.
@@ -1402,7 +1403,6 @@ protected:
 
 	void mac_markScreenAsDirty(int x, int y, int w, int h);
 	void mac_drawStripToScreen(VirtScreen *vs, int top, int x, int y, int width, int height);
-	void mac_drawLoomPracticeMode();
 	void mac_drawIndy3TextBox();
 	void mac_undrawIndy3TextBox();
 	void mac_undrawIndy3CreditsText();


Commit: 5ad9ac4c128946406d0c2b9fa28b953666e31eea
    https://github.com/scummvm/scummvm/commit/5ad9ac4c128946406d0c2b9fa28b953666e31eea
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Add dialog drawing to Mac Gui

However, the dialogs only act as static pictures. You can't interact
with them. The file dialogs are not implemented at all, and I need to
add word wrapping.

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 3d65a308d3b..29309971faf 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -30,6 +30,8 @@
 #include "graphics/macgui/macfontmanager.h"
 #include "graphics/macgui/macwindowmanager.h"
 
+#include "image/pict.h"
+
 #include "scumm/actor.h"
 #include "scumm/charset.h"
 #include "scumm/gfx_mac.h"
@@ -593,15 +595,47 @@ bool MacGui::getFontParams(FontId fontId, int &id, int &size, int &slant) {
 
 Graphics::Surface *MacGui::loadPict(int id) {
 	Common::MacResManager resource;
+	Graphics::Surface *s = nullptr;
 
 	resource.open(_resourceFile);
 
 	Common::SeekableReadStream *res = resource.getResource(MKTAG('P', 'I', 'C', 'T'), id);
 
-	// The PICT decoder is mainly for v2 pictures, so at least for now we
-	// don't use it. The images we need to decode are simple bitmaps
-	// anyway.
+	// IQ logos are PICT v2
+	if (id == 4000 || id == 4001) {
+		Image::PICTDecoder pict;
+		if (pict.loadStream(*res)) {
+			const Graphics::Surface *s1 = pict.getSurface();
+			const byte *palette = pict.getPalette();
+
+			s = new Graphics::Surface();
+			s->create(s1->w, s1->h, Graphics::PixelFormat::createFormatCLUT8());
+
+			// The palette doesn't match the game's palette at all, so remap
+			// the colors to the custom area of the palette. It's assumed that
+			// only one such picture will be loaded at a time.
+
+			if (palette) {
+				_system->getPaletteManager()->setPalette(palette, kCustomColor, pict.getPaletteColorCount());
+
+				for (int y = 0; y < s->h; y++) {
+					for (int x = 0; x < s->w; x++) {
+						s->setPixel(x, y, kCustomColor + s1->getPixel(x, y));
+					}
+				}
+			} else
+				s->copyFrom(*s1);
+
+		}
+	} else {
+		s = decodePictV1(res);
+	}
+
+	resource.close();
+	return s;
+}
 
+Graphics::Surface *MacGui::decodePictV1(Common::SeekableReadStream *res) {
 	uint16 size = res->readUint16BE();
 
 	uint16 top = res->readUint16BE();
@@ -621,10 +655,10 @@ Graphics::Surface *MacGui::loadPict(int id) {
 		byte opcode = res->readByte();
 		byte value;
 		int x1, x2, y1, y2;
-		int rowBytes;
 
 		int x = 0;
 		int y = 0;
+		bool compressed = false;
 
 		switch (opcode) {
 		case 0x01: // clipRgn
@@ -638,6 +672,10 @@ Graphics::Surface *MacGui::loadPict(int id) {
 			break;
 
 		case 0x99: // PackBitsRgn
+			compressed = true;
+			// Fall through
+
+		case 0x91: // BitsRgn
 			res->skip(2);	// Skip rowBytes
 
 			y1 = res->readSint16BE();
@@ -650,37 +688,58 @@ Graphics::Surface *MacGui::loadPict(int id) {
 			res->skip(2);	// Skip mode
 			res->skip(res->readUint16BE() - 2);	// Skip maskRgn
 
-			for (y = y1; y < y2 && y < height; y++) {
-				x = x1;
-				size = res->readByte();
+			if (!compressed) {
+				for (y = y1; y < y2 && y < height; y++) {
+					byte b = res->readByte();
+					byte bit = 0x80;
 
-				while (size > 0) {
-					byte count = res->readByte();
-					size--;
+					for (x = x1; x < x2 && x < width; x++) {
+						if (b & bit)
+							s->setPixel(x, y, kBlack);
+						else
+							s->setPixel(x, y, kWhite);
 
-					bool repeat;
+						bit >>= 1;
 
-					if (count >= 128) {
-						// Repeat value
-						count = 256 - count;
-						repeat = true;
-						value = res->readByte();
-						size--;
-					} else {
-						// Copy values
-						repeat = false;
+						if (bit == 0) {
+							b = res->readByte();
+							bit = 0x80;
+						}
 					}
+				}
+			} else {
+				for (y = y1; y < y2 && y < height; y++) {
+					x = x1;
+					size = res->readByte();
 
-					for (int j = 0; j <= count; j++) {
-						if (!repeat) {
+					while (size > 0) {
+						byte count = res->readByte();
+						size--;
+
+						bool repeat;
+
+						if (count >= 128) {
+							// Repeat value
+							count = 256 - count;
+							repeat = true;
 							value = res->readByte();
 							size--;
+						} else {
+							// Copy values
+							repeat = false;
 						}
-						for (int k = 7; k >= 0 && x < x2 && x < width; k--, x++) {
-							if (value & (1 << k))
-								s->setPixel(x, y, kBlack);
-							else
-								s->setPixel(x, y, kWhite);
+
+						for (int j = 0; j <= count; j++) {
+							if (!repeat) {
+								value = res->readByte();
+								size--;
+							}
+							for (int k = 7; k >= 0 && x < x2 && x < width; k--, x++) {
+								if (value & (1 << k))
+									s->setPixel(x, y, kBlack);
+								else
+									s->setPixel(x, y, kWhite);
+							}
 						}
 					}
 				}
@@ -697,13 +756,11 @@ Graphics::Surface *MacGui::loadPict(int id) {
 			break;
 
 		default:
-			debug("Unknown opcode: 0x%02x", opcode);
+			debug("decodePictV1: Unknown opcode: 0x%02x", opcode);
 			break;
 		}
 	}
 
-	resource.close();
-
 	return s;
 }
 
@@ -713,16 +770,194 @@ bool MacGui::handleMenu(int id, Common::String &name) {
 	if (id == 0)
 		return true;
 
+	// Originally, the menu bar would still be visible. I don't know how
+	// to replicate that effect.
+
 	_windowManager->getMenu()->closeMenu();
 
-	if (id == 100) {
+	switch (id) {
+	case 100:
 		showAboutDialog();
 		return true;
+
+	// If we ever need to handle the Edit menu, do it here.
 	}
 
 	return false;
 }
 
+// For now, this just draws the dialog. It doesn't create any widgets, and it
+// doesn't manipulate any data.
+
+Common::String MacGui::getDialogString(Common::SeekableReadStream *res, int len, Common::StringArray substitutions) {
+	Common::String str;
+
+	for (int i = 0; i < len; i++) {
+		byte s = res->readByte();
+
+		if (s == '^' && i < len - 1) {
+			i++;
+			uint nr = res->readByte() - '0';
+
+			if (nr < substitutions.size())
+				str += substitutions[nr];
+			else
+				str += "?";
+		} else
+			str += s;
+	}
+
+	return str;
+}
+
+void MacGui::drawDialog(int dialogId, Common::StringArray substitutions, int defaultButton) {
+	Common::MacResManager resource;
+	Common::SeekableReadStream *res;
+
+	resource.open(_resourceFile);
+
+	Common::Rect bounds;
+
+	res = resource.getResource(MKTAG('D', 'L', 'O', 'G'), dialogId);
+	if (res) {
+		bounds.top = res->readUint16BE();
+		bounds.left = res->readUint16BE();
+		bounds.bottom = res->readUint16BE();
+		bounds.right = res->readUint16BE();
+
+		// Grow the window to include the outer bounds
+		bounds.grow(8);
+
+		// Compensate for the original not drawing the game at the very top of
+		// the screen.
+		bounds.translate(0, -40);
+	} else {
+		bounds.top = 0;
+		bounds.left = 0;
+		bounds.bottom = 86;
+		bounds.right = 340;
+
+		bounds.translate(86, 88);
+	}
+
+	SimpleWindow *window = openWindow(bounds);
+	Graphics::Surface *s = window->innerSurface();
+
+	res = resource.getResource(MKTAG('D', 'I', 'T', 'L'), dialogId);
+
+	if (res) {
+		int numItems = res->readUint16BE() + 1;
+
+		const Graphics::Font *font = getFont(kSystemFont);
+
+		for (int i = 0; i < numItems; i++) {
+			res->skip(4);	// Placeholder for handle or procedure pointer
+
+			Common::Rect r;
+			int x, y;
+
+			r.top = res->readUint16BE();
+			r.left = res->readUint16BE();
+			r.bottom = res->readUint16BE();
+			r.right = res->readUint16BE();
+
+			r.translate(2, 2);
+
+			int type = res->readByte();
+			int len = res->readByte();
+
+			Common::String str;
+			Graphics::Surface *pict;
+
+			switch (type & 0x7F) {
+			case 4:
+				// Button
+				str = getDialogString(res, len, substitutions);
+
+#if 0
+				// This would be how to draw a default button
+				r.grow(4);
+				Graphics::drawRoundRect(r, 7, kBlack, true, SimpleWindow::plotPixel, window);
+				r.grow(-3);
+				Graphics::drawRoundRect(r, 5, kWhite, true, SimpleWindow::plotPixel, window);
+				r.grow(-1);
+#endif
+
+				Graphics::drawRoundRect(r, 5, kBlack, false, SimpleWindow::plotPixel, window);
+				font->drawString(s, str, r.left, r.top + 2, r.width(), kBlack, Graphics::kTextAlignCenter);
+				break;
+
+			case 5:
+				// Checkbox
+				str = getDialogString(res, len, substitutions);
+
+				x = r.left + 2;
+				y = r.bottom - r.height() / 2 - 6;
+
+				s->hLine(x, y, x + 11, kBlack);
+				s->hLine(x, y + 11, x + 11, kBlack);
+				s->vLine(x, y + 1, y + 10, kBlack);
+				s->vLine(x + 11, y + 1, y + 10, kBlack);
+				s->drawLine(x + 1, y + 1, x + 10, y + 10, kBlack);
+				s->drawLine(x + 1, y + 10, x + 10, y + 1, kBlack);
+
+				font->drawString(s, str, x + 16, y - 2, r.width() - 16, kBlack);
+				break;
+
+			case 8:
+				// Static text
+				str = getDialogString(res, len, substitutions);
+				font->drawString(s, str, r.left + 1, r.top, r.width(), kBlack);
+				break;
+
+			case 64:
+				// Picture
+				pict = loadPict(res->readUint16BE());
+				if (pict) {
+					window->drawSprite(pict, r.left, r.top);
+					pict->free();
+					delete pict;
+				}
+				break;
+
+			default:
+				warning("Unknown item type %d", type);
+				res->skip(len);
+				break;
+			}
+
+			if (len & 1)
+				res->skip(1);
+		}
+	}
+
+	resource.close();
+
+	window->show();
+
+	bool done = false;
+
+	while (!done && !_vm->shouldQuit()) {
+		Common::Event event;
+
+		while (_system->getEventManager()->pollEvent(event)) {
+			switch (event.type) {
+			case Common::EVENT_LBUTTONDOWN:
+				done = true;
+				break;
+
+			default:
+				break;
+			}
+		}
+
+		_system->delayMillis(10);
+		_system->updateScreen();
+	}
+
+	delete window;
+}
+
 bool MacGui::handleEvent(Common::Event &event) {
 	return _windowManager->processEvent(event);
 }
@@ -880,12 +1115,11 @@ bool MacLoomGui::getFontParams(FontId fontId, int &id, int &size, int &slant) {
 
 void MacLoomGui::setupCursor(int &width, int &height, int &hotspotX, int &hotspotY, int &animate) {
 	Common::MacResManager resource;
+	Graphics::MacCursor macCursor;
 
 	resource.open(_resourceFile);
 
-	Common::MacResIDArray resArray = resource.getResIDArray(MKTAG('C', 'U', 'R', 'S'));
-	Common::SeekableReadStream *curs = resource.getResource(MKTAG('C', 'U', 'R', 'S'), resArray[0]);
-	Graphics::MacCursor macCursor;
+	Common::SeekableReadStream *curs = resource.getResource(MKTAG('C', 'U', 'R', 'S'), 1000);
 
 	if (macCursor.readFromStream(*curs)) {
 		width = macCursor.getWidth();
@@ -897,12 +1131,61 @@ void MacLoomGui::setupCursor(int &width, int &height, int &hotspotX, int &hotspo
 		_windowManager->replaceCursor(Graphics::kMacCursorCustom, &macCursor);
 	}
 
+	resource.close();
 	delete curs;
 }
 
 bool MacLoomGui::handleMenu(int id, Common::String &name) {
 	if (MacGui::handleMenu(id, name))
 		return true;
+
+	int dialogId = -1;
+	int defaultButton = -1;
+	Common::StringArray substitutions;
+
+	switch (id) {
+	case 200:
+		// Open. Uses standard Macintosh open file dialog
+		debug("MacLoomGui: Open");
+		break;
+
+	case 201:
+		// Save. Uses standard Macintosh save file dialog
+		debug("MacLoomGui: Save");
+		break;
+
+	case 202:
+		// Restart. Standard Ok/Cancel dialog.
+		dialogId = 502;
+		substitutions.push_back("Are you sure you want to restart this game from the beginning?");
+		break;
+
+	case 203:
+		// Pause game
+		break;
+
+	case 204:
+		// Options
+		dialogId = 1000;
+		substitutions.push_back(Common::String::format("%d", _vm->VAR(_vm->VAR_MACHINE_SPEED)));
+		break;
+
+	case 205:
+		// Quit. Standard Ok/Cancel dialog.
+		dialogId = 502;
+		substitutions.push_back("Are you sure you want to quit?");
+		break;
+
+	default:
+		warning("Unknown menu command: %d", id);
+		break;
+	}
+
+	if (dialogId != -1) {
+		drawDialog(dialogId, substitutions, defaultButton);
+		return true;
+	}
+
 	return false;
 }
 
@@ -2388,6 +2671,63 @@ void MacIndy3Gui::printCharToTextArea(int chr, int x, int y, int color) {
 bool MacIndy3Gui::handleMenu(int id, Common::String &name) {
 	if (MacGui::handleMenu(id, name))
 		return true;
+
+	int dialogId = -1;
+	int defaultButton = -1;
+	Common::StringArray substitutions;
+
+	switch (id) {
+	case 200:
+		// Open. Does not work, and might not make sense.
+		// dialogId = (_vm->_renderMode == Common::kRenderMacintoshBW) ? 4000 : 4001;
+		debug("MacIndy3Gui: Open");
+		break;
+
+	case 201:
+		// Save. Does not work, and might not make sense.
+		// dialogId = (_vm->_renderMode == Common::kRenderMacintoshBW) ? 4000 : 4001;
+		debug("MacIndy3Gui: Save");
+		break;
+
+	case 202:
+		// Restart. Standard Ok/Cancel dialog.
+		dialogId = 502;
+		substitutions.push_back("Are you sure you want to restart this game from the beginning?");
+		break;
+
+	case 203:
+		// Pause game
+		break;
+
+	case 204:
+		// IQ Points
+		dialogId = (_vm->_renderMode == Common::kRenderMacintoshBW) ? 1001 : 1002;
+		substitutions.push_back(Common::String::format("%d", _vm->VAR(244)));
+		substitutions.push_back(Common::String::format("%d", _vm->VAR(245)));
+		break;
+
+	case 205:
+		// Options
+		dialogId = 1000;
+		substitutions.push_back(Common::String::format("%d", _vm->VAR(_vm->VAR_MACHINE_SPEED)));
+		break;
+
+	case 206:
+		// Quit. Standard Ok/Cancel dialog.
+		dialogId = 502;
+		substitutions.push_back("Are you sure you want to quit?");
+		break;
+
+	default:
+		debug("MacIndy3Gui::handleMenu: Unknown menu command: %d", id);
+		break;
+	}
+
+	if (dialogId != -1) {
+		drawDialog(dialogId, substitutions, defaultButton);
+		return true;
+	}
+
 	return false;
 }
 
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index dbbd940500a..b097e5ff1f9 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -68,10 +68,16 @@ protected:
 		kBrightMagenta = 13,
 		kBrightYellow = 14,
 		kWhite = 15,
+
+		// Reserved for custom colors, loaded from PICT resources.
+		kCustomColor = 100,
+
 		kBackground = 254,	// Gray or checkerboard
 		kTransparency = 255
 	};
 
+	Common::String getDialogString(Common::SeekableReadStream *res, int len, Common::StringArray substitutions);
+
 public:
 	enum TextStyle {
 		kStyleHeader,
@@ -110,6 +116,7 @@ public:
 		Common::Array<Common::Rect> _dirtyRects;
 
 		void copyToScreen(Graphics::Surface *s = nullptr) const;
+
 	public:
 		SimpleWindow(MacGui *gui, OSystem *system, Graphics::Surface *from, Common::Rect bounds, SimpleWindowStyle style = kStyleNormal);
 		~SimpleWindow();
@@ -167,10 +174,12 @@ public:
 	virtual bool getFontParams(FontId fontId, int &id, int &size, int &slant);
 
 	Graphics::Surface *loadPict(int id);
+	Graphics::Surface *decodePictV1(Common::SeekableReadStream *res);
 
 	virtual bool handleMenu(int id, Common::String &name);
 
 	virtual void showAboutDialog() = 0;
+	void drawDialog(int dialogId, Common::StringArray substitutions, int defaultButton);
 
 	virtual bool isVerbGuiActive() const { return false; }
 	virtual void reset() {}


Commit: 05a832be5ca1b73b88b19bb16289a218fe1a06a4
    https://github.com/scummvm/scummvm/commit/05a832be5ca1b73b88b19bb16289a218fe1a06a4
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Draw "Okay" as default button in Mac GUI where appropriate

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 29309971faf..fc29d1ae600 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -813,6 +813,7 @@ Common::String MacGui::getDialogString(Common::SeekableReadStream *res, int len,
 void MacGui::drawDialog(int dialogId, Common::StringArray substitutions, int defaultButton) {
 	Common::MacResManager resource;
 	Common::SeekableReadStream *res;
+	int button = 0;
 
 	resource.open(_resourceFile);
 
@@ -874,17 +875,17 @@ void MacGui::drawDialog(int dialogId, Common::StringArray substitutions, int def
 				// Button
 				str = getDialogString(res, len, substitutions);
 
-#if 0
-				// This would be how to draw a default button
-				r.grow(4);
-				Graphics::drawRoundRect(r, 7, kBlack, true, SimpleWindow::plotPixel, window);
-				r.grow(-3);
-				Graphics::drawRoundRect(r, 5, kWhite, true, SimpleWindow::plotPixel, window);
-				r.grow(-1);
-#endif
+				if (button == defaultButton) {
+					r.grow(4);
+					Graphics::drawRoundRect(r, 7, kBlack, true, SimpleWindow::plotPixel, window);
+					r.grow(-3);
+					Graphics::drawRoundRect(r, 5, kWhite, true, SimpleWindow::plotPixel, window);
+					r.grow(-1);
+				}
 
 				Graphics::drawRoundRect(r, 5, kBlack, false, SimpleWindow::plotPixel, window);
 				font->drawString(s, str, r.left, r.top + 2, r.width(), kBlack, Graphics::kTextAlignCenter);
+				button++;
 				break;
 
 			case 5:
@@ -1157,6 +1158,7 @@ bool MacLoomGui::handleMenu(int id, Common::String &name) {
 	case 202:
 		// Restart. Standard Ok/Cancel dialog.
 		dialogId = 502;
+		defaultButton = 0;
 		substitutions.push_back("Are you sure you want to restart this game from the beginning?");
 		break;
 
@@ -1173,6 +1175,7 @@ bool MacLoomGui::handleMenu(int id, Common::String &name) {
 	case 205:
 		// Quit. Standard Ok/Cancel dialog.
 		dialogId = 502;
+		defaultButton = 0;
 		substitutions.push_back("Are you sure you want to quit?");
 		break;
 
@@ -2692,6 +2695,7 @@ bool MacIndy3Gui::handleMenu(int id, Common::String &name) {
 	case 202:
 		// Restart. Standard Ok/Cancel dialog.
 		dialogId = 502;
+		defaultButton = 0;
 		substitutions.push_back("Are you sure you want to restart this game from the beginning?");
 		break;
 
@@ -2715,6 +2719,7 @@ bool MacIndy3Gui::handleMenu(int id, Common::String &name) {
 	case 206:
 		// Quit. Standard Ok/Cancel dialog.
 		dialogId = 502;
+		defaultButton = 0;
 		substitutions.push_back("Are you sure you want to quit?");
 		break;
 


Commit: bb879fa2f7485ff9764d07171c34423ad30b77ad
    https://github.com/scummvm/scummvm/commit/bb879fa2f7485ff9764d07171c34423ad30b77ad
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Began work on making the Mac dialog buttons etc. into widgets

This means that the Mac GUI will have two separate classes of widgets.
Unfortunate, perhaps, but they behave in different ways so maybe it will
be ok. For now, at least.

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index fc29d1ae600..7ced8166c1d 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -208,7 +208,145 @@ Common::KeyState ScummEngine::mac_showOldStyleBannerAndPause(const char *msg, in
 	return ks;
 }
 
-// Very simple window class
+// Very simple window class and widgets. It is perhaps unfortunate that we have
+// two different sets of widget classes, but they behave quite differently.
+
+MacGui::MacWidget::MacWidget(SimpleWindow *window, Common::Rect bounds) : _window(window), _bounds(bounds) {}
+
+MacGui::MacButton::MacButton(SimpleWindow *window, Common::Rect bounds, Common::String text, bool isDefault) : MacWidget(window, bounds), _text(text), _isDefault(isDefault) {}
+
+void MacGui::MacButton::draw() {
+	Graphics::Surface *s = _window->innerSurface();
+	Color fg, bg;
+	int x0, x1, x2, x3;
+	int y0, y1;
+
+	x0 = _bounds.left + 3;
+	x1 = _bounds.right - 4;
+
+	y0 = _bounds.top + 3;
+	y1 = _bounds.bottom - 4;
+
+	s->hLine(x0, _bounds.top, x1, kBlack);
+	s->hLine(x0, _bounds.bottom - 1, x1, kBlack);
+	s->vLine(_bounds.left, y0, y1, kBlack);
+	s->vLine(_bounds.right - 1, y0, y1, kBlack);
+
+	if (_isPressed) {
+		fg = kWhite;
+		bg = kBlack;
+	} else {
+		fg = kBlack;
+		bg = kWhite;
+	}
+
+	// The way the corners are rounded, we can fill this entire rectangle
+	// in one go.
+
+	s->fillRect(Common::Rect(_bounds.left + 1, _bounds.top + 1, _bounds.right - 1, _bounds.bottom - 1), bg);
+
+	// ScummVM's rounded rectangles aren't a complete match for QuickDraw's
+	// rounded rectangles, so we draw the corners manually.
+
+	int innerCorner[][2] = {
+		{ 1, 2 },
+		{ 1, 1 }
+	};
+
+	for (int i = 0; i < ARRAYSIZE(innerCorner); i++) {
+		x0 = _bounds.left + innerCorner[i][0];
+		x1 = _bounds.left + innerCorner[i][1];
+		x2 = _bounds.right - innerCorner[i][1] - 1;
+		x3 = _bounds.right - innerCorner[i][0] - 1;
+
+		y0 = _bounds.top + i + 1;
+		y1 = _bounds.bottom - i - 2;
+
+		s->hLine(x0, y0, x1, kBlack);
+		s->hLine(x2, y0, x3, kBlack);
+		s->hLine(x0, y1, x1, kBlack);
+		s->hLine(x2, y1, x3, kBlack);
+	}
+
+	const Graphics::Font *font = _window->_gui->getFont(kSystemFont);
+
+	font->drawString(_window->innerSurface(), _text, _bounds.left, _bounds.top + 2, _bounds.width(), fg, Graphics::kTextAlignCenter);
+
+	if (_isDefault && _firstDraw) {
+		for (int i = 0; i < 3; i++) {
+			x0 = _bounds.left + 1;
+			x1 = _bounds.right - 2;
+
+			y0 = _bounds.top + 2;
+			y1 = _bounds.bottom - 3;
+
+			s->hLine(x0, _bounds.top - 4 + i, x1, kBlack);
+			s->hLine(x0, _bounds.bottom + 3 - i, x1, kBlack);
+			s->vLine(_bounds.left - 4 + i, y0, y1, kBlack);
+			s->vLine(_bounds.right + 3 - i, y0, y1, kBlack);
+		}
+
+		int outerCorner[][2] = {
+			{ -1, 0 },
+			{ -2, 0 },
+			{ -3, 1 },
+			{ -3, -1 },
+			{ -4, -1 }
+		};
+
+		for (int i = 0; i < ARRAYSIZE(outerCorner); i++) {
+			x0 = _bounds.left + outerCorner[i][0];
+			x1 = _bounds.left + outerCorner[i][1];
+			x2 = _bounds.right - outerCorner[i][1] - 1;
+			x3 = _bounds.right - outerCorner[i][0] - 1;
+
+			y0 = _bounds.top - 3 + i;
+			y1 = _bounds.bottom + 2 - i;
+
+			s->hLine(x0, y0, x1, kBlack);
+			s->hLine(x2, y0, x3, kBlack);
+			s->hLine(x0, y1, x1, kBlack);
+			s->hLine(x2, y1, x3, kBlack);
+		}
+
+		_firstDraw = false;
+	}
+
+	// The first time, the whole window is redrawn so we don't have to
+	// mark the default button as any dirtier than a regular one.
+
+	_window->markRectAsDirty(_bounds);
+}
+
+MacGui::MacCheckbox::MacCheckbox(MacGui::SimpleWindow *window, Common::Rect bounds, Common::String text, bool isChecked) : MacWidget(window, bounds), _text(text), _isChecked(isChecked) {}
+
+void MacGui::MacCheckbox::draw() {
+	Graphics::Surface *s = _window->innerSurface();
+	Common::Rect box(_bounds.left + 2, _bounds.top + 2, _bounds.left + 14, _bounds.top + 14);
+
+	s->fillRect(box, kBlack);
+	box.grow(_isPressed ? -2 : -1);
+	s->fillRect(box, kWhite);
+
+	if (_isChecked) {
+		s->drawLine(box.left, box.top, box.right - 1, box.bottom - 1, kBlack);
+		s->drawLine(box.left, box.bottom - 1, box.right - 1, box.top, kBlack);
+	}
+
+	if (_firstDraw) {
+		const Graphics::Font *font = _window->_gui->getFont(kSystemFont);
+		int stringWidth = font->getStringWidth(_text);
+
+		int x = _bounds.left + 18;
+		int y = _bounds.top;
+
+		_bounds.right = x + stringWidth + 1;
+		font->drawString(_window->innerSurface(), _text, x, y, stringWidth, kBlack);
+		_firstDraw = false;
+	}
+
+	_window->markRectAsDirty(_bounds);
+}
 
 MacGui::SimpleWindow::SimpleWindow(MacGui *gui, OSystem *system, Graphics::Surface *from, Common::Rect bounds, SimpleWindowStyle style) : _gui(gui), _system(system), _from(from), _bounds(bounds) {
 	_pauseToken = _gui->_vm->pauseEngine();
@@ -263,6 +401,11 @@ MacGui::SimpleWindow::~SimpleWindow() {
 	copyToScreen(_backup);
 	_backup->free();
 	delete _backup;
+
+	for (uint i = 0; i < _widgets.size(); i++)
+		delete _widgets[i];
+
+	_widgets.clear();
 	_pauseToken.clear();
 }
 
@@ -278,6 +421,20 @@ void MacGui::SimpleWindow::show() {
 	_dirtyRects.clear();
 }
 
+void MacGui::SimpleWindow::addButton(Common::Rect bounds, Common::String text, bool isDefault) {
+	MacButton *button = new MacButton(this, bounds, text, isDefault);
+
+	_widgets.push_back(button);
+	button->draw();
+}
+
+void MacGui::SimpleWindow::addCheckbox(Common::Rect bounds, Common::String text, bool isChecked) {
+	MacCheckbox *checkbox = new MacCheckbox(this, bounds, text, isChecked);
+
+	_widgets.push_back(checkbox);
+	checkbox->draw();
+}
+
 void MacGui::SimpleWindow::markRectAsDirty(Common::Rect r) {
 	_dirtyRects.push_back(r);
 }
@@ -855,7 +1012,6 @@ void MacGui::drawDialog(int dialogId, Common::StringArray substitutions, int def
 			res->skip(4);	// Placeholder for handle or procedure pointer
 
 			Common::Rect r;
-			int x, y;
 
 			r.top = res->readUint16BE();
 			r.left = res->readUint16BE();
@@ -874,17 +1030,7 @@ void MacGui::drawDialog(int dialogId, Common::StringArray substitutions, int def
 			case 4:
 				// Button
 				str = getDialogString(res, len, substitutions);
-
-				if (button == defaultButton) {
-					r.grow(4);
-					Graphics::drawRoundRect(r, 7, kBlack, true, SimpleWindow::plotPixel, window);
-					r.grow(-3);
-					Graphics::drawRoundRect(r, 5, kWhite, true, SimpleWindow::plotPixel, window);
-					r.grow(-1);
-				}
-
-				Graphics::drawRoundRect(r, 5, kBlack, false, SimpleWindow::plotPixel, window);
-				font->drawString(s, str, r.left, r.top + 2, r.width(), kBlack, Graphics::kTextAlignCenter);
+				window->addButton(r, str, button == defaultButton);
 				button++;
 				break;
 
@@ -892,17 +1038,15 @@ void MacGui::drawDialog(int dialogId, Common::StringArray substitutions, int def
 				// Checkbox
 				str = getDialogString(res, len, substitutions);
 
-				x = r.left + 2;
-				y = r.bottom - r.height() / 2 - 6;
+				// The DITL may define a larger than necessary
+				// area for the checkbox, so normalize the
+				// height here. The width will be normalized
+				// when the checkbox is first drawn.
 
-				s->hLine(x, y, x + 11, kBlack);
-				s->hLine(x, y + 11, x + 11, kBlack);
-				s->vLine(x, y + 1, y + 10, kBlack);
-				s->vLine(x + 11, y + 1, y + 10, kBlack);
-				s->drawLine(x + 1, y + 1, x + 10, y + 10, kBlack);
-				s->drawLine(x + 1, y + 10, x + 10, y + 1, kBlack);
+				r.top = r.bottom - r.height() / 2 - 8;
+				r.bottom = r.top + 16;
 
-				font->drawString(s, str, x + 16, y - 2, r.width() - 16, kBlack);
+				window->addCheckbox(r, str, true);
 				break;
 
 			case 8:
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index b097e5ff1f9..df8ed7e0f21 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -99,9 +99,46 @@ public:
 		kStyleRounded
 	};
 
+	class SimpleWindow;
+
+	class MacWidget {
+	protected:
+		MacGui::SimpleWindow *_window;
+		Common::Rect _bounds;
+
+	public:
+		MacWidget(MacGui::SimpleWindow *window, Common::Rect bounds);
+		virtual ~MacWidget() {};
+
+		virtual void draw() = 0;
+	};
+
+	class MacButton : public MacWidget {
+	private:
+		Common::String _text;
+		bool _isPressed = false;
+		bool _isDefault;
+		bool _firstDraw = true;
+
+	public:
+		MacButton(MacGui::SimpleWindow *window, Common::Rect bounds, Common::String text, bool isDefault);
+		void draw();
+	};
+
+	class MacCheckbox : public MacWidget {
+	private:
+		Common::String _text;
+		bool _isPressed = false;
+		bool _isChecked;
+		bool _firstDraw = true;
+
+	public:
+		MacCheckbox(MacGui::SimpleWindow *window, Common::Rect bounds, Common::String text, bool isChecked);
+		void draw();
+	};
+
 	class SimpleWindow {
 	private:
-		MacGui *_gui;
 		OSystem *_system;
 		Common::Rect _bounds;
 		int _margin;
@@ -113,11 +150,15 @@ public:
 		Graphics::Surface _surface;
 		Graphics::Surface _innerSurface;
 
+		Common::Array<MacWidget *> _widgets;
+
 		Common::Array<Common::Rect> _dirtyRects;
 
 		void copyToScreen(Graphics::Surface *s = nullptr) const;
 
 	public:
+		MacGui *_gui;
+
 		SimpleWindow(MacGui *gui, OSystem *system, Graphics::Surface *from, Common::Rect bounds, SimpleWindowStyle style = kStyleNormal);
 		~SimpleWindow();
 
@@ -126,6 +167,9 @@ public:
 
 		void show();
 
+		void addButton(Common::Rect bounds, Common::String text, bool isDefault);
+		void addCheckbox(Common::Rect bounds, Common::String text, bool isChecked);
+
 		void markRectAsDirty(Common::Rect r);
 		void update(bool fullRedraw = false);
 


Commit: f45ba49b56557986c78222986cfa01e4f642fe5f
    https://github.com/scummvm/scummvm/commit/f45ba49b56557986c78222986cfa01e4f642fe5f
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Give the Mac dialog an event loop, and start acting on events

I still need to figure out a good way to communicate the state of the
dialog back to the caller. And implement the slider widget.

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 7ced8166c1d..ef68a6ce059 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -348,6 +348,11 @@ void MacGui::MacCheckbox::draw() {
 	_window->markRectAsDirty(_bounds);
 }
 
+void MacGui::MacCheckbox::action() {
+	_isChecked = !_isChecked;
+	draw();
+}
+
 MacGui::SimpleWindow::SimpleWindow(MacGui *gui, OSystem *system, Graphics::Surface *from, Common::Rect bounds, SimpleWindowStyle style) : _gui(gui), _system(system), _from(from), _bounds(bounds) {
 	_pauseToken = _gui->_vm->pauseEngine();
 
@@ -421,18 +426,21 @@ void MacGui::SimpleWindow::show() {
 	_dirtyRects.clear();
 }
 
-void MacGui::SimpleWindow::addButton(Common::Rect bounds, Common::String text, bool isDefault) {
-	MacButton *button = new MacButton(this, bounds, text, isDefault);
+MacGui::MacWidget *MacGui::SimpleWindow::findWidget(int x, int y) {
+	for (uint i = 0; i < _widgets.size(); i++) {
+		if (_widgets[i]->findWidget(x, y))
+			return _widgets[i];
+	}
 
-	_widgets.push_back(button);
-	button->draw();
+	return nullptr;
 }
 
-void MacGui::SimpleWindow::addCheckbox(Common::Rect bounds, Common::String text, bool isChecked) {
-	MacCheckbox *checkbox = new MacCheckbox(this, bounds, text, isChecked);
+void MacGui::SimpleWindow::addButton(Common::Rect bounds, Common::String text, bool isDefault) {
+	_widgets.push_back(new MacButton(this, bounds, text, isDefault));
+}
 
-	_widgets.push_back(checkbox);
-	checkbox->draw();
+void MacGui::SimpleWindow::addCheckbox(Common::Rect bounds, Common::String text, bool isChecked) {
+	_widgets.push_back(new MacCheckbox(this, bounds, text, isChecked));
 }
 
 void MacGui::SimpleWindow::markRectAsDirty(Common::Rect r) {
@@ -472,6 +480,59 @@ void MacGui::SimpleWindow::drawSprite(const Graphics::Surface *sprite, int x, in
 	markRectAsDirty(Common::Rect(x, y, x + sprite->w, y + sprite->h));
 }
 
+void MacGui::SimpleWindow::runDialog() {
+	for (uint i = 0; i < _widgets.size(); i++)
+		_widgets[i]->draw();
+
+	show();
+
+	bool done = false;
+	MacGui::MacWidget *pressedWidget = nullptr;
+
+	while (!done && !_gui->_vm->shouldQuit()) {
+		Common::Event event;
+
+		while (_system->getEventManager()->pollEvent(event)) {
+			if (Common::isMouseEvent(event)) {
+				event.mouse.x -= (_bounds.left + _margin);
+				event.mouse.y -= (_bounds.top + _margin);
+			}
+
+			switch (event.type) {
+			case Common::EVENT_LBUTTONDOWN:
+				pressedWidget = findWidget(event.mouse.x, event.mouse.y);
+				if (pressedWidget)
+					pressedWidget->setPressed(true);
+				break;
+
+			case Common::EVENT_LBUTTONUP:
+				if (pressedWidget) {
+					pressedWidget->setPressed(false);
+					pressedWidget->action();
+					pressedWidget = nullptr;
+				}
+				break;
+
+			case Common::EVENT_MOUSEMOVE:
+				if (pressedWidget) {
+					if (!pressedWidget->findWidget(event.mouse.x, event.mouse.y)) {
+						pressedWidget->setPressed(false);
+						pressedWidget = nullptr;
+					}
+				}
+				break;
+
+			default:
+				break;
+			}
+		}
+
+		_system->delayMillis(10);
+		update();
+		_system->updateScreen();
+	}
+}
+
 void MacGui::SimpleWindow::drawSprite(const Graphics::Surface *sprite, int x, int y, Common::Rect(clipRect)) {
 	Common::Rect subRect(0, 0, sprite->w, sprite->h);
 
@@ -967,7 +1028,95 @@ Common::String MacGui::getDialogString(Common::SeekableReadStream *res, int len,
 	return str;
 }
 
-void MacGui::drawDialog(int dialogId, Common::StringArray substitutions, int defaultButton) {
+bool MacGui::handleEvent(Common::Event &event) {
+	return _windowManager->processEvent(event);
+}
+
+void MacGui::setPalette(const byte *palette, uint size) {
+	_windowManager->passPalette(palette, size);
+}
+
+void MacGui::updateWindowManager() {
+	// TODO: Originally, the left Alt button opens the menu. In ScummVM,
+	//       it triggers on mouse-over. Preferrably both should work (the
+	//       latter should work better for touch screens), but it's hard
+	//       to get them to coexist.
+
+	// We want the arrow cursor for menus. Note that the menu triggers even
+	// when the mouse is invisible, which may or may not be a bug. But the
+	// original did allow you to open the menu with Alt even when the
+	// cursor was visible, so for now it's a feature.
+
+	bool isActive = _windowManager->isMenuActive();
+
+	if (isActive) {
+		if (!_menuIsActive) {
+			_cursorWasVisible = CursorMan.showMouse(true);
+			_windowManager->pushCursor(Graphics::kMacCursorArrow);
+		}
+	} else {
+		if (_menuIsActive) {
+			if (_windowManager->getCursorType() == Graphics::kMacCursorArrow)
+				_windowManager->popCursor();
+			CursorMan.showMouse(_cursorWasVisible);
+		}
+	}
+
+	_menuIsActive = isActive;
+	_windowManager->draw();
+}
+
+MacGui::SimpleWindow *MacGui::drawBanner(char *message) {
+	MacGui::SimpleWindow *window = createWindow(Common::Rect(70, 189, 570, 211), kStyleRounded);
+	const Graphics::Font *font = getFont(_vm->_game.id == GID_INDY3 ? kIndy3FontMedium : kLoomFontMedium);
+
+	Graphics::Surface *s = window->innerSurface();
+	font->drawString(s, (char *)message, 0, 0, s->w, kBlack, Graphics::kTextAlignCenter);
+
+	window->show();
+	return window;
+}
+
+int MacGui::delay(uint32 ms) {
+	uint32 to;
+
+	if (ms == (uint32)-1)
+		to = 0xFFFFFFFF;
+	else
+		to = _system->getMillis() + ms;
+
+	while (_system->getMillis() < to) {
+		Common::Event event;
+
+		while (_system->getEventManager()->pollEvent(event)) {
+			switch (event.type) {
+			case Common::EVENT_QUIT:
+				return 2;
+
+			case Common::EVENT_LBUTTONDOWN:
+				return 1;
+
+			default:
+				break;
+			}
+		}
+
+		uint32 delta = to - _system->getMillis();
+
+		if (delta > 0) {
+			_system->delayMillis(MIN<uint32>(delta, 10));
+			_system->updateScreen();
+		}
+	}
+
+	return 0;
+}
+
+MacGui::SimpleWindow *MacGui::createWindow(Common::Rect bounds, SimpleWindowStyle style) {
+	return new SimpleWindow(this, _system, _surface, bounds, style);
+}
+
+MacGui::SimpleWindow *MacGui::createDialog(int dialogId, Common::StringArray substitutions, int defaultButton) {
 	Common::MacResManager resource;
 	Common::SeekableReadStream *res;
 	int button = 0;
@@ -998,7 +1147,7 @@ void MacGui::drawDialog(int dialogId, Common::StringArray substitutions, int def
 		bounds.translate(86, 88);
 	}
 
-	SimpleWindow *window = openWindow(bounds);
+	SimpleWindow *window = createWindow(bounds);
 	Graphics::Surface *s = window->innerSurface();
 
 	res = resource.getResource(MKTAG('D', 'I', 'T', 'L'), dialogId);
@@ -1077,120 +1226,9 @@ void MacGui::drawDialog(int dialogId, Common::StringArray substitutions, int def
 	}
 
 	resource.close();
-
-	window->show();
-
-	bool done = false;
-
-	while (!done && !_vm->shouldQuit()) {
-		Common::Event event;
-
-		while (_system->getEventManager()->pollEvent(event)) {
-			switch (event.type) {
-			case Common::EVENT_LBUTTONDOWN:
-				done = true;
-				break;
-
-			default:
-				break;
-			}
-		}
-
-		_system->delayMillis(10);
-		_system->updateScreen();
-	}
-
-	delete window;
-}
-
-bool MacGui::handleEvent(Common::Event &event) {
-	return _windowManager->processEvent(event);
-}
-
-void MacGui::setPalette(const byte *palette, uint size) {
-	_windowManager->passPalette(palette, size);
-}
-
-void MacGui::updateWindowManager() {
-	// TODO: Originally, the left Alt button opens the menu. In ScummVM,
-	//       it triggers on mouse-over. Preferrably both should work (the
-	//       latter should work better for touch screens), but it's hard
-	//       to get them to coexist.
-
-	// We want the arrow cursor for menus. Note that the menu triggers even
-	// when the mouse is invisible, which may or may not be a bug. But the
-	// original did allow you to open the menu with Alt even when the
-	// cursor was visible, so for now it's a feature.
-
-	bool isActive = _windowManager->isMenuActive();
-
-	if (isActive) {
-		if (!_menuIsActive) {
-			_cursorWasVisible = CursorMan.showMouse(true);
-			_windowManager->pushCursor(Graphics::kMacCursorArrow);
-		}
-	} else {
-		if (_menuIsActive) {
-			if (_windowManager->getCursorType() == Graphics::kMacCursorArrow)
-				_windowManager->popCursor();
-			CursorMan.showMouse(_cursorWasVisible);
-		}
-	}
-
-	_menuIsActive = isActive;
-	_windowManager->draw();
-}
-
-MacGui::SimpleWindow *MacGui::drawBanner(char *message) {
-	MacGui::SimpleWindow *window = openWindow(Common::Rect(70, 189, 570, 211), kStyleRounded);
-	const Graphics::Font *font = getFont(_vm->_game.id == GID_INDY3 ? kIndy3FontMedium : kLoomFontMedium);
-
-	Graphics::Surface *s = window->innerSurface();
-	font->drawString(s, (char *)message, 0, 0, s->w, kBlack, Graphics::kTextAlignCenter);
-
-	window->show();
 	return window;
 }
 
-int MacGui::delay(uint32 ms) {
-	uint32 to;
-
-	if (ms == (uint32)-1)
-		to = 0xFFFFFFFF;
-	else
-		to = _system->getMillis() + ms;
-
-	while (_system->getMillis() < to) {
-		Common::Event event;
-
-		while (_system->getEventManager()->pollEvent(event)) {
-			switch (event.type) {
-			case Common::EVENT_QUIT:
-				return 2;
-
-			case Common::EVENT_LBUTTONDOWN:
-				return 1;
-
-			default:
-				break;
-			}
-		}
-
-		uint32 delta = to - _system->getMillis();
-
-		if (delta > 0) {
-			_system->delayMillis(MIN<uint32>(delta, 10));
-			_system->updateScreen();
-		}
-	}
-
-	return 0;
-}
-
-MacGui::SimpleWindow *MacGui::openWindow(Common::Rect bounds, SimpleWindowStyle style) {
-	return new SimpleWindow(this, _system, _surface, bounds, style);
-}
-
 // ===========================================================================
 // The Mac Loom GUI. This one is pretty simple.
 // ===========================================================================
@@ -1329,7 +1367,8 @@ bool MacLoomGui::handleMenu(int id, Common::String &name) {
 	}
 
 	if (dialogId != -1) {
-		drawDialog(dialogId, substitutions, defaultButton);
+		SimpleWindow *dialog = createDialog(dialogId, substitutions, defaultButton);
+		dialog->runDialog();
 		return true;
 	}
 
@@ -1347,7 +1386,7 @@ void MacLoomGui::showAboutDialog() {
 	int y = (400 - height) / 2;
 
 	Common::Rect bounds(x, y, x + width, y + height);
-	SimpleWindow *window = openWindow(bounds);
+	SimpleWindow *window = createWindow(bounds);
 	Graphics::Surface *lucasFilm = loadPict(5000);
 	Graphics::Surface *loom = loadPict(5001);
 
@@ -2873,7 +2912,8 @@ bool MacIndy3Gui::handleMenu(int id, Common::String &name) {
 	}
 
 	if (dialogId != -1) {
-		drawDialog(dialogId, substitutions, defaultButton);
+		SimpleWindow *dialog = createDialog(dialogId, substitutions, defaultButton);
+		dialog->runDialog();
 		return true;
 	}
 
@@ -2891,7 +2931,7 @@ void MacIndy3Gui::showAboutDialog() {
 	int y = (400 - height) / 2;
 
 	Common::Rect bounds(x, y, x + width, y + height);
-	SimpleWindow *window = openWindow(bounds);
+	SimpleWindow *window = createWindow(bounds);
 	Graphics::Surface *pict = loadPict(2000);
 
 	// For the background of the sprites to match the background of the
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index df8ed7e0f21..a3bccc861d2 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -105,42 +105,54 @@ public:
 	protected:
 		MacGui::SimpleWindow *_window;
 		Common::Rect _bounds;
+		bool _isPressed = false;
 
 	public:
 		MacWidget(MacGui::SimpleWindow *window, Common::Rect bounds);
 		virtual ~MacWidget() {};
 
+		void setPressed(bool pressed) {
+			_isPressed = pressed;
+			draw();
+		}
+
+		bool findWidget(int x, int y) {
+			return _bounds.contains(x, y);
+		}
+
 		virtual void draw() = 0;
+		virtual void action() {}
 	};
 
 	class MacButton : public MacWidget {
 	private:
 		Common::String _text;
-		bool _isPressed = false;
 		bool _isDefault;
 		bool _firstDraw = true;
 
 	public:
 		MacButton(MacGui::SimpleWindow *window, Common::Rect bounds, Common::String text, bool isDefault);
+
 		void draw();
 	};
 
 	class MacCheckbox : public MacWidget {
 	private:
 		Common::String _text;
-		bool _isPressed = false;
 		bool _isChecked;
 		bool _firstDraw = true;
 
 	public:
 		MacCheckbox(MacGui::SimpleWindow *window, Common::Rect bounds, Common::String text, bool isChecked);
 		void draw();
+		void action();
 	};
 
 	class SimpleWindow {
 	private:
 		OSystem *_system;
 		Common::Rect _bounds;
+		Common::Rect _innerBounds;
 		int _margin;
 
 		PauseToken _pauseToken;
@@ -151,7 +163,6 @@ public:
 		Graphics::Surface _innerSurface;
 
 		Common::Array<MacWidget *> _widgets;
-
 		Common::Array<Common::Rect> _dirtyRects;
 
 		void copyToScreen(Graphics::Surface *s = nullptr) const;
@@ -166,6 +177,9 @@ public:
 		Graphics::Surface *innerSurface() { return &_innerSurface; }
 
 		void show();
+		void runDialog();
+
+		MacWidget *findWidget(int x, int y);
 
 		void addButton(Common::Rect bounds, Common::String text, bool isDefault);
 		void addCheckbox(Common::Rect bounds, Common::String text, bool isChecked);
@@ -223,7 +237,6 @@ public:
 	virtual bool handleMenu(int id, Common::String &name);
 
 	virtual void showAboutDialog() = 0;
-	void drawDialog(int dialogId, Common::StringArray substitutions, int defaultButton);
 
 	virtual bool isVerbGuiActive() const { return false; }
 	virtual void reset() {}
@@ -240,7 +253,9 @@ public:
 	SimpleWindow *drawBanner(char *message);
 
 	int delay(uint32 ms);
-	SimpleWindow *openWindow(Common::Rect bounds, SimpleWindowStyle style = kStyleNormal);
+
+	SimpleWindow *createWindow(Common::Rect bounds, SimpleWindowStyle style = kStyleNormal);
+	SimpleWindow *createDialog(int dialogId, Common::StringArray substitutions, int defaultButton);
 };
 
 class MacLoomGui : public MacGui {


Commit: 9a6499b21d7f7df98b30dcb0d4929a396dd96656
    https://github.com/scummvm/scummvm/commit/9a6499b21d7f7df98b30dcb0d4929a396dd96656
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Mac GUI texts are now widgets, too

Plus some refactoring in preparation for getting the dialogs to tell,
not just show, what they're doing.

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index ef68a6ce059..8fb1014d952 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -211,9 +211,38 @@ Common::KeyState ScummEngine::mac_showOldStyleBannerAndPause(const char *msg, in
 // Very simple window class and widgets. It is perhaps unfortunate that we have
 // two different sets of widget classes, but they behave quite differently.
 
-MacGui::MacWidget::MacWidget(SimpleWindow *window, Common::Rect bounds) : _window(window), _bounds(bounds) {}
+MacGui::MacWidget::MacWidget(SimpleWindow *window, Common::Rect bounds, Common::String text) : _window(window), _bounds(bounds), _text(text) {
+}
+
+bool MacGui::MacWidget::findWidget(int x, int y) {
+	return _isEnabled && _bounds.contains(x, y);
+}
+
+int MacGui::MacWidget::drawText(Common::String &text, int x, int y, int w, Color color, Graphics::TextAlign align) {
+	const Graphics::Font *font = _window->_gui->getFont(kSystemFont);
 
-MacGui::MacButton::MacButton(SimpleWindow *window, Common::Rect bounds, Common::String text, bool isDefault) : MacWidget(window, bounds), _text(text), _isDefault(isDefault) {}
+	Common::String line;
+	int lineWidth = 0;
+
+	for (uint i = 0; i < text.size(); i++) {
+		if (text[i] == '^') {
+			Common::String &subst = _window->getSubstitution(text[++i] - '0');
+			for (uint j = 0; j < subst.size(); j++) {
+				line += subst[j];
+				lineWidth += font->getCharWidth(subst[j]);
+			}
+		} else {
+			line += text[i];
+			lineWidth += font->getCharWidth(text[i]);
+		}
+	}
+
+	font->drawString(_window->innerSurface(), line, x, y, w, color, align);
+	return lineWidth;
+}
+
+MacGui::MacButton::MacButton(SimpleWindow *window, Common::Rect bounds, Common::String text) : MacWidget(window, bounds, text) {
+}
 
 void MacGui::MacButton::draw() {
 	Graphics::Surface *s = _window->innerSurface();
@@ -268,9 +297,7 @@ void MacGui::MacButton::draw() {
 		s->hLine(x2, y1, x3, kBlack);
 	}
 
-	const Graphics::Font *font = _window->_gui->getFont(kSystemFont);
-
-	font->drawString(_window->innerSurface(), _text, _bounds.left, _bounds.top + 2, _bounds.width(), fg, Graphics::kTextAlignCenter);
+	drawText(_text, _bounds.left, _bounds.top + 2, _bounds.width(), fg, Graphics::kTextAlignCenter);
 
 	if (_isDefault && _firstDraw) {
 		for (int i = 0; i < 3; i++) {
@@ -318,7 +345,8 @@ void MacGui::MacButton::draw() {
 	_window->markRectAsDirty(_bounds);
 }
 
-MacGui::MacCheckbox::MacCheckbox(MacGui::SimpleWindow *window, Common::Rect bounds, Common::String text, bool isChecked) : MacWidget(window, bounds), _text(text), _isChecked(isChecked) {}
+MacGui::MacCheckbox::MacCheckbox(MacGui::SimpleWindow *window, Common::Rect bounds, Common::String text) : MacWidget(window, bounds, text) {
+}
 
 void MacGui::MacCheckbox::draw() {
 	Graphics::Surface *s = _window->innerSurface();
@@ -334,14 +362,11 @@ void MacGui::MacCheckbox::draw() {
 	}
 
 	if (_firstDraw) {
-		const Graphics::Font *font = _window->_gui->getFont(kSystemFont);
-		int stringWidth = font->getStringWidth(_text);
-
 		int x = _bounds.left + 18;
 		int y = _bounds.top;
 
+		int stringWidth = drawText(_text, x, y, _bounds.right - x, kBlack);
 		_bounds.right = x + stringWidth + 1;
-		font->drawString(_window->innerSurface(), _text, x, y, stringWidth, kBlack);
 		_firstDraw = false;
 	}
 
@@ -353,6 +378,14 @@ void MacGui::MacCheckbox::action() {
 	draw();
 }
 
+MacGui::MacText::MacText(MacGui::SimpleWindow *window, Common::Rect bounds, Common::String text) : MacWidget(window, bounds, text) {
+	_isEnabled = false;
+}
+
+void MacGui::MacText::draw() {
+	drawText(_text, _bounds.left, _bounds.top, _bounds.width(), kBlack);
+}
+
 MacGui::SimpleWindow::SimpleWindow(MacGui *gui, OSystem *system, Graphics::Surface *from, Common::Rect bounds, SimpleWindowStyle style) : _gui(gui), _system(system), _from(from), _bounds(bounds) {
 	_pauseToken = _gui->_vm->pauseEngine();
 
@@ -435,12 +468,16 @@ MacGui::MacWidget *MacGui::SimpleWindow::findWidget(int x, int y) {
 	return nullptr;
 }
 
-void MacGui::SimpleWindow::addButton(Common::Rect bounds, Common::String text, bool isDefault) {
-	_widgets.push_back(new MacButton(this, bounds, text, isDefault));
+void MacGui::SimpleWindow::addButton(Common::Rect bounds, Common::String text) {
+	_widgets.push_back(new MacButton(this, bounds, text));
 }
 
-void MacGui::SimpleWindow::addCheckbox(Common::Rect bounds, Common::String text, bool isChecked) {
-	_widgets.push_back(new MacCheckbox(this, bounds, text, isChecked));
+void MacGui::SimpleWindow::addCheckbox(Common::Rect bounds, Common::String text) {
+	_widgets.push_back(new MacCheckbox(this, bounds, text));
+}
+
+void MacGui::SimpleWindow::addText(Common::Rect bounds, Common::String text) {
+	_widgets.push_back(new MacText(this, bounds, text));
 }
 
 void MacGui::SimpleWindow::markRectAsDirty(Common::Rect r) {
@@ -598,7 +635,7 @@ void MacGui::SimpleWindow::plotPatternDarkenOnly(int x, int y, int pattern, void
 		s->setPixel(x, y, kBlack);
 }
 
-void MacGui::SimpleWindow::drawText(Common::Rect r, const TextLine *lines) {
+void MacGui::SimpleWindow::drawTexts(Common::Rect r, const TextLine *lines) {
 	if (!lines)
 		return;
 
@@ -649,7 +686,7 @@ void MacGui::SimpleWindow::drawTextBox(Common::Rect r, const TextLine *lines, in
 	Graphics::drawRoundRect(r, arc, kBlack, false, plotPixel, this);
 	markRectAsDirty(r);
 
-	drawText(r, lines);
+	drawTexts(r, lines);
 }
 
 // ===========================================================================
@@ -995,7 +1032,7 @@ bool MacGui::handleMenu(int id, Common::String &name) {
 
 	switch (id) {
 	case 100:
-		showAboutDialog();
+		runAboutDialog();
 		return true;
 
 	// If we ever need to handle the Edit menu, do it here.
@@ -1004,26 +1041,43 @@ bool MacGui::handleMenu(int id, Common::String &name) {
 	return false;
 }
 
-// For now, this just draws the dialog. It doesn't create any widgets, and it
-// doesn't manipulate any data.
+bool MacGui::runQuitDialog() {
+	SimpleWindow *window = createDialog(502);
 
-Common::String MacGui::getDialogString(Common::SeekableReadStream *res, int len, Common::StringArray substitutions) {
-	Common::String str;
+	window->setDefaultWidget(0);
+	window->addSubstitution("Are you sure you want to quit?");
+	window->runDialog();
 
-	for (int i = 0; i < len; i++) {
-		byte s = res->readByte();
+	delete window;
+	return true;
+}
 
-		if (s == '^' && i < len - 1) {
-			i++;
-			uint nr = res->readByte() - '0';
+bool MacGui::runRestartDialog() {
+	SimpleWindow *window = createDialog(502);
 
-			if (nr < substitutions.size())
-				str += substitutions[nr];
-			else
-				str += "?";
-		} else
-			str += s;
-	}
+	window->setDefaultWidget(0);
+	window->addSubstitution("Are you sure you want to quit?");
+	window->runDialog();
+
+	delete window;
+	return true;
+}
+
+bool MacGui::runOptionsDialog() {
+	SimpleWindow *window = createDialog(1000);
+
+	window->addSubstitution(Common::String::format("%d", _vm->VAR(_vm->VAR_MACHINE_SPEED)));
+	window->runDialog();
+
+	delete window;
+	return true;
+}
+
+Common::String MacGui::getDialogString(Common::SeekableReadStream *res, int len) {
+	Common::String str;
+
+	for (int i = 0; i < len; i++)
+		str += res->readByte();
 
 	return str;
 }
@@ -1116,7 +1170,7 @@ MacGui::SimpleWindow *MacGui::createWindow(Common::Rect bounds, SimpleWindowStyl
 	return new SimpleWindow(this, _system, _surface, bounds, style);
 }
 
-MacGui::SimpleWindow *MacGui::createDialog(int dialogId, Common::StringArray substitutions, int defaultButton) {
+MacGui::SimpleWindow *MacGui::createDialog(int dialogId) {
 	Common::MacResManager resource;
 	Common::SeekableReadStream *res;
 	int button = 0;
@@ -1148,15 +1202,12 @@ MacGui::SimpleWindow *MacGui::createDialog(int dialogId, Common::StringArray sub
 	}
 
 	SimpleWindow *window = createWindow(bounds);
-	Graphics::Surface *s = window->innerSurface();
 
 	res = resource.getResource(MKTAG('D', 'I', 'T', 'L'), dialogId);
 
 	if (res) {
 		int numItems = res->readUint16BE() + 1;
 
-		const Graphics::Font *font = getFont(kSystemFont);
-
 		for (int i = 0; i < numItems; i++) {
 			res->skip(4);	// Placeholder for handle or procedure pointer
 
@@ -1178,14 +1229,14 @@ MacGui::SimpleWindow *MacGui::createDialog(int dialogId, Common::StringArray sub
 			switch (type & 0x7F) {
 			case 4:
 				// Button
-				str = getDialogString(res, len, substitutions);
-				window->addButton(r, str, button == defaultButton);
+				str = getDialogString(res, len);
+				window->addButton(r, str);
 				button++;
 				break;
 
 			case 5:
 				// Checkbox
-				str = getDialogString(res, len, substitutions);
+				str = getDialogString(res, len);
 
 				// The DITL may define a larger than necessary
 				// area for the checkbox, so normalize the
@@ -1195,13 +1246,13 @@ MacGui::SimpleWindow *MacGui::createDialog(int dialogId, Common::StringArray sub
 				r.top = r.bottom - r.height() / 2 - 8;
 				r.bottom = r.top + 16;
 
-				window->addCheckbox(r, str, true);
+				window->addCheckbox(r, str);
 				break;
 
 			case 8:
 				// Static text
-				str = getDialogString(res, len, substitutions);
-				font->drawString(s, str, r.left + 1, r.top, r.width(), kBlack);
+				str = getDialogString(res, len);
+				window->addText(r, str);
 				break;
 
 			case 64:
@@ -1322,10 +1373,6 @@ bool MacLoomGui::handleMenu(int id, Common::String &name) {
 	if (MacGui::handleMenu(id, name))
 		return true;
 
-	int dialogId = -1;
-	int defaultButton = -1;
-	Common::StringArray substitutions;
-
 	switch (id) {
 	case 200:
 		// Open. Uses standard Macintosh open file dialog
@@ -1339,9 +1386,7 @@ bool MacLoomGui::handleMenu(int id, Common::String &name) {
 
 	case 202:
 		// Restart. Standard Ok/Cancel dialog.
-		dialogId = 502;
-		defaultButton = 0;
-		substitutions.push_back("Are you sure you want to restart this game from the beginning?");
+		runRestartDialog();
 		break;
 
 	case 203:
@@ -1350,15 +1395,12 @@ bool MacLoomGui::handleMenu(int id, Common::String &name) {
 
 	case 204:
 		// Options
-		dialogId = 1000;
-		substitutions.push_back(Common::String::format("%d", _vm->VAR(_vm->VAR_MACHINE_SPEED)));
+		runOptionsDialog();
 		break;
 
 	case 205:
 		// Quit. Standard Ok/Cancel dialog.
-		dialogId = 502;
-		defaultButton = 0;
-		substitutions.push_back("Are you sure you want to quit?");
+		runQuitDialog();
 		break;
 
 	default:
@@ -1366,16 +1408,10 @@ bool MacLoomGui::handleMenu(int id, Common::String &name) {
 		break;
 	}
 
-	if (dialogId != -1) {
-		SimpleWindow *dialog = createDialog(dialogId, substitutions, defaultButton);
-		dialog->runDialog();
-		return true;
-	}
-
 	return false;
 }
 
-void MacLoomGui::showAboutDialog() {
+void MacLoomGui::runAboutDialog() {
 	// The About window is not a a dialog resource. Its size appears to be
 	// hard-coded (416x166), and it's drawn centered. The graphics are in
 	// PICT 5000 and 5001.
@@ -1556,7 +1592,7 @@ void MacLoomGui::showAboutDialog() {
 				fastForward = false;
 				darkenOnly = true;
 				waitFrames = 40;	// ~2 seconds
-				window->drawText(r, page1);
+				window->drawTexts(r, page1);
 				break;
 
 			case 4:
@@ -1570,7 +1606,7 @@ void MacLoomGui::showAboutDialog() {
 				darkenOnly = true;
 				waitFrames = 130;	// ~6.5 seconds
 				window->drawSprite(loom, 95, 38);
-				window->drawText(r, page2);
+				window->drawTexts(r, page2);
 				break;
 
 				growth = -2;
@@ -1580,40 +1616,40 @@ void MacLoomGui::showAboutDialog() {
 				fastForward = false;
 				darkenOnly = true;
 				waitFrames = 80;	// ~4 seconds
-				window->drawText(r, page3);
+				window->drawTexts(r, page3);
 				break;
 
 			case 9:
 				fastForward = false;
 				darkenOnly = true;
 				waitFrames = 80;
-				window->drawText(r, page4);
+				window->drawTexts(r, page4);
 				break;
 
 			case 11:
 				fastForward = false;
 				darkenOnly = true;
 				waitFrames = 80;
-				window->drawText(r, page5);
+				window->drawTexts(r, page5);
 				break;
 
 			case 13:
 				fastForward = false;
 				darkenOnly = true;
 				waitFrames = 80;
-				window->drawText(r, page6);
+				window->drawTexts(r, page6);
 				break;
 
 			case 15:
 				fastForward = false;
 				darkenOnly = true;
 				waitFrames = 260;	// ~13 seconds
-				window->drawText(r, page7);
+				window->drawTexts(r, page7);
 				break;
 
 			case 17:
 				fastForward = false;
-				window->drawText(r, page8);
+				window->drawTexts(r, page8);
 				break;
 			}
 
@@ -2859,7 +2895,6 @@ bool MacIndy3Gui::handleMenu(int id, Common::String &name) {
 		return true;
 
 	int dialogId = -1;
-	int defaultButton = -1;
 	Common::StringArray substitutions;
 
 	switch (id) {
@@ -2877,9 +2912,7 @@ bool MacIndy3Gui::handleMenu(int id, Common::String &name) {
 
 	case 202:
 		// Restart. Standard Ok/Cancel dialog.
-		dialogId = 502;
-		defaultButton = 0;
-		substitutions.push_back("Are you sure you want to restart this game from the beginning?");
+		runRestartDialog();
 		break;
 
 	case 203:
@@ -2888,22 +2921,17 @@ bool MacIndy3Gui::handleMenu(int id, Common::String &name) {
 
 	case 204:
 		// IQ Points
-		dialogId = (_vm->_renderMode == Common::kRenderMacintoshBW) ? 1001 : 1002;
-		substitutions.push_back(Common::String::format("%d", _vm->VAR(244)));
-		substitutions.push_back(Common::String::format("%d", _vm->VAR(245)));
+		runIqPointsDialog();
 		break;
 
 	case 205:
 		// Options
-		dialogId = 1000;
-		substitutions.push_back(Common::String::format("%d", _vm->VAR(_vm->VAR_MACHINE_SPEED)));
+		runOptionsDialog();
 		break;
 
 	case 206:
 		// Quit. Standard Ok/Cancel dialog.
-		dialogId = 502;
-		defaultButton = 0;
-		substitutions.push_back("Are you sure you want to quit?");
+		runQuitDialog();
 		break;
 
 	default:
@@ -2912,7 +2940,7 @@ bool MacIndy3Gui::handleMenu(int id, Common::String &name) {
 	}
 
 	if (dialogId != -1) {
-		SimpleWindow *dialog = createDialog(dialogId, substitutions, defaultButton);
+		SimpleWindow *dialog = createDialog(dialogId);
 		dialog->runDialog();
 		return true;
 	}
@@ -2920,7 +2948,7 @@ bool MacIndy3Gui::handleMenu(int id, Common::String &name) {
 	return false;
 }
 
-void MacIndy3Gui::showAboutDialog() {
+void MacIndy3Gui::runAboutDialog() {
 	// The About window is not a a dialog resource. Its size appears to be
 	// hard-coded (416x166), and it's drawn centered. The graphics are in
 	// PICT 2000.
@@ -3159,6 +3187,17 @@ void MacIndy3Gui::clearAboutDialog(SimpleWindow *window) {
 	window->fillPattern(Common::Rect(2, 136, s->w - 2, s->h - 4), 0xA5A5);
 }
 
+bool MacIndy3Gui::runIqPointsDialog() {
+	SimpleWindow *window = createDialog((_vm->_renderMode == Common::kRenderMacintoshBW) ? 1001 : 1002);
+
+	window->addSubstitution(Common::String::format("%d", _vm->VAR(244)));
+	window->addSubstitution(Common::String::format("%d", _vm->VAR(245)));
+	window->runDialog();
+
+	delete window;
+	return true;
+}
+
 // Before the GUI rewrite, the scroll offset was saved in variable 67. Let's
 // continue that tradition, just in case. If nothing else, it gives us an easy
 // way to still store it in savegames.
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index a3bccc861d2..3bab3fddab6 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -76,7 +76,7 @@ protected:
 		kTransparency = 255
 	};
 
-	Common::String getDialogString(Common::SeekableReadStream *res, int len, Common::StringArray substitutions);
+	Common::String getDialogString(Common::SeekableReadStream *res, int len);
 
 public:
 	enum TextStyle {
@@ -105,10 +105,12 @@ public:
 	protected:
 		MacGui::SimpleWindow *_window;
 		Common::Rect _bounds;
+		Common::String _text;
+		bool _isEnabled = true;
 		bool _isPressed = false;
 
 	public:
-		MacWidget(MacGui::SimpleWindow *window, Common::Rect bounds);
+		MacWidget(MacGui::SimpleWindow *window, Common::Rect bounds, Common::String text);
 		virtual ~MacWidget() {};
 
 		void setPressed(bool pressed) {
@@ -116,9 +118,10 @@ public:
 			draw();
 		}
 
-		bool findWidget(int x, int y) {
-			return _bounds.contains(x, y);
-		}
+		virtual void makeDefaultWidget() {}
+		virtual bool findWidget(int x, int y);
+
+		int drawText(Common::String &text, int x, int y, int w, Color color, Graphics::TextAlign align = Graphics::kTextAlignLeft);
 
 		virtual void draw() = 0;
 		virtual void action() {}
@@ -126,28 +129,34 @@ public:
 
 	class MacButton : public MacWidget {
 	private:
-		Common::String _text;
-		bool _isDefault;
+		bool _isDefault = false;
 		bool _firstDraw = true;
 
 	public:
-		MacButton(MacGui::SimpleWindow *window, Common::Rect bounds, Common::String text, bool isDefault);
+		MacButton(MacGui::SimpleWindow *window, Common::Rect bounds, Common::String text);
+
+		void makeDefautlWidget() { _isDefault = true; }
 
 		void draw();
 	};
 
 	class MacCheckbox : public MacWidget {
 	private:
-		Common::String _text;
-		bool _isChecked;
+		bool _isChecked = false;
 		bool _firstDraw = true;
 
 	public:
-		MacCheckbox(MacGui::SimpleWindow *window, Common::Rect bounds, Common::String text, bool isChecked);
+		MacCheckbox(MacGui::SimpleWindow *window, Common::Rect bounds, Common::String text);
 		void draw();
 		void action();
 	};
 
+	class MacText : public MacWidget {
+	public:
+		MacText(MacGui::SimpleWindow *window, Common::Rect bounds, Common::String text);
+		void draw();
+	};
+
 	class SimpleWindow {
 	private:
 		OSystem *_system;
@@ -162,6 +171,10 @@ public:
 		Graphics::Surface _surface;
 		Graphics::Surface _innerSurface;
 
+		Common::StringArray _substitutions;
+
+		int _defaultWidget = -1;
+
 		Common::Array<MacWidget *> _widgets;
 		Common::Array<Common::Rect> _dirtyRects;
 
@@ -179,10 +192,16 @@ public:
 		void show();
 		void runDialog();
 
+		void setDefaultWidget(int nr) { _defaultWidget = nr; }
 		MacWidget *findWidget(int x, int y);
 
-		void addButton(Common::Rect bounds, Common::String text, bool isDefault);
-		void addCheckbox(Common::Rect bounds, Common::String text, bool isChecked);
+		void addButton(Common::Rect bounds, Common::String text);
+		void addCheckbox(Common::Rect bounds, Common::String text);
+		void addText(Common::Rect bounds, Common::String text);
+
+		void clearSustitutions() { _substitutions.clear(); }
+		void addSubstitution(Common::String text) { _substitutions.push_back(text); }
+		Common::String &getSubstitution(int n) { return _substitutions[n]; }
 
 		void markRectAsDirty(Common::Rect r);
 		void update(bool fullRedraw = false);
@@ -194,7 +213,7 @@ public:
 		void fillPattern(Common::Rect r, uint16 pattern);
 		void drawSprite(const Graphics::Surface *sprite, int x, int y);
 		void drawSprite(const Graphics::Surface *sprite, int x, int y, Common::Rect clipRect);
-		void drawText(Common::Rect r, const TextLine *lines);
+		void drawTexts(Common::Rect r, const TextLine *lines);
 		void drawTextBox(Common::Rect r, const TextLine *lines, int arc = 9);
 	};
 
@@ -236,7 +255,10 @@ public:
 
 	virtual bool handleMenu(int id, Common::String &name);
 
-	virtual void showAboutDialog() = 0;
+	virtual void runAboutDialog() = 0;
+	bool runQuitDialog();
+	bool runRestartDialog();
+	bool runOptionsDialog();
 
 	virtual bool isVerbGuiActive() const { return false; }
 	virtual void reset() {}
@@ -255,7 +277,7 @@ public:
 	int delay(uint32 ms);
 
 	SimpleWindow *createWindow(Common::Rect bounds, SimpleWindowStyle style = kStyleNormal);
-	SimpleWindow *createDialog(int dialogId, Common::StringArray substitutions, int defaultButton);
+	SimpleWindow *createDialog(int dialogId);
 };
 
 class MacLoomGui : public MacGui {
@@ -278,7 +300,7 @@ public:
 
 	bool handleMenu(int id, Common::String &name);
 
-	void showAboutDialog();
+	void runAboutDialog();
 
 	void resetAfterLoad();
 	void update(int delta);
@@ -305,16 +327,15 @@ public:
 	void setupCursor(int &width, int &height, int &hotspotX, int &hotspotY, int &animate);
 
 	Graphics::Surface *textArea() { return &_textArea; }
-	void clearTextArea() {
-		_textArea.fillRect(Common::Rect(_textArea.w, _textArea.h), 0);
-	}
+	void clearTextArea() { _textArea.fillRect(Common::Rect(_textArea.w, _textArea.h), kBlack); }
 	void initTextAreaForActor(Actor *a, byte color);
 	void printCharToTextArea(int chr, int x, int y, int color);
 
 	bool handleMenu(int id, Common::String &name);
 
-	void showAboutDialog();
+	void runAboutDialog();
 	void clearAboutDialog(SimpleWindow *window);
+	bool runIqPointsDialog();
 
 	// There is a distinction between the GUI being allowed and being
 	// active. Allowed means that it's allowed to draw verbs, but not that


Commit: b1af72c69757b42c65af0ece379351135e4c15fc
    https://github.com/scummvm/scummvm/commit/b1af72c69757b42c65af0ece379351135e4c15fc
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Use correct text for Mac GUI quit dialog.

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 8fb1014d952..b7444fe5fad 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -1056,7 +1056,7 @@ bool MacGui::runRestartDialog() {
 	SimpleWindow *window = createDialog(502);
 
 	window->setDefaultWidget(0);
-	window->addSubstitution("Are you sure you want to quit?");
+	window->addSubstitution("Are you sure you want to restart this game from the beginning?");
 	window->runDialog();
 
 	delete window;


Commit: abc2c88c136fee532003813dd996982d7c17cab6
    https://github.com/scummvm/scummvm/commit/abc2c88c136fee532003813dd996982d7c17cab6
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Add word-wrapping to Mac GUI

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index b7444fe5fad..0cdd11e13e0 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -218,26 +218,56 @@ bool MacGui::MacWidget::findWidget(int x, int y) {
 	return _isEnabled && _bounds.contains(x, y);
 }
 
-int MacGui::MacWidget::drawText(Common::String &text, int x, int y, int w, Color color, Graphics::TextAlign align) {
+int MacGui::MacWidget::drawText(Common::String text, int x, int y, int w, Color color, Graphics::TextAlign align) {
 	const Graphics::Font *font = _window->_gui->getFont(kSystemFont);
 
-	Common::String line;
+	for (uint i = 0; i < text.size() - 1; i++) {
+		if (text[i] == '^') {
+			Common::String &subst = _window->getSubstitution(text[i + 1] - '0');
+			text.replace(i, 2, subst);
+
+		}
+	}
+
+	Common::StringArray lines;
+	int start = 0;
+	int maxLineWidth = 0;
 	int lineWidth = 0;
+	int lastSpace = -1;
 
 	for (uint i = 0; i < text.size(); i++) {
-		if (text[i] == '^') {
-			Common::String &subst = _window->getSubstitution(text[++i] - '0');
-			for (uint j = 0; j < subst.size(); j++) {
-				line += subst[j];
-				lineWidth += font->getCharWidth(subst[j]);
-			}
-		} else {
-			line += text[i];
-			lineWidth += font->getCharWidth(text[i]);
+		if (text[i] == ' ')
+			lastSpace = i;
+
+		lineWidth += font->getCharWidth(text[i]);
+
+		if (lineWidth > w) {
+			if (lastSpace > start)
+				i = lastSpace;
+
+			if (lineWidth > maxLineWidth)
+				maxLineWidth = lineWidth;
+
+			lines.push_back(text.substr(start, i - start));
+			lineWidth = 0;
+
+			if (lastSpace > start)
+				start = i + 1;
+			else
+				start = i;
 		}
 	}
 
-	font->drawString(_window->innerSurface(), line, x, y, w, color, align);
+	lines.push_back(text.substr(start));
+
+	if (lineWidth > maxLineWidth)
+		maxLineWidth = lineWidth;
+
+	for (uint i = 0; i < lines.size(); i++) {
+		font->drawString(_window->innerSurface(), lines[i], x, y, w, color, align);
+		y += font->getFontHeight();
+	}
+
 	return lineWidth;
 }
 
@@ -1252,6 +1282,7 @@ MacGui::SimpleWindow *MacGui::createDialog(int dialogId) {
 			case 8:
 				// Static text
 				str = getDialogString(res, len);
+				r.left++;
 				window->addText(r, str);
 				break;
 
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 3bab3fddab6..4d57b8ea665 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -121,7 +121,7 @@ public:
 		virtual void makeDefaultWidget() {}
 		virtual bool findWidget(int x, int y);
 
-		int drawText(Common::String &text, int x, int y, int w, Color color, Graphics::TextAlign align = Graphics::kTextAlignLeft);
+		int drawText(Common::String text, int x, int y, int w, Color color, Graphics::TextAlign align = Graphics::kTextAlignLeft);
 
 		virtual void draw() = 0;
 		virtual void action() {}
@@ -135,7 +135,7 @@ public:
 	public:
 		MacButton(MacGui::SimpleWindow *window, Common::Rect bounds, Common::String text);
 
-		void makeDefautlWidget() { _isDefault = true; }
+		void makeDefaultWidget() { _isDefault = true; }
 
 		void draw();
 	};
@@ -173,8 +173,6 @@ public:
 
 		Common::StringArray _substitutions;
 
-		int _defaultWidget = -1;
-
 		Common::Array<MacWidget *> _widgets;
 		Common::Array<Common::Rect> _dirtyRects;
 
@@ -192,7 +190,7 @@ public:
 		void show();
 		void runDialog();
 
-		void setDefaultWidget(int nr) { _defaultWidget = nr; }
+		void setDefaultWidget(int nr) { _widgets[nr]->makeDefaultWidget(); }
 		MacWidget *findWidget(int x, int y);
 
 		void addButton(Common::Rect bounds, Common::String text);


Commit: 6fa8a9245d45dbaf698c78c59db3824cf14567a7
    https://github.com/scummvm/scummvm/commit/6fa8a9245d45dbaf698c78c59db3824cf14567a7
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Make pictures be widgets in the Mac GUI

Now everything is a widget. They just don't do much yet.

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 0cdd11e13e0..36041a04b1b 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -413,7 +413,24 @@ MacGui::MacText::MacText(MacGui::SimpleWindow *window, Common::Rect bounds, Comm
 }
 
 void MacGui::MacText::draw() {
+	_window->innerSurface()->fillRect(_bounds, kWhite);
 	drawText(_text, _bounds.left, _bounds.top, _bounds.width(), kBlack);
+	_window->markRectAsDirty(_bounds);
+}
+
+MacGui::MacPicture::MacPicture(MacGui::SimpleWindow *window, Common::Rect bounds, int id) : MacWidget(window, bounds, "Picture") {
+	_picture = _window->_gui->loadPict(id);
+}
+
+MacGui::MacPicture::~MacPicture() {
+	if (_picture) {
+		_picture->free();
+		delete _picture;
+	}
+}
+
+void MacGui::MacPicture::draw() {
+	_window->drawSprite(_picture, _bounds.left, _bounds.top);
 }
 
 MacGui::SimpleWindow::SimpleWindow(MacGui *gui, OSystem *system, Graphics::Surface *from, Common::Rect bounds, SimpleWindowStyle style) : _gui(gui), _system(system), _from(from), _bounds(bounds) {
@@ -510,6 +527,10 @@ void MacGui::SimpleWindow::addText(Common::Rect bounds, Common::String text) {
 	_widgets.push_back(new MacText(this, bounds, text));
 }
 
+void MacGui::SimpleWindow::addPicture(Common::Rect bounds, int id) {
+	_widgets.push_back(new MacPicture(this, bounds, id));
+}
+
 void MacGui::SimpleWindow::markRectAsDirty(Common::Rect r) {
 	_dirtyRects.push_back(r);
 }
@@ -1254,7 +1275,6 @@ MacGui::SimpleWindow *MacGui::createDialog(int dialogId) {
 			int len = res->readByte();
 
 			Common::String str;
-			Graphics::Surface *pict;
 
 			switch (type & 0x7F) {
 			case 4:
@@ -1288,12 +1308,7 @@ MacGui::SimpleWindow *MacGui::createDialog(int dialogId) {
 
 			case 64:
 				// Picture
-				pict = loadPict(res->readUint16BE());
-				if (pict) {
-					window->drawSprite(pict, r.left, r.top);
-					pict->free();
-					delete pict;
-				}
+				window->addPicture(r, res->readUint16BE());
 				break;
 
 			default:
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 4d57b8ea665..85256085650 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -157,6 +157,17 @@ public:
 		void draw();
 	};
 
+	class MacPicture : public MacWidget {
+	private:
+		Graphics::Surface *_picture = nullptr;
+
+	public:
+		MacPicture(MacGui::SimpleWindow *window, Common::Rect bounds, int id);
+		~MacPicture();
+
+		void draw();
+	};
+
 	class SimpleWindow {
 	private:
 		OSystem *_system;
@@ -196,6 +207,7 @@ public:
 		void addButton(Common::Rect bounds, Common::String text);
 		void addCheckbox(Common::Rect bounds, Common::String text);
 		void addText(Common::Rect bounds, Common::String text);
+		void addPicture(Common::Rect bounds, int id);
 
 		void clearSustitutions() { _substitutions.clear(); }
 		void addSubstitution(Common::String text) { _substitutions.push_back(text); }


Commit: 961cd2cffbceb9209d33726150f87d00aefff1f1
    https://github.com/scummvm/scummvm/commit/961cd2cffbceb9209d33726150f87d00aefff1f1
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Partially show in the Indy 3 Mac Open/Save dialogs

I don't think a Macintosh file picker makes sense for ScummVM, and for
Loom it presumably uses the built-in one. But it's still good that we
can show something for reference, so that we can work from that later.

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 36041a04b1b..9a34e92f97e 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -219,13 +219,18 @@ bool MacGui::MacWidget::findWidget(int x, int y) {
 }
 
 int MacGui::MacWidget::drawText(Common::String text, int x, int y, int w, Color color, Graphics::TextAlign align) {
+	if (text.empty())
+		return 0;
+
 	const Graphics::Font *font = _window->_gui->getFont(kSystemFont);
 
 	for (uint i = 0; i < text.size() - 1; i++) {
 		if (text[i] == '^') {
-			Common::String &subst = _window->getSubstitution(text[i + 1] - '0');
-			text.replace(i, 2, subst);
-
+			uint nr = text[i + 1] - '0';
+			if (_window->hasSubstitution(nr)) {
+				Common::String &subst = _window->getSubstitution(nr);
+				text.replace(i, 2, subst);
+			}
 		}
 	}
 
@@ -1218,6 +1223,11 @@ int MacGui::delay(uint32 ms) {
 }
 
 MacGui::SimpleWindow *MacGui::createWindow(Common::Rect bounds, SimpleWindowStyle style) {
+	if (bounds.left < 0 || bounds.top < 0 || bounds.right >= 640 || bounds.bottom >= 400) {
+		// This happens with the Last Crusade file dialogs.
+		bounds.moveTo((640 - bounds.width()) / 2, 27);
+	}
+
 	return new SimpleWindow(this, _system, _surface, bounds, style);
 }
 
@@ -1269,6 +1279,7 @@ MacGui::SimpleWindow *MacGui::createDialog(int dialogId) {
 			r.bottom = res->readUint16BE();
 			r.right = res->readUint16BE();
 
+			// Move to appropriate position on inner surface
 			r.translate(2, 2);
 
 			int type = res->readByte();
@@ -1277,6 +1288,12 @@ MacGui::SimpleWindow *MacGui::createDialog(int dialogId) {
 			Common::String str;
 
 			switch (type & 0x7F) {
+			case 0:
+				// User item
+				window->innerSurface()->frameRect(r, kRed);
+				res->skip(len);
+				break;
+
 			case 4:
 				// Button
 				str = getDialogString(res, len);
@@ -1306,6 +1323,12 @@ MacGui::SimpleWindow *MacGui::createDialog(int dialogId) {
 				window->addText(r, str);
 				break;
 
+			case 16:
+				// Editable text
+				window->innerSurface()->frameRect(r, kGreen);
+				res->skip(len);
+				break;
+
 			case 64:
 				// Picture
 				window->addPicture(r, res->readUint16BE());
@@ -1421,17 +1444,17 @@ bool MacLoomGui::handleMenu(int id, Common::String &name) {
 
 	switch (id) {
 	case 200:
-		// Open. Uses standard Macintosh open file dialog
-		debug("MacLoomGui: Open");
+		// Open
+		runOpenDialog();
 		break;
 
 	case 201:
-		// Save. Uses standard Macintosh save file dialog
-		debug("MacLoomGui: Save");
+		// Save
+		runSaveDialog();
 		break;
 
 	case 202:
-		// Restart. Standard Ok/Cancel dialog.
+		// Restart
 		runRestartDialog();
 		break;
 
@@ -1445,7 +1468,7 @@ bool MacLoomGui::handleMenu(int id, Common::String &name) {
 		break;
 
 	case 205:
-		// Quit. Standard Ok/Cancel dialog.
+		// Quit
 		runQuitDialog();
 		break;
 
@@ -1721,6 +1744,20 @@ void MacLoomGui::runAboutDialog() {
 	delete window;
 }
 
+bool MacLoomGui::runOpenDialog() {
+	// Standard file picker dialog. We don't yet have one, and it might
+	// not make sense for ScummVM.
+	warning("MacLoomGui::runOpenDialog()");
+	return true;
+}
+
+bool MacLoomGui::runSaveDialog() {
+	// Standard file picker dialog. We don't yet have one, and it might
+	// not make sense for ScummVM.
+	warning("MacLoomGui::runSaveDialog()");
+	return true;
+}
+
 void MacLoomGui::resetAfterLoad() {
 	reset();
 
@@ -2945,19 +2982,17 @@ bool MacIndy3Gui::handleMenu(int id, Common::String &name) {
 
 	switch (id) {
 	case 200:
-		// Open. Does not work, and might not make sense.
-		// dialogId = (_vm->_renderMode == Common::kRenderMacintoshBW) ? 4000 : 4001;
-		debug("MacIndy3Gui: Open");
+		// Open
+		runOpenDialog();
 		break;
 
 	case 201:
-		// Save. Does not work, and might not make sense.
-		// dialogId = (_vm->_renderMode == Common::kRenderMacintoshBW) ? 4000 : 4001;
-		debug("MacIndy3Gui: Save");
+		// Save
+		runSaveDialog();
 		break;
 
 	case 202:
-		// Restart. Standard Ok/Cancel dialog.
+		// Restart
 		runRestartDialog();
 		break;
 
@@ -2976,7 +3011,7 @@ bool MacIndy3Gui::handleMenu(int id, Common::String &name) {
 		break;
 
 	case 206:
-		// Quit. Standard Ok/Cancel dialog.
+		// Quit
 		runQuitDialog();
 		break;
 
@@ -3233,6 +3268,28 @@ void MacIndy3Gui::clearAboutDialog(SimpleWindow *window) {
 	window->fillPattern(Common::Rect(2, 136, s->w - 2, s->h - 4), 0xA5A5);
 }
 
+bool MacIndy3Gui::runOpenDialog() {
+	SimpleWindow *window = createDialog((_vm->_renderMode == Common::kRenderMacintoshBW) ? 4000 : 4001);
+
+	window->addSubstitution(Common::String::format("%d", _vm->VAR(244)));
+	window->addSubstitution(Common::String::format("%d", _vm->VAR(245)));
+	window->runDialog();
+
+	delete window;
+	return true;
+}
+
+bool MacIndy3Gui::runSaveDialog() {
+	SimpleWindow *window = createDialog((_vm->_renderMode == Common::kRenderMacintoshBW) ? 3998: 3999);
+
+	window->addSubstitution(Common::String::format("%d", _vm->VAR(244)));
+	window->addSubstitution(Common::String::format("%d", _vm->VAR(245)));
+	window->runDialog();
+
+	delete window;
+	return true;
+}
+
 bool MacIndy3Gui::runIqPointsDialog() {
 	SimpleWindow *window = createDialog((_vm->_renderMode == Common::kRenderMacintoshBW) ? 1001 : 1002);
 
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 85256085650..e982e205c6b 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -211,7 +211,9 @@ public:
 
 		void clearSustitutions() { _substitutions.clear(); }
 		void addSubstitution(Common::String text) { _substitutions.push_back(text); }
-		Common::String &getSubstitution(int n) { return _substitutions[n]; }
+
+		bool hasSubstitution(uint n) { return n < _substitutions.size(); }
+		Common::String &getSubstitution(uint n) { return _substitutions[n]; }
 
 		void markRectAsDirty(Common::Rect r);
 		void update(bool fullRedraw = false);
@@ -266,6 +268,8 @@ public:
 	virtual bool handleMenu(int id, Common::String &name);
 
 	virtual void runAboutDialog() = 0;
+	virtual bool runOpenDialog() = 0;
+	virtual bool runSaveDialog() = 0;
 	bool runQuitDialog();
 	bool runRestartDialog();
 	bool runOptionsDialog();
@@ -311,6 +315,8 @@ public:
 	bool handleMenu(int id, Common::String &name);
 
 	void runAboutDialog();
+	bool runOpenDialog();
+	bool runSaveDialog();
 
 	void resetAfterLoad();
 	void update(int delta);
@@ -345,6 +351,8 @@ public:
 
 	void runAboutDialog();
 	void clearAboutDialog(SimpleWindow *window);
+	bool runOpenDialog();
+	bool runSaveDialog();
 	bool runIqPointsDialog();
 
 	// There is a distinction between the GUI being allowed and being


Commit: f83b5b80a5adff1e92d8f7fa7a321f5fa01d456f
    https://github.com/scummvm/scummvm/commit/f83b5b80a5adff1e92d8f7fa7a321f5fa01d456f
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Some cleanups and fixes to the Mac GUI

Text should be positioned correctly in buttons (though the rounded
corners are still hard-coded for one particular size), and I've tried
documenting what the widgets in the different dialogs are. They don't
have internal IDs, as far as I can tell, so we can only identify them by
their index in the _widgets array.

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 9a34e92f97e..53375fa1d46 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -311,6 +311,8 @@ void MacGui::MacButton::draw() {
 
 	// ScummVM's rounded rectangles aren't a complete match for QuickDraw's
 	// rounded rectangles, so we draw the corners manually.
+	//
+	// Unfortunately, these hard-coded ones only work for most buttons.
 
 	int innerCorner[][2] = {
 		{ 1, 2 },
@@ -332,7 +334,9 @@ void MacGui::MacButton::draw() {
 		s->hLine(x2, y1, x3, kBlack);
 	}
 
-	drawText(_text, _bounds.left, _bounds.top + 2, _bounds.width(), fg, Graphics::kTextAlignCenter);
+	const Graphics::Font *font = _window->_gui->getFont(kSystemFont);
+
+	drawText(_text, _bounds.left, _bounds.top + (_bounds.height() - font->getFontHeight()) / 2, _bounds.width(), fg, Graphics::kTextAlignCenter);
 
 	if (_isDefault && _firstDraw) {
 		for (int i = 0; i < 3; i++) {
@@ -817,7 +821,10 @@ void MacGui::initialize() {
 			Common::String name = menu->getName(subItem);
 
 			if (!name.empty())
+{
+debug("%d: %s", id, name.c_str());
 				menu->setAction(subItem, id++);
+}
 		}
 	}
 
@@ -1087,17 +1094,43 @@ bool MacGui::handleMenu(int id, Common::String &name) {
 	_windowManager->getMenu()->closeMenu();
 
 	switch (id) {
-	case 100:
+	case 100:	// About
 		runAboutDialog();
 		return true;
 
-	// If we ever need to handle the Edit menu, do it here.
+	case 200:	// Open
+		runOpenDialog();
+		return true;
+
+	case 201:	// Save
+		runSaveDialog();
+		return true;
+
+	case 202:	// Restart
+		runRestartDialog();
+		return true;
+
+	case 203:	// Pause
+		return true;
+
+	case 300:	// Undo
+	case 301:	// Cut
+	case 302:	// Copy
+	case 303:	// Paste
+	case 304:	// Clear
+		return true;
 	}
 
 	return false;
 }
 
 bool MacGui::runQuitDialog() {
+	// Widgets:
+	//
+	// 0 - Okay button
+	// 1 - Cancel button
+	// 2 - "^0" text
+
 	SimpleWindow *window = createDialog(502);
 
 	window->setDefaultWidget(0);
@@ -1109,6 +1142,12 @@ bool MacGui::runQuitDialog() {
 }
 
 bool MacGui::runRestartDialog() {
+	// Widgets:
+	//
+	// 0 - Okay button
+	// 1 - Cancel button
+	// 2 - "^0" text
+
 	SimpleWindow *window = createDialog(502);
 
 	window->setDefaultWidget(0);
@@ -1119,16 +1158,6 @@ bool MacGui::runRestartDialog() {
 	return true;
 }
 
-bool MacGui::runOptionsDialog() {
-	SimpleWindow *window = createDialog(1000);
-
-	window->addSubstitution(Common::String::format("%d", _vm->VAR(_vm->VAR_MACHINE_SPEED)));
-	window->runDialog();
-
-	delete window;
-	return true;
-}
-
 Common::String MacGui::getDialogString(Common::SeekableReadStream *res, int len) {
 	Common::String str;
 
@@ -1290,6 +1319,7 @@ MacGui::SimpleWindow *MacGui::createDialog(int dialogId) {
 			switch (type & 0x7F) {
 			case 0:
 				// User item
+debug("User item: %d %d %d %d", r.left, r.top, r.right, r.bottom);
 				window->innerSurface()->frameRect(r, kRed);
 				res->skip(len);
 				break;
@@ -1297,6 +1327,7 @@ MacGui::SimpleWindow *MacGui::createDialog(int dialogId) {
 			case 4:
 				// Button
 				str = getDialogString(res, len);
+debug("Button: %s (%d %d %d %d)", str.c_str(), r.left, r.top, r.right, r.bottom);
 				window->addButton(r, str);
 				button++;
 				break;
@@ -1304,6 +1335,7 @@ MacGui::SimpleWindow *MacGui::createDialog(int dialogId) {
 			case 5:
 				// Checkbox
 				str = getDialogString(res, len);
+debug("Checkbox: %s", str.c_str());
 
 				// The DITL may define a larger than necessary
 				// area for the checkbox, so normalize the
@@ -1319,18 +1351,21 @@ MacGui::SimpleWindow *MacGui::createDialog(int dialogId) {
 			case 8:
 				// Static text
 				str = getDialogString(res, len);
+debug("Text: %s", str.c_str());
 				r.left++;
 				window->addText(r, str);
 				break;
 
 			case 16:
 				// Editable text
+debug("Editable");
 				window->innerSurface()->frameRect(r, kGreen);
 				res->skip(len);
 				break;
 
 			case 64:
 				// Picture
+debug("Picture");
 				window->addPicture(r, res->readUint16BE());
 				break;
 
@@ -1443,32 +1478,11 @@ bool MacLoomGui::handleMenu(int id, Common::String &name) {
 		return true;
 
 	switch (id) {
-	case 200:
-		// Open
-		runOpenDialog();
-		break;
-
-	case 201:
-		// Save
-		runSaveDialog();
-		break;
-
-	case 202:
-		// Restart
-		runRestartDialog();
-		break;
-
-	case 203:
-		// Pause game
-		break;
-
-	case 204:
-		// Options
+	case 204:	// Options
 		runOptionsDialog();
 		break;
 
-	case 205:
-		// Quit
+	case 205:	// Quit
 		runQuitDialog();
 		break;
 
@@ -1758,6 +1772,34 @@ bool MacLoomGui::runSaveDialog() {
 	return true;
 }
 
+bool MacLoomGui::runOptionsDialog() {
+	// Widgets:
+	//
+	// 0 - Okay button
+	// 1 - Cancel button
+	// 2 - Sound checkbox
+	// 3 - Music checkbox
+	// 4 - Picture
+	// 5 - Picture
+	// 6 - Scrolling checkbox
+	// 7 - Full Animation checkbox
+	// 8 - Picture
+	// 9 - Picture
+	// 10 - "Machine Speed:  ^0" text
+
+	SimpleWindow *window = createDialog(1000);
+
+	// TODO: I don't know where it gets the "Machine Speed" from. It doesn't
+	// appear to be VAR_MACHINE_SPEED, because I think that's only set to 1
+	// or 0, and may be the "Full Animation" setting.
+
+	window->addSubstitution(Common::String::format("%d", _vm->VAR(_vm->VAR_MACHINE_SPEED)));
+	window->runDialog();
+
+	delete window;
+	return true;
+}
+
 void MacLoomGui::resetAfterLoad() {
 	reset();
 
@@ -2981,37 +3023,15 @@ bool MacIndy3Gui::handleMenu(int id, Common::String &name) {
 	Common::StringArray substitutions;
 
 	switch (id) {
-	case 200:
-		// Open
-		runOpenDialog();
-		break;
-
-	case 201:
-		// Save
-		runSaveDialog();
-		break;
-
-	case 202:
-		// Restart
-		runRestartDialog();
-		break;
-
-	case 203:
-		// Pause game
-		break;
-
-	case 204:
-		// IQ Points
+	case 204:	// IQ Points
 		runIqPointsDialog();
 		break;
 
-	case 205:
-		// Options
+	case 205:	// Options
 		runOptionsDialog();
 		break;
 
-	case 206:
-		// Quit
+	case 206:	// Quit
 		runQuitDialog();
 		break;
 
@@ -3269,8 +3289,26 @@ void MacIndy3Gui::clearAboutDialog(SimpleWindow *window) {
 }
 
 bool MacIndy3Gui::runOpenDialog() {
+	// Widgets:
+	//
+	// 0 - Open button
+	// 1 - Weird button outside the dialog (folder dropdown?)
+	// 2 - Cancel button
+	// 3 - User item (disk label?)
+	// 4 - Eject button
+	// 5 - Drive button
+	// 6 - User item (file list?)
+	// 7 - User item (scrollbar?)
+	// 8 - User item (line between Desktop and Open buttons?)
+	// 9 - Empty text
+	// 10 - "IQ" picture
+	// 11 - "Episode: ^0" text
+	// 12 - "Series: ^1" text
+	// 13 - "(Indy Quotient)" text
+
 	SimpleWindow *window = createDialog((_vm->_renderMode == Common::kRenderMacintoshBW) ? 4000 : 4001);
 
+	window->setDefaultWidget(0);
 	window->addSubstitution(Common::String::format("%d", _vm->VAR(244)));
 	window->addSubstitution(Common::String::format("%d", _vm->VAR(245)));
 	window->runDialog();
@@ -3280,8 +3318,24 @@ bool MacIndy3Gui::runOpenDialog() {
 }
 
 bool MacIndy3Gui::runSaveDialog() {
+	// Widgets:
+	//
+	// 1 - Save button
+	// 2 - Cancel button
+	// 3 - "Save as:" text
+	// 4 - User item (disk label?)
+	// 5 - Eject button
+	// 6 - Drive button
+	// 7 - Editable text (save file name)
+	// 8 - User item (file list?)
+	// 9 - "IQ" picture
+	// 10 - "Episode: ^0" text
+	// 11 - "Series: ^1" text
+	// 12 - "(Indy Quotient)" text
+
 	SimpleWindow *window = createDialog((_vm->_renderMode == Common::kRenderMacintoshBW) ? 3998: 3999);
 
+	window->setDefaultWidget(0);
 	window->addSubstitution(Common::String::format("%d", _vm->VAR(244)));
 	window->addSubstitution(Common::String::format("%d", _vm->VAR(245)));
 	window->runDialog();
@@ -3290,7 +3344,38 @@ bool MacIndy3Gui::runSaveDialog() {
 	return true;
 }
 
+bool MacIndy3Gui::runOptionsDialog() {
+	// Widgets:
+	//
+	// 0 - Okay button
+	// 1 - Cancel button
+	// 2 - Sound checkbox
+	// 3 - Music checkbox
+	// 4 - Picture
+	// 5 - Picture
+	// 6 - "Machine speed rating:" text
+	// 7 - "^0" text
+	// 8 - Scrolling checkbox
+
+	SimpleWindow *window = createDialog(1000);
+
+	window->addSubstitution(Common::String::format("%d", _vm->VAR(_vm->VAR_MACHINE_SPEED)));
+	window->runDialog();
+
+	delete window;
+	return true;
+}
+
 bool MacIndy3Gui::runIqPointsDialog() {
+	// Widgets
+	//
+	// 0 - Done button
+	// 1 - Reset Series IQ button
+	// 2 - "(Indy Quotient)" text
+	// 3 - "Episode: ^0" text
+	// 4 - "Series: ^1" text
+	// 5 - "IQ" picture
+
 	SimpleWindow *window = createDialog((_vm->_renderMode == Common::kRenderMacintoshBW) ? 1001 : 1002);
 
 	window->addSubstitution(Common::String::format("%d", _vm->VAR(244)));
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index e982e205c6b..583067c9c01 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -270,9 +270,10 @@ public:
 	virtual void runAboutDialog() = 0;
 	virtual bool runOpenDialog() = 0;
 	virtual bool runSaveDialog() = 0;
+	virtual bool runOptionsDialog() = 0;
+
 	bool runQuitDialog();
 	bool runRestartDialog();
-	bool runOptionsDialog();
 
 	virtual bool isVerbGuiActive() const { return false; }
 	virtual void reset() {}
@@ -317,6 +318,7 @@ public:
 	void runAboutDialog();
 	bool runOpenDialog();
 	bool runSaveDialog();
+	bool runOptionsDialog();
 
 	void resetAfterLoad();
 	void update(int delta);
@@ -353,6 +355,7 @@ public:
 	void clearAboutDialog(SimpleWindow *window);
 	bool runOpenDialog();
 	bool runSaveDialog();
+	bool runOptionsDialog();
 	bool runIqPointsDialog();
 
 	// There is a distinction between the GUI being allowed and being


Commit: e90e08d9c67aafdecc38602fbd34aaf2f5259a82
    https://github.com/scummvm/scummvm/commit/e90e08d9c67aafdecc38602fbd34aaf2f5259a82
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Add most of the interactions for the Mac GUI widgets

The slider widgets are not yet implemented, and there is currently no
way to disable checkboxes.

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 53375fa1d46..1635b21f8bb 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -515,13 +515,13 @@ void MacGui::SimpleWindow::show() {
 	_dirtyRects.clear();
 }
 
-MacGui::MacWidget *MacGui::SimpleWindow::findWidget(int x, int y) {
+int MacGui::SimpleWindow::findWidget(int x, int y) {
 	for (uint i = 0; i < _widgets.size(); i++) {
 		if (_widgets[i]->findWidget(x, y))
-			return _widgets[i];
+			return i;
 	}
 
-	return nullptr;
+	return -1;
 }
 
 void MacGui::SimpleWindow::addButton(Common::Rect bounds, Common::String text) {
@@ -577,16 +577,19 @@ void MacGui::SimpleWindow::drawSprite(const Graphics::Surface *sprite, int x, in
 	markRectAsDirty(Common::Rect(x, y, x + sprite->w, y + sprite->h));
 }
 
-void MacGui::SimpleWindow::runDialog() {
+int MacGui::SimpleWindow::runDialog() {
 	for (uint i = 0; i < _widgets.size(); i++)
 		_widgets[i]->draw();
 
 	show();
 
-	bool done = false;
-	MacGui::MacWidget *pressedWidget = nullptr;
+	int pressedWidget = -1;
 
-	while (!done && !_gui->_vm->shouldQuit()) {
+	// Run the dialog until something happens to a widget. It's up to the
+	// caller to repeat the calls to runDialog() until the dialog is
+	// finished.
+
+	while (!_gui->_vm->shouldQuit()) {
 		Common::Event event;
 
 		while (_system->getEventManager()->pollEvent(event)) {
@@ -598,23 +601,23 @@ void MacGui::SimpleWindow::runDialog() {
 			switch (event.type) {
 			case Common::EVENT_LBUTTONDOWN:
 				pressedWidget = findWidget(event.mouse.x, event.mouse.y);
-				if (pressedWidget)
-					pressedWidget->setPressed(true);
+				if (pressedWidget != -1)
+					_widgets[pressedWidget]->setPressed(true);
 				break;
 
 			case Common::EVENT_LBUTTONUP:
-				if (pressedWidget) {
-					pressedWidget->setPressed(false);
-					pressedWidget->action();
-					pressedWidget = nullptr;
+				if (pressedWidget != -1) {
+					_widgets[pressedWidget]->setPressed(false);
+					_widgets[pressedWidget]->action();
+					return pressedWidget;
 				}
 				break;
 
 			case Common::EVENT_MOUSEMOVE:
-				if (pressedWidget) {
-					if (!pressedWidget->findWidget(event.mouse.x, event.mouse.y)) {
-						pressedWidget->setPressed(false);
-						pressedWidget = nullptr;
+				if (pressedWidget != -1) {
+					if (!_widgets[pressedWidget]->findWidget(event.mouse.x, event.mouse.y)) {
+						_widgets[pressedWidget]->setPressed(false);
+						pressedWidget = -1;
 					}
 				}
 				break;
@@ -628,6 +631,8 @@ void MacGui::SimpleWindow::runDialog() {
 		update();
 		_system->updateScreen();
 	}
+
+	return -1;
 }
 
 void MacGui::SimpleWindow::drawSprite(const Graphics::Surface *sprite, int x, int y, Common::Rect(clipRect)) {
@@ -821,10 +826,8 @@ void MacGui::initialize() {
 			Common::String name = menu->getName(subItem);
 
 			if (!name.empty())
-{
-debug("%d: %s", id, name.c_str());
 				menu->setAction(subItem, id++);
-}
+
 		}
 	}
 
@@ -1099,15 +1102,18 @@ bool MacGui::handleMenu(int id, Common::String &name) {
 		return true;
 
 	case 200:	// Open
-		runOpenDialog();
+		if (runOpenDialog())
+			debug("Open a saved game");
 		return true;
 
 	case 201:	// Save
-		runSaveDialog();
+		if (runSaveDialog())
+			debug("Save a game");
 		return true;
 
 	case 202:	// Restart
-		runRestartDialog();
+		if (runRestartDialog())
+			debug("Game should restart now");
 		return true;
 
 	case 203:	// Pause
@@ -1135,10 +1141,24 @@ bool MacGui::runQuitDialog() {
 
 	window->setDefaultWidget(0);
 	window->addSubstitution("Are you sure you want to quit?");
-	window->runDialog();
+
+	// When quitting, the default action is to quit
+	bool ret = true;
+
+	while (!_vm->shouldQuit()) {
+		int clicked = window->runDialog();
+
+		if (clicked == 0)
+			break;
+
+		if (clicked == 1) {
+			ret = false;
+			break;
+		}
+	}
 
 	delete window;
-	return true;
+	return ret;
 }
 
 bool MacGui::runRestartDialog() {
@@ -1152,10 +1172,24 @@ bool MacGui::runRestartDialog() {
 
 	window->setDefaultWidget(0);
 	window->addSubstitution("Are you sure you want to restart this game from the beginning?");
-	window->runDialog();
+
+	// When quitting, the default action is to not restart
+	bool ret = false;
+
+	while (!_vm->shouldQuit()) {
+		int clicked = window->runDialog();
+
+		if (clicked == 0) {
+			ret = true;
+			break;
+		}
+
+		if (clicked == 1)
+			break;
+	}
 
 	delete window;
-	return true;
+	return ret;
 }
 
 Common::String MacGui::getDialogString(Common::SeekableReadStream *res, int len) {
@@ -1319,7 +1353,6 @@ MacGui::SimpleWindow *MacGui::createDialog(int dialogId) {
 			switch (type & 0x7F) {
 			case 0:
 				// User item
-debug("User item: %d %d %d %d", r.left, r.top, r.right, r.bottom);
 				window->innerSurface()->frameRect(r, kRed);
 				res->skip(len);
 				break;
@@ -1327,7 +1360,6 @@ debug("User item: %d %d %d %d", r.left, r.top, r.right, r.bottom);
 			case 4:
 				// Button
 				str = getDialogString(res, len);
-debug("Button: %s (%d %d %d %d)", str.c_str(), r.left, r.top, r.right, r.bottom);
 				window->addButton(r, str);
 				button++;
 				break;
@@ -1335,7 +1367,6 @@ debug("Button: %s (%d %d %d %d)", str.c_str(), r.left, r.top, r.right, r.bottom)
 			case 5:
 				// Checkbox
 				str = getDialogString(res, len);
-debug("Checkbox: %s", str.c_str());
 
 				// The DITL may define a larger than necessary
 				// area for the checkbox, so normalize the
@@ -1351,21 +1382,18 @@ debug("Checkbox: %s", str.c_str());
 			case 8:
 				// Static text
 				str = getDialogString(res, len);
-debug("Text: %s", str.c_str());
 				r.left++;
 				window->addText(r, str);
 				break;
 
 			case 16:
 				// Editable text
-debug("Editable");
 				window->innerSurface()->frameRect(r, kGreen);
 				res->skip(len);
 				break;
 
 			case 64:
 				// Picture
-debug("Picture");
 				window->addPicture(r, res->readUint16BE());
 				break;
 
@@ -1479,11 +1507,13 @@ bool MacLoomGui::handleMenu(int id, Common::String &name) {
 
 	switch (id) {
 	case 204:	// Options
-		runOptionsDialog();
+		if (runOptionsDialog())
+			debug("Options should be applied now");
 		break;
 
 	case 205:	// Quit
-		runQuitDialog();
+		if (runQuitDialog())
+			debug("Game should quit now");
 		break;
 
 	default:
@@ -1794,10 +1824,28 @@ bool MacLoomGui::runOptionsDialog() {
 	// or 0, and may be the "Full Animation" setting.
 
 	window->addSubstitution(Common::String::format("%d", _vm->VAR(_vm->VAR_MACHINE_SPEED)));
-	window->runDialog();
+
+	// When quitting, the default action is not to not apply options
+	bool ret = false;
+
+	while (!_vm->shouldQuit()) {
+		int clicked = window->runDialog();
+
+		if (clicked == 0) {
+			ret = true;
+			break;
+		}
+
+		if (clicked == 1)
+			break;
+
+		if (clicked == 2) {
+			debug("TODO: Unchecking sound should disable music");
+		}
+	}
 
 	delete window;
-	return true;
+	return ret;
 }
 
 void MacLoomGui::resetAfterLoad() {
@@ -3028,11 +3076,13 @@ bool MacIndy3Gui::handleMenu(int id, Common::String &name) {
 		break;
 
 	case 205:	// Options
-		runOptionsDialog();
+		if (runOptionsDialog())
+			debug("Options should be applied now");
 		break;
 
 	case 206:	// Quit
-		runQuitDialog();
+		if (runQuitDialog())
+			debug("Game should quit now");
 		break;
 
 	default:
@@ -3311,37 +3361,65 @@ bool MacIndy3Gui::runOpenDialog() {
 	window->setDefaultWidget(0);
 	window->addSubstitution(Common::String::format("%d", _vm->VAR(244)));
 	window->addSubstitution(Common::String::format("%d", _vm->VAR(245)));
-	window->runDialog();
+
+	// When quitting, the default action is not to open a saved game
+	bool ret = false;
+
+	while (!_vm->shouldQuit()) {
+		int clicked = window->runDialog();
+
+		if (clicked == 0) {
+			ret = true;
+			break;
+		}
+
+		if (clicked == 2)
+			break;
+	}
 
 	delete window;
-	return true;
+	return ret;
 }
 
 bool MacIndy3Gui::runSaveDialog() {
 	// Widgets:
 	//
-	// 1 - Save button
-	// 2 - Cancel button
-	// 3 - "Save as:" text
-	// 4 - User item (disk label?)
-	// 5 - Eject button
-	// 6 - Drive button
-	// 7 - Editable text (save file name)
-	// 8 - User item (file list?)
-	// 9 - "IQ" picture
-	// 10 - "Episode: ^0" text
-	// 11 - "Series: ^1" text
-	// 12 - "(Indy Quotient)" text
+	// 0 - Save button
+	// 1 - Cancel button
+	// 2 - "Save as:" text
+	// 3 - User item (disk label?)
+	// 4 - Eject button
+	// 5 - Drive button
+	// 6 - Editable text (save file name)
+	// 7 - User item (file list?)
+	// 8 - "IQ" picture
+	// 9 - "Episode: ^0" text
+	// 10 - "Series: ^1" text
+	// 11 - "(Indy Quotient)" text
 
 	SimpleWindow *window = createDialog((_vm->_renderMode == Common::kRenderMacintoshBW) ? 3998: 3999);
 
 	window->setDefaultWidget(0);
 	window->addSubstitution(Common::String::format("%d", _vm->VAR(244)));
 	window->addSubstitution(Common::String::format("%d", _vm->VAR(245)));
-	window->runDialog();
+
+	// When quitting, the default action is not to save a game
+	bool ret = false;
+
+	while (!_vm->shouldQuit()) {
+		int clicked = window->runDialog();
+
+		if (clicked == 0) {
+			ret = true;
+			break;
+		}
+
+		if (clicked == 1)
+			break;
+	}
 
 	delete window;
-	return true;
+	return ret;
 }
 
 bool MacIndy3Gui::runOptionsDialog() {
@@ -3360,10 +3438,28 @@ bool MacIndy3Gui::runOptionsDialog() {
 	SimpleWindow *window = createDialog(1000);
 
 	window->addSubstitution(Common::String::format("%d", _vm->VAR(_vm->VAR_MACHINE_SPEED)));
-	window->runDialog();
+
+	// When quitting, the default action is not to not apply options
+	bool ret = false;
+
+	while (!_vm->shouldQuit()) {
+		int clicked = window->runDialog();
+
+		if (clicked == 0) {
+			ret = true;
+			break;
+		}
+
+		if (clicked == 1)
+			break;
+
+		if (clicked == 2) {
+			debug("TODO: Unchecking sound should disable music");
+		}
+	}
 
 	delete window;
-	return true;
+	return ret;
 }
 
 bool MacIndy3Gui::runIqPointsDialog() {
@@ -3380,7 +3476,21 @@ bool MacIndy3Gui::runIqPointsDialog() {
 
 	window->addSubstitution(Common::String::format("%d", _vm->VAR(244)));
 	window->addSubstitution(Common::String::format("%d", _vm->VAR(245)));
-	window->runDialog();
+
+	while (!_vm->shouldQuit()) {
+		int clicked = window->runDialog();
+
+		if (clicked == 0)
+			break;
+
+		if (clicked == 1) {
+			window->replaceSubstitution(1, Common::String::format("%d", 0));
+
+			window->redrawWidget(4);
+
+			debug("TODO: Actually clear the series IQ score");
+		}
+	}
 
 	delete window;
 	return true;
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 583067c9c01..15f43bc8a90 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -199,18 +199,19 @@ public:
 		Graphics::Surface *innerSurface() { return &_innerSurface; }
 
 		void show();
-		void runDialog();
+		int runDialog();
 
 		void setDefaultWidget(int nr) { _widgets[nr]->makeDefaultWidget(); }
-		MacWidget *findWidget(int x, int y);
+		int findWidget(int x, int y);
+		void redrawWidget(int nr) { _widgets[nr]->draw(); }
 
 		void addButton(Common::Rect bounds, Common::String text);
 		void addCheckbox(Common::Rect bounds, Common::String text);
 		void addText(Common::Rect bounds, Common::String text);
 		void addPicture(Common::Rect bounds, int id);
 
-		void clearSustitutions() { _substitutions.clear(); }
 		void addSubstitution(Common::String text) { _substitutions.push_back(text); }
+		void replaceSubstitution(int nr, Common::String text) { _substitutions[nr] = text; }
 
 		bool hasSubstitution(uint n) { return n < _substitutions.size(); }
 		Common::String &getSubstitution(uint n) { return _substitutions[n]; }


Commit: 71e1ffcb3313e36c5fb46fab6fc0237ad312dac9
    https://github.com/scummvm/scummvm/commit/71e1ffcb3313e36c5fb46fab6fc0237ad312dac9
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Implement disabled text, and fleshed out options dialogs

Unchecking sound disables music, like in the original.

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 1635b21f8bb..da6fad5bc58 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -211,11 +211,8 @@ Common::KeyState ScummEngine::mac_showOldStyleBannerAndPause(const char *msg, in
 // Very simple window class and widgets. It is perhaps unfortunate that we have
 // two different sets of widget classes, but they behave quite differently.
 
-MacGui::MacWidget::MacWidget(SimpleWindow *window, Common::Rect bounds, Common::String text) : _window(window), _bounds(bounds), _text(text) {
-}
-
 bool MacGui::MacWidget::findWidget(int x, int y) {
-	return _isEnabled && _bounds.contains(x, y);
+	return _enabled && _bounds.contains(x, y);
 }
 
 int MacGui::MacWidget::drawText(Common::String text, int x, int y, int w, Color color, Graphics::TextAlign align) {
@@ -268,18 +265,33 @@ int MacGui::MacWidget::drawText(Common::String text, int x, int y, int w, Color
 	if (lineWidth > maxLineWidth)
 		maxLineWidth = lineWidth;
 
+	int y0 = y;
+
 	for (uint i = 0; i < lines.size(); i++) {
-		font->drawString(_window->innerSurface(), lines[i], x, y, w, color, align);
-		y += font->getFontHeight();
+		font->drawString(_window->innerSurface(), lines[i], x, y0, w, color, align);
+
+		// Implementing drawing of disabled text is too much of a
+		// hassle. Simply draw a white checkerboard pattern on top
+		// of it.
+
+		if (!_enabled) {
+			Common::Rect textBox = font->getBoundingBox(lines[i], x, y0, w, align);
+
+			for (int y1 = textBox.top; y1 < textBox.bottom; y1++) {
+				for (int x1 = textBox.left; x1 < textBox.right; x1++) {
+					if (((x1 + y1) % 2) == 0)
+						_window->innerSurface()->setPixel(x1, y1, kWhite);
+				}
+			}
+		}
+
+		y0 += font->getFontHeight();
 	}
 
 	return lineWidth;
 }
 
-MacGui::MacButton::MacButton(SimpleWindow *window, Common::Rect bounds, Common::String text) : MacWidget(window, bounds, text) {
-}
-
-void MacGui::MacButton::draw() {
+void MacGui::MacButton::draw(bool fullRedraw) {
 	Graphics::Surface *s = _window->innerSurface();
 	Color fg, bg;
 	int x0, x1, x2, x3;
@@ -296,7 +308,7 @@ void MacGui::MacButton::draw() {
 	s->vLine(_bounds.left, y0, y1, kBlack);
 	s->vLine(_bounds.right - 1, y0, y1, kBlack);
 
-	if (_isPressed) {
+	if (_pressed) {
 		fg = kWhite;
 		bg = kBlack;
 	} else {
@@ -338,7 +350,7 @@ void MacGui::MacButton::draw() {
 
 	drawText(_text, _bounds.left, _bounds.top + (_bounds.height() - font->getFontHeight()) / 2, _bounds.width(), fg, Graphics::kTextAlignCenter);
 
-	if (_isDefault && _firstDraw) {
+	if (_isDefault && fullRedraw) {
 		for (int i = 0; i < 3; i++) {
 			x0 = _bounds.left + 1;
 			x1 = _bounds.right - 2;
@@ -374,8 +386,6 @@ void MacGui::MacButton::draw() {
 			s->hLine(x0, y1, x1, kBlack);
 			s->hLine(x2, y1, x3, kBlack);
 		}
-
-		_firstDraw = false;
 	}
 
 	// The first time, the whole window is redrawn so we don't have to
@@ -384,50 +394,56 @@ void MacGui::MacButton::draw() {
 	_window->markRectAsDirty(_bounds);
 }
 
-MacGui::MacCheckbox::MacCheckbox(MacGui::SimpleWindow *window, Common::Rect bounds, Common::String text) : MacWidget(window, bounds, text) {
+MacGui::MacCheckbox::MacCheckbox(MacGui::SimpleWindow *window, Common::Rect bounds, Common::String text, bool enabled) : MacWidget(window, bounds, text, enabled) {
+	// The DITL may define a larger than necessary area for the checkbox,
+	// so we need to calculate the hit bounds.
+
+	const Graphics::Font *font = _window->_gui->getFont(kSystemFont);
+
+	_hitBounds.left = _bounds.left;
+	_hitBounds.top = _bounds.bottom - _bounds.height() / 2 - 8;
+	_hitBounds.bottom = _bounds.top + 18;
+	_hitBounds.right = _bounds.left + 18 + font->getStringWidth(_text) + 2;
 }
 
-void MacGui::MacCheckbox::draw() {
+void MacGui::MacCheckbox::draw(bool fullRedraw) {
 	Graphics::Surface *s = _window->innerSurface();
-	Common::Rect box(_bounds.left + 2, _bounds.top + 2, _bounds.left + 14, _bounds.top + 14);
+	Common::Rect box(_hitBounds.left + 2, _hitBounds.top + 2, _hitBounds.left + 14, _hitBounds.top + 14);
+
+	if (fullRedraw) {
+		_window->innerSurface()->fillRect(_bounds, kWhite);
+
+		int x = _hitBounds.left + 18;
+		int y = _hitBounds.top;
+
+		drawText(_text, x, y, _hitBounds.right - x, kBlack);
+		_window->markRectAsDirty(_bounds);
+	} else {
+		_window->markRectAsDirty(box);
+	}
 
 	s->fillRect(box, kBlack);
-	box.grow(_isPressed ? -2 : -1);
+	box.grow(_pressed ? -2 : -1);
 	s->fillRect(box, kWhite);
 
-	if (_isChecked) {
+	if (_value && _enabled) {
 		s->drawLine(box.left, box.top, box.right - 1, box.bottom - 1, kBlack);
 		s->drawLine(box.left, box.bottom - 1, box.right - 1, box.top, kBlack);
 	}
-
-	if (_firstDraw) {
-		int x = _bounds.left + 18;
-		int y = _bounds.top;
-
-		int stringWidth = drawText(_text, x, y, _bounds.right - x, kBlack);
-		_bounds.right = x + stringWidth + 1;
-		_firstDraw = false;
-	}
-
-	_window->markRectAsDirty(_bounds);
 }
 
 void MacGui::MacCheckbox::action() {
-	_isChecked = !_isChecked;
+	_value = _value ? 0 : 1;
 	draw();
 }
 
-MacGui::MacText::MacText(MacGui::SimpleWindow *window, Common::Rect bounds, Common::String text) : MacWidget(window, bounds, text) {
-	_isEnabled = false;
-}
-
-void MacGui::MacText::draw() {
+void MacGui::MacText::draw(bool fullRedraw) {
 	_window->innerSurface()->fillRect(_bounds, kWhite);
 	drawText(_text, _bounds.left, _bounds.top, _bounds.width(), kBlack);
 	_window->markRectAsDirty(_bounds);
 }
 
-MacGui::MacPicture::MacPicture(MacGui::SimpleWindow *window, Common::Rect bounds, int id) : MacWidget(window, bounds, "Picture") {
+MacGui::MacPicture::MacPicture(MacGui::SimpleWindow *window, Common::Rect bounds, int id, bool enabled) : MacWidget(window, bounds, "Picture", enabled) {
 	_picture = _window->_gui->loadPict(id);
 }
 
@@ -438,7 +454,7 @@ MacGui::MacPicture::~MacPicture() {
 	}
 }
 
-void MacGui::MacPicture::draw() {
+void MacGui::MacPicture::draw(bool fullRedraw) {
 	_window->drawSprite(_picture, _bounds.left, _bounds.top);
 }
 
@@ -511,6 +527,7 @@ void MacGui::SimpleWindow::copyToScreen(Graphics::Surface *s) const {
 }
 
 void MacGui::SimpleWindow::show() {
+	_visible = true;
 	copyToScreen();
 	_dirtyRects.clear();
 }
@@ -524,20 +541,20 @@ int MacGui::SimpleWindow::findWidget(int x, int y) {
 	return -1;
 }
 
-void MacGui::SimpleWindow::addButton(Common::Rect bounds, Common::String text) {
-	_widgets.push_back(new MacButton(this, bounds, text));
+void MacGui::SimpleWindow::addButton(Common::Rect bounds, Common::String text, bool enabled) {
+	_widgets.push_back(new MacButton(this, bounds, text, enabled));
 }
 
-void MacGui::SimpleWindow::addCheckbox(Common::Rect bounds, Common::String text) {
-	_widgets.push_back(new MacCheckbox(this, bounds, text));
+void MacGui::SimpleWindow::addCheckbox(Common::Rect bounds, Common::String text, bool enabled) {
+	_widgets.push_back(new MacCheckbox(this, bounds, text, enabled));
 }
 
-void MacGui::SimpleWindow::addText(Common::Rect bounds, Common::String text) {
-	_widgets.push_back(new MacText(this, bounds, text));
+void MacGui::SimpleWindow::addText(Common::Rect bounds, Common::String text, bool enabled) {
+	_widgets.push_back(new MacText(this, bounds, text, enabled));
 }
 
-void MacGui::SimpleWindow::addPicture(Common::Rect bounds, int id) {
-	_widgets.push_back(new MacPicture(this, bounds, id));
+void MacGui::SimpleWindow::addPicture(Common::Rect bounds, int id, bool enabled) {
+	_widgets.push_back(new MacPicture(this, bounds, id, enabled));
 }
 
 void MacGui::SimpleWindow::markRectAsDirty(Common::Rect r) {
@@ -578,11 +595,11 @@ void MacGui::SimpleWindow::drawSprite(const Graphics::Surface *sprite, int x, in
 }
 
 int MacGui::SimpleWindow::runDialog() {
-	for (uint i = 0; i < _widgets.size(); i++)
-		_widgets[i]->draw();
-
 	show();
 
+	for (uint i = 0; i < _widgets.size(); i++)
+		_widgets[i]->draw(true);
+
 	int pressedWidget = -1;
 
 	// Run the dialog until something happens to a widget. It's up to the
@@ -1349,6 +1366,7 @@ MacGui::SimpleWindow *MacGui::createDialog(int dialogId) {
 			int len = res->readByte();
 
 			Common::String str;
+			bool enabled = ((type & 0x80) == 0);
 
 			switch (type & 0x7F) {
 			case 0:
@@ -1360,30 +1378,21 @@ MacGui::SimpleWindow *MacGui::createDialog(int dialogId) {
 			case 4:
 				// Button
 				str = getDialogString(res, len);
-				window->addButton(r, str);
+				window->addButton(r, str, enabled);
 				button++;
 				break;
 
 			case 5:
 				// Checkbox
 				str = getDialogString(res, len);
-
-				// The DITL may define a larger than necessary
-				// area for the checkbox, so normalize the
-				// height here. The width will be normalized
-				// when the checkbox is first drawn.
-
-				r.top = r.bottom - r.height() / 2 - 8;
-				r.bottom = r.top + 16;
-
-				window->addCheckbox(r, str);
+				window->addCheckbox(r, str, enabled);
 				break;
 
 			case 8:
 				// Static text
 				str = getDialogString(res, len);
 				r.left++;
-				window->addText(r, str);
+				window->addText(r, str, enabled);
 				break;
 
 			case 16:
@@ -1394,7 +1403,7 @@ MacGui::SimpleWindow *MacGui::createDialog(int dialogId) {
 
 			case 64:
 				// Picture
-				window->addPicture(r, res->readUint16BE());
+				window->addPicture(r, res->readUint16BE(), enabled);
 				break;
 
 			default:
@@ -1817,8 +1826,22 @@ bool MacLoomGui::runOptionsDialog() {
 	// 9 - Picture
 	// 10 - "Machine Speed:  ^0" text
 
+	// TODO: Get these from the SCUMM engine
+	int sound = 0;
+	int music = 0;
+	int scrolling = 0;
+	int fullAnimation = 0;
+
 	SimpleWindow *window = createDialog(1000);
 
+	window->setWidgetValue(2, sound);
+	window->setWidgetValue(3, music);
+	window->setWidgetValue(6, scrolling);
+	window->setWidgetValue(7, fullAnimation);
+
+	if (!sound)
+		window->setWidgetEnabled(3, false);
+
 	// TODO: I don't know where it gets the "Machine Speed" from. It doesn't
 	// appear to be VAR_MACHINE_SPEED, because I think that's only set to 1
 	// or 0, and may be the "Full Animation" setting.
@@ -1840,10 +1863,17 @@ bool MacLoomGui::runOptionsDialog() {
 			break;
 
 		if (clicked == 2) {
-			debug("TODO: Unchecking sound should disable music");
+			window->setWidgetEnabled(3, window->getWidgetValue(2) != 0);
 		}
 	}
 
+	if (ret) {
+		debug("sound: %d", window->getWidgetValue(2));
+		debug("music: %d", window->getWidgetValue(3));
+		debug("scrolling: %d", window->getWidgetValue(6));
+		debug("full animation: %d", window->getWidgetValue(7));
+	}
+
 	delete window;
 	return ret;
 }
@@ -3435,8 +3465,20 @@ bool MacIndy3Gui::runOptionsDialog() {
 	// 7 - "^0" text
 	// 8 - Scrolling checkbox
 
+	// TODO: Get these from the SCUMM engine
+	int sound = 0;
+	int music = 0;
+	int scrolling = 0;
+
 	SimpleWindow *window = createDialog(1000);
 
+	window->setWidgetValue(2, sound);
+	window->setWidgetValue(3, music);
+	window->setWidgetValue(8, scrolling);
+
+	if (!sound)
+		window->setWidgetEnabled(3, false);
+
 	window->addSubstitution(Common::String::format("%d", _vm->VAR(_vm->VAR_MACHINE_SPEED)));
 
 	// When quitting, the default action is not to not apply options
@@ -3454,10 +3496,16 @@ bool MacIndy3Gui::runOptionsDialog() {
 			break;
 
 		if (clicked == 2) {
-			debug("TODO: Unchecking sound should disable music");
+			window->setWidgetEnabled(3, window->getWidgetValue(2) != 0);
 		}
 	}
 
+	if (ret) {
+		debug("sound: %d", window->getWidgetValue(2));
+		debug("music: %d", window->getWidgetValue(3));
+		debug("scrolling: %d", window->getWidgetValue(8));
+	}
+
 	delete window;
 	return ret;
 }
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 15f43bc8a90..078d2c00fea 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -106,55 +106,77 @@ public:
 		MacGui::SimpleWindow *_window;
 		Common::Rect _bounds;
 		Common::String _text;
-		bool _isEnabled = true;
-		bool _isPressed = false;
+		bool _enabled = true;
+		bool _pressed = false;
+		int _value = 0;
 
 	public:
-		MacWidget(MacGui::SimpleWindow *window, Common::Rect bounds, Common::String text);
+		MacWidget(MacGui::SimpleWindow *window, Common::Rect bounds, Common::String text, bool enabled) : _window(window), _bounds(bounds), _text(text), _enabled(enabled) {}
 		virtual ~MacWidget() {};
 
+		bool isEnabled() { return _enabled; }
+
+		void setEnabled(bool enabled) {
+			_enabled = enabled;
+			if (_window->isVisible())
+				draw(true);
+		}
+
 		void setPressed(bool pressed) {
-			_isPressed = pressed;
-			draw();
+			_pressed = pressed;
+			if (_window->isVisible())
+				draw();
+		}
+
+		void setValue(int value) {
+			_value = value;
+			if (_window->isVisible())
+				draw();
 		}
 
+		int getValue() { return _value; }
+
 		virtual void makeDefaultWidget() {}
 		virtual bool findWidget(int x, int y);
 
 		int drawText(Common::String text, int x, int y, int w, Color color, Graphics::TextAlign align = Graphics::kTextAlignLeft);
 
-		virtual void draw() = 0;
+		virtual void draw(bool fullRedraw = false) = 0;
 		virtual void action() {}
 	};
 
 	class MacButton : public MacWidget {
 	private:
 		bool _isDefault = false;
-		bool _firstDraw = true;
 
 	public:
-		MacButton(MacGui::SimpleWindow *window, Common::Rect bounds, Common::String text);
+		MacButton(MacGui::SimpleWindow *window, Common::Rect bounds, Common::String text, bool enabled) : MacWidget(window, bounds, text, enabled) {}
 
 		void makeDefaultWidget() { _isDefault = true; }
 
-		void draw();
+		void draw(bool fullRedraw = false);
 	};
 
 	class MacCheckbox : public MacWidget {
 	private:
+		Common::Rect _hitBounds;
 		bool _isChecked = false;
-		bool _firstDraw = true;
 
 	public:
-		MacCheckbox(MacGui::SimpleWindow *window, Common::Rect bounds, Common::String text);
-		void draw();
+		MacCheckbox(MacGui::SimpleWindow *window, Common::Rect bounds, Common::String text, bool enabled);
+		void draw(bool fullRedraw = false);
 		void action();
 	};
 
+	// The dialogs add texts as disabled, but we don't want it to be drawn
+	// as disabled so we enable it and make it "disabled" by giving it a
+	// custom findWidget().
+
 	class MacText : public MacWidget {
 	public:
-		MacText(MacGui::SimpleWindow *window, Common::Rect bounds, Common::String text);
-		void draw();
+		MacText(MacGui::SimpleWindow *window, Common::Rect bounds, Common::String text, bool enabled) : MacWidget(window, bounds, text, true) {}
+		bool findWidget(int x, int y) { return false; }
+		void draw(bool fullRedraw = false);
 	};
 
 	class MacPicture : public MacWidget {
@@ -162,10 +184,10 @@ public:
 		Graphics::Surface *_picture = nullptr;
 
 	public:
-		MacPicture(MacGui::SimpleWindow *window, Common::Rect bounds, int id);
+		MacPicture(MacGui::SimpleWindow *window, Common::Rect bounds, int id, bool enabled);
 		~MacPicture();
 
-		void draw();
+		void draw(bool fullRedraw = false);
 	};
 
 	class SimpleWindow {
@@ -175,6 +197,8 @@ public:
 		Common::Rect _innerBounds;
 		int _margin;
 
+		bool _visible = false;
+
 		PauseToken _pauseToken;
 
 		Graphics::Surface *_from = nullptr;
@@ -198,17 +222,23 @@ public:
 		Graphics::Surface *surface() { return &_surface; }
 		Graphics::Surface *innerSurface() { return &_innerSurface; }
 
+		bool isVisible() { return _visible; }
+
 		void show();
 		int runDialog();
 
 		void setDefaultWidget(int nr) { _widgets[nr]->makeDefaultWidget(); }
+		void setWidgetEnabled(int nr, bool enabled) { _widgets[nr]->setEnabled(enabled); }
+		bool isWidgetEnabled(int nr) { return _widgets[nr]->isEnabled(); }
+		int getWidgetValue(int nr) { return _widgets[nr]->getValue(); }
+		void setWidgetValue(int nr, int value) { _widgets[nr]->setValue(value); }
 		int findWidget(int x, int y);
 		void redrawWidget(int nr) { _widgets[nr]->draw(); }
 
-		void addButton(Common::Rect bounds, Common::String text);
-		void addCheckbox(Common::Rect bounds, Common::String text);
-		void addText(Common::Rect bounds, Common::String text);
-		void addPicture(Common::Rect bounds, int id);
+		void addButton(Common::Rect bounds, Common::String text, bool enabled);
+		void addCheckbox(Common::Rect bounds, Common::String text, bool enabled);
+		void addText(Common::Rect bounds, Common::String text, bool enabled);
+		void addPicture(Common::Rect bounds, int id, bool enabled);
 
 		void addSubstitution(Common::String text) { _substitutions.push_back(text); }
 		void replaceSubstitution(int nr, Common::String text) { _substitutions[nr] = text; }


Commit: ca0f75065b2c0cea2ef0371e29901b8f4364ebb2
    https://github.com/scummvm/scummvm/commit/ca0f75065b2c0cea2ef0371e29901b8f4364ebb2
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Renamed SimpleWindow to MacDialogWindow

Because the window isn't as simple as it used to be.

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index da6fad5bc58..e0b52a8d938 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -182,7 +182,7 @@ Common::KeyState ScummEngine::mac_showOldStyleBannerAndPause(const char *msg, in
 	// Fetch the translated string for the message...
 	convertMessageToString((const byte *)msg, (byte *)bannerMsg, sizeof(bannerMsg));
 
-	MacGui::SimpleWindow *window = _macGui->drawBanner(bannerMsg);
+	MacGui::MacDialogWindow *window = _macGui->drawBanner(bannerMsg);
 
 	// Pause shake effect
 	_shakeTempSavedState = _shakeEnabled;
@@ -394,7 +394,7 @@ void MacGui::MacButton::draw(bool fullRedraw) {
 	_window->markRectAsDirty(_bounds);
 }
 
-MacGui::MacCheckbox::MacCheckbox(MacGui::SimpleWindow *window, Common::Rect bounds, Common::String text, bool enabled) : MacWidget(window, bounds, text, enabled) {
+MacGui::MacCheckbox::MacCheckbox(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::String text, bool enabled) : MacWidget(window, bounds, text, enabled) {
 	// The DITL may define a larger than necessary area for the checkbox,
 	// so we need to calculate the hit bounds.
 
@@ -443,7 +443,7 @@ void MacGui::MacText::draw(bool fullRedraw) {
 	_window->markRectAsDirty(_bounds);
 }
 
-MacGui::MacPicture::MacPicture(MacGui::SimpleWindow *window, Common::Rect bounds, int id, bool enabled) : MacWidget(window, bounds, "Picture", enabled) {
+MacGui::MacPicture::MacPicture(MacGui::MacDialogWindow *window, Common::Rect bounds, int id, bool enabled) : MacWidget(window, bounds, "Picture", enabled) {
 	_picture = _window->_gui->loadPict(id);
 }
 
@@ -458,7 +458,7 @@ void MacGui::MacPicture::draw(bool fullRedraw) {
 	_window->drawSprite(_picture, _bounds.left, _bounds.top);
 }
 
-MacGui::SimpleWindow::SimpleWindow(MacGui *gui, OSystem *system, Graphics::Surface *from, Common::Rect bounds, SimpleWindowStyle style) : _gui(gui), _system(system), _from(from), _bounds(bounds) {
+MacGui::MacDialogWindow::MacDialogWindow(MacGui *gui, OSystem *system, Graphics::Surface *from, Common::Rect bounds, MacDialogWindowStyle style) : _gui(gui), _system(system), _from(from), _bounds(bounds) {
 	_pauseToken = _gui->_vm->pauseEngine();
 
 	_backup = new Graphics::Surface();
@@ -507,7 +507,7 @@ MacGui::SimpleWindow::SimpleWindow(MacGui *gui, OSystem *system, Graphics::Surfa
 	}
 }
 
-MacGui::SimpleWindow::~SimpleWindow() {
+MacGui::MacDialogWindow::~MacDialogWindow() {
 	copyToScreen(_backup);
 	_backup->free();
 	delete _backup;
@@ -519,20 +519,20 @@ MacGui::SimpleWindow::~SimpleWindow() {
 	_pauseToken.clear();
 }
 
-void MacGui::SimpleWindow::copyToScreen(Graphics::Surface *s) const {
+void MacGui::MacDialogWindow::copyToScreen(Graphics::Surface *s) const {
 	if (s) {
 		_from->copyRectToSurface(*s, _bounds.left, _bounds.top, Common::Rect(0, 0, _bounds.width(), _bounds.height()));
 	}
 	_system->copyRectToScreen(_from->getBasePtr(_bounds.left, _bounds.top), _from->pitch, _bounds.left, _bounds.top, _bounds.width(), _bounds.height());
 }
 
-void MacGui::SimpleWindow::show() {
+void MacGui::MacDialogWindow::show() {
 	_visible = true;
 	copyToScreen();
 	_dirtyRects.clear();
 }
 
-int MacGui::SimpleWindow::findWidget(int x, int y) {
+int MacGui::MacDialogWindow::findWidget(int x, int y) {
 	for (uint i = 0; i < _widgets.size(); i++) {
 		if (_widgets[i]->findWidget(x, y))
 			return i;
@@ -541,27 +541,27 @@ int MacGui::SimpleWindow::findWidget(int x, int y) {
 	return -1;
 }
 
-void MacGui::SimpleWindow::addButton(Common::Rect bounds, Common::String text, bool enabled) {
+void MacGui::MacDialogWindow::addButton(Common::Rect bounds, Common::String text, bool enabled) {
 	_widgets.push_back(new MacButton(this, bounds, text, enabled));
 }
 
-void MacGui::SimpleWindow::addCheckbox(Common::Rect bounds, Common::String text, bool enabled) {
+void MacGui::MacDialogWindow::addCheckbox(Common::Rect bounds, Common::String text, bool enabled) {
 	_widgets.push_back(new MacCheckbox(this, bounds, text, enabled));
 }
 
-void MacGui::SimpleWindow::addText(Common::Rect bounds, Common::String text, bool enabled) {
+void MacGui::MacDialogWindow::addText(Common::Rect bounds, Common::String text, bool enabled) {
 	_widgets.push_back(new MacText(this, bounds, text, enabled));
 }
 
-void MacGui::SimpleWindow::addPicture(Common::Rect bounds, int id, bool enabled) {
+void MacGui::MacDialogWindow::addPicture(Common::Rect bounds, int id, bool enabled) {
 	_widgets.push_back(new MacPicture(this, bounds, id, enabled));
 }
 
-void MacGui::SimpleWindow::markRectAsDirty(Common::Rect r) {
+void MacGui::MacDialogWindow::markRectAsDirty(Common::Rect r) {
 	_dirtyRects.push_back(r);
 }
 
-void MacGui::SimpleWindow::update(bool fullRedraw) {
+void MacGui::MacDialogWindow::update(bool fullRedraw) {
 	if (fullRedraw) {
 		_dirtyRects.clear();
 		markRectAsDirty(Common::Rect(0, 0, _innerSurface.w, _innerSurface.h));
@@ -578,7 +578,7 @@ void MacGui::SimpleWindow::update(bool fullRedraw) {
 	_dirtyRects.clear();
 }
 
-void MacGui::SimpleWindow::fillPattern(Common::Rect r, uint16 pattern) {
+void MacGui::MacDialogWindow::fillPattern(Common::Rect r, uint16 pattern) {
 	for (int y = r.top; y < r.bottom; y++) {
 		for (int x = r.left; x < r.right; x++) {
 			int bit = 0x8000 >> (4 * (y % 4) + (x % 4));
@@ -589,12 +589,12 @@ void MacGui::SimpleWindow::fillPattern(Common::Rect r, uint16 pattern) {
 	markRectAsDirty(r);
 }
 
-void MacGui::SimpleWindow::drawSprite(const Graphics::Surface *sprite, int x, int y) {
+void MacGui::MacDialogWindow::drawSprite(const Graphics::Surface *sprite, int x, int y) {
 	_innerSurface.copyRectToSurface(*sprite, x, y, Common::Rect(0, 0, sprite->w, sprite->h));
 	markRectAsDirty(Common::Rect(x, y, x + sprite->w, y + sprite->h));
 }
 
-int MacGui::SimpleWindow::runDialog() {
+int MacGui::MacDialogWindow::runDialog() {
 	show();
 
 	for (uint i = 0; i < _widgets.size(); i++)
@@ -652,7 +652,7 @@ int MacGui::SimpleWindow::runDialog() {
 	return -1;
 }
 
-void MacGui::SimpleWindow::drawSprite(const Graphics::Surface *sprite, int x, int y, Common::Rect(clipRect)) {
+void MacGui::MacDialogWindow::drawSprite(const Graphics::Surface *sprite, int x, int y, Common::Rect(clipRect)) {
 	Common::Rect subRect(0, 0, sprite->w, sprite->h);
 
 	if (x < clipRect.left) {
@@ -679,8 +679,8 @@ void MacGui::SimpleWindow::drawSprite(const Graphics::Surface *sprite, int x, in
 	}
 }
 
-void MacGui::SimpleWindow::plotPixel(int x, int y, int color, void *data) {
-	MacGui::SimpleWindow *window = (MacGui::SimpleWindow *)data;
+void MacGui::MacDialogWindow::plotPixel(int x, int y, int color, void *data) {
+	MacGui::MacDialogWindow *window = (MacGui::MacDialogWindow *)data;
 	Graphics::Surface *s = window->innerSurface();
 	s->setPixel(x, y, color);
 }
@@ -690,13 +690,13 @@ void MacGui::SimpleWindow::plotPixel(int x, int y, int color, void *data) {
 // subtle effect that I suspect it was just doing some different magic, maybe
 // with XOR, but I couldn't get that to work by eye only.
 
-void MacGui::SimpleWindow::plotPattern(int x, int y, int pattern, void *data) {
+void MacGui::MacDialogWindow::plotPattern(int x, int y, int pattern, void *data) {
 	const uint16 patterns[] = {
 		0x0000, 0x2828, 0xA5A5, 0xD7D7,
 		0xFFFF,	0xD7D7, 0xA5A5, 0x2828
 	};
 
-	MacGui::SimpleWindow *window = (MacGui::SimpleWindow *)data;
+	MacGui::MacDialogWindow *window = (MacGui::MacDialogWindow *)data;
 	Graphics::Surface *s = window->innerSurface();
 	int bit = 0x8000 >> (4 * (y % 4) + (x % 4));
 	if (patterns[pattern] & bit)
@@ -705,19 +705,19 @@ void MacGui::SimpleWindow::plotPattern(int x, int y, int pattern, void *data) {
 		s->setPixel(x, y, kWhite);
 }
 
-void MacGui::SimpleWindow::plotPatternDarkenOnly(int x, int y, int pattern, void *data) {
+void MacGui::MacDialogWindow::plotPatternDarkenOnly(int x, int y, int pattern, void *data) {
 	const uint16 patterns[] = {
 		0x0000, 0x2828, 0xA5A5, 0xD7D7, 0xFFFF
 	};
 
-	MacGui::SimpleWindow *window = (MacGui::SimpleWindow *)data;
+	MacGui::MacDialogWindow *window = (MacGui::MacDialogWindow *)data;
 	Graphics::Surface *s = window->innerSurface();
 	int bit = 0x8000 >> (4 * (y % 4) + (x % 4));
 	if (patterns[pattern] & bit)
 		s->setPixel(x, y, kBlack);
 }
 
-void MacGui::SimpleWindow::drawTexts(Common::Rect r, const TextLine *lines) {
+void MacGui::MacDialogWindow::drawTexts(Common::Rect r, const TextLine *lines) {
 	if (!lines)
 		return;
 
@@ -763,7 +763,7 @@ void MacGui::SimpleWindow::drawTexts(Common::Rect r, const TextLine *lines) {
 	}
 }
 
-void MacGui::SimpleWindow::drawTextBox(Common::Rect r, const TextLine *lines, int arc) {
+void MacGui::MacDialogWindow::drawTextBox(Common::Rect r, const TextLine *lines, int arc) {
 	Graphics::drawRoundRect(r, arc, kWhite, true, plotPixel, this);
 	Graphics::drawRoundRect(r, arc, kBlack, false, plotPixel, this);
 	markRectAsDirty(r);
@@ -1154,7 +1154,7 @@ bool MacGui::runQuitDialog() {
 	// 1 - Cancel button
 	// 2 - "^0" text
 
-	SimpleWindow *window = createDialog(502);
+	MacDialogWindow *window = createDialog(502);
 
 	window->setDefaultWidget(0);
 	window->addSubstitution("Are you sure you want to quit?");
@@ -1185,7 +1185,7 @@ bool MacGui::runRestartDialog() {
 	// 1 - Cancel button
 	// 2 - "^0" text
 
-	SimpleWindow *window = createDialog(502);
+	MacDialogWindow *window = createDialog(502);
 
 	window->setDefaultWidget(0);
 	window->addSubstitution("Are you sure you want to restart this game from the beginning?");
@@ -1256,8 +1256,8 @@ void MacGui::updateWindowManager() {
 	_windowManager->draw();
 }
 
-MacGui::SimpleWindow *MacGui::drawBanner(char *message) {
-	MacGui::SimpleWindow *window = createWindow(Common::Rect(70, 189, 570, 211), kStyleRounded);
+MacGui::MacDialogWindow *MacGui::drawBanner(char *message) {
+	MacGui::MacDialogWindow *window = createWindow(Common::Rect(70, 189, 570, 211), kStyleRounded);
 	const Graphics::Font *font = getFont(_vm->_game.id == GID_INDY3 ? kIndy3FontMedium : kLoomFontMedium);
 
 	Graphics::Surface *s = window->innerSurface();
@@ -1302,16 +1302,16 @@ int MacGui::delay(uint32 ms) {
 	return 0;
 }
 
-MacGui::SimpleWindow *MacGui::createWindow(Common::Rect bounds, SimpleWindowStyle style) {
+MacGui::MacDialogWindow *MacGui::createWindow(Common::Rect bounds, MacDialogWindowStyle style) {
 	if (bounds.left < 0 || bounds.top < 0 || bounds.right >= 640 || bounds.bottom >= 400) {
 		// This happens with the Last Crusade file dialogs.
 		bounds.moveTo((640 - bounds.width()) / 2, 27);
 	}
 
-	return new SimpleWindow(this, _system, _surface, bounds, style);
+	return new MacDialogWindow(this, _system, _surface, bounds, style);
 }
 
-MacGui::SimpleWindow *MacGui::createDialog(int dialogId) {
+MacGui::MacDialogWindow *MacGui::createDialog(int dialogId) {
 	Common::MacResManager resource;
 	Common::SeekableReadStream *res;
 	int button = 0;
@@ -1342,7 +1342,7 @@ MacGui::SimpleWindow *MacGui::createDialog(int dialogId) {
 		bounds.translate(86, 88);
 	}
 
-	SimpleWindow *window = createWindow(bounds);
+	MacDialogWindow *window = createWindow(bounds);
 
 	res = resource.getResource(MKTAG('D', 'I', 'T', 'L'), dialogId);
 
@@ -1544,7 +1544,7 @@ void MacLoomGui::runAboutDialog() {
 	int y = (400 - height) / 2;
 
 	Common::Rect bounds(x, y, x + width, y + height);
-	SimpleWindow *window = createWindow(bounds);
+	MacDialogWindow *window = createWindow(bounds);
 	Graphics::Surface *lucasFilm = loadPict(5000);
 	Graphics::Surface *loom = loadPict(5001);
 
@@ -1656,7 +1656,7 @@ void MacLoomGui::runAboutDialog() {
 			if (pattern > 4)
 				darkenOnly = false;
 
-			Graphics::drawRoundRect(r, 7, pattern, true, darkenOnly ? SimpleWindow::plotPatternDarkenOnly : SimpleWindow::plotPattern, window);
+			Graphics::drawRoundRect(r, 7, pattern, true, darkenOnly ? MacDialogWindow::plotPatternDarkenOnly : MacDialogWindow::plotPattern, window);
 
 			if (!fastForward)
 				window->markRectAsDirty(r);
@@ -1832,7 +1832,7 @@ bool MacLoomGui::runOptionsDialog() {
 	int scrolling = 0;
 	int fullAnimation = 0;
 
-	SimpleWindow *window = createDialog(1000);
+	MacDialogWindow *window = createDialog(1000);
 
 	window->setWidgetValue(2, sound);
 	window->setWidgetValue(3, music);
@@ -3121,7 +3121,7 @@ bool MacIndy3Gui::handleMenu(int id, Common::String &name) {
 	}
 
 	if (dialogId != -1) {
-		SimpleWindow *dialog = createDialog(dialogId);
+		MacDialogWindow *dialog = createDialog(dialogId);
 		dialog->runDialog();
 		return true;
 	}
@@ -3140,7 +3140,7 @@ void MacIndy3Gui::runAboutDialog() {
 	int y = (400 - height) / 2;
 
 	Common::Rect bounds(x, y, x + width, y + height);
-	SimpleWindow *window = createWindow(bounds);
+	MacDialogWindow *window = createWindow(bounds);
 	Graphics::Surface *pict = loadPict(2000);
 
 	// For the background of the sprites to match the background of the
@@ -3359,7 +3359,7 @@ void MacIndy3Gui::runAboutDialog() {
 	delete window;
 }
 
-void MacIndy3Gui::clearAboutDialog(SimpleWindow *window) {
+void MacIndy3Gui::clearAboutDialog(MacDialogWindow *window) {
 	Graphics::Surface *s = window->innerSurface();
 
 	window->fillPattern(Common::Rect(2, 2, s->w - 2, 132), 0x8020);
@@ -3386,7 +3386,7 @@ bool MacIndy3Gui::runOpenDialog() {
 	// 12 - "Series: ^1" text
 	// 13 - "(Indy Quotient)" text
 
-	SimpleWindow *window = createDialog((_vm->_renderMode == Common::kRenderMacintoshBW) ? 4000 : 4001);
+	MacDialogWindow *window = createDialog((_vm->_renderMode == Common::kRenderMacintoshBW) ? 4000 : 4001);
 
 	window->setDefaultWidget(0);
 	window->addSubstitution(Common::String::format("%d", _vm->VAR(244)));
@@ -3427,7 +3427,7 @@ bool MacIndy3Gui::runSaveDialog() {
 	// 10 - "Series: ^1" text
 	// 11 - "(Indy Quotient)" text
 
-	SimpleWindow *window = createDialog((_vm->_renderMode == Common::kRenderMacintoshBW) ? 3998: 3999);
+	MacDialogWindow *window = createDialog((_vm->_renderMode == Common::kRenderMacintoshBW) ? 3998: 3999);
 
 	window->setDefaultWidget(0);
 	window->addSubstitution(Common::String::format("%d", _vm->VAR(244)));
@@ -3470,7 +3470,7 @@ bool MacIndy3Gui::runOptionsDialog() {
 	int music = 0;
 	int scrolling = 0;
 
-	SimpleWindow *window = createDialog(1000);
+	MacDialogWindow *window = createDialog(1000);
 
 	window->setWidgetValue(2, sound);
 	window->setWidgetValue(3, music);
@@ -3520,7 +3520,7 @@ bool MacIndy3Gui::runIqPointsDialog() {
 	// 4 - "Series: ^1" text
 	// 5 - "IQ" picture
 
-	SimpleWindow *window = createDialog((_vm->_renderMode == Common::kRenderMacintoshBW) ? 1001 : 1002);
+	MacDialogWindow *window = createDialog((_vm->_renderMode == Common::kRenderMacintoshBW) ? 1001 : 1002);
 
 	window->addSubstitution(Common::String::format("%d", _vm->VAR(244)));
 	window->addSubstitution(Common::String::format("%d", _vm->VAR(245)));
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 078d2c00fea..e60b34dea9d 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -94,16 +94,16 @@ public:
 		const char *str;
 	};
 
-	enum SimpleWindowStyle {
+	enum MacDialogWindowStyle {
 		kStyleNormal,
 		kStyleRounded
 	};
 
-	class SimpleWindow;
+	class MacDialogWindow;
 
 	class MacWidget {
 	protected:
-		MacGui::SimpleWindow *_window;
+		MacGui::MacDialogWindow *_window;
 		Common::Rect _bounds;
 		Common::String _text;
 		bool _enabled = true;
@@ -111,7 +111,7 @@ public:
 		int _value = 0;
 
 	public:
-		MacWidget(MacGui::SimpleWindow *window, Common::Rect bounds, Common::String text, bool enabled) : _window(window), _bounds(bounds), _text(text), _enabled(enabled) {}
+		MacWidget(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::String text, bool enabled) : _window(window), _bounds(bounds), _text(text), _enabled(enabled) {}
 		virtual ~MacWidget() {};
 
 		bool isEnabled() { return _enabled; }
@@ -150,7 +150,7 @@ public:
 		bool _isDefault = false;
 
 	public:
-		MacButton(MacGui::SimpleWindow *window, Common::Rect bounds, Common::String text, bool enabled) : MacWidget(window, bounds, text, enabled) {}
+		MacButton(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::String text, bool enabled) : MacWidget(window, bounds, text, enabled) {}
 
 		void makeDefaultWidget() { _isDefault = true; }
 
@@ -163,7 +163,7 @@ public:
 		bool _isChecked = false;
 
 	public:
-		MacCheckbox(MacGui::SimpleWindow *window, Common::Rect bounds, Common::String text, bool enabled);
+		MacCheckbox(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::String text, bool enabled);
 		void draw(bool fullRedraw = false);
 		void action();
 	};
@@ -174,7 +174,7 @@ public:
 
 	class MacText : public MacWidget {
 	public:
-		MacText(MacGui::SimpleWindow *window, Common::Rect bounds, Common::String text, bool enabled) : MacWidget(window, bounds, text, true) {}
+		MacText(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::String text, bool enabled) : MacWidget(window, bounds, text, true) {}
 		bool findWidget(int x, int y) { return false; }
 		void draw(bool fullRedraw = false);
 	};
@@ -184,13 +184,13 @@ public:
 		Graphics::Surface *_picture = nullptr;
 
 	public:
-		MacPicture(MacGui::SimpleWindow *window, Common::Rect bounds, int id, bool enabled);
+		MacPicture(MacGui::MacDialogWindow *window, Common::Rect bounds, int id, bool enabled);
 		~MacPicture();
 
 		void draw(bool fullRedraw = false);
 	};
 
-	class SimpleWindow {
+	class MacDialogWindow {
 	private:
 		OSystem *_system;
 		Common::Rect _bounds;
@@ -216,8 +216,8 @@ public:
 	public:
 		MacGui *_gui;
 
-		SimpleWindow(MacGui *gui, OSystem *system, Graphics::Surface *from, Common::Rect bounds, SimpleWindowStyle style = kStyleNormal);
-		~SimpleWindow();
+		MacDialogWindow(MacGui *gui, OSystem *system, Graphics::Surface *from, Common::Rect bounds, MacDialogWindowStyle style = kStyleNormal);
+		~MacDialogWindow();
 
 		Graphics::Surface *surface() { return &_surface; }
 		Graphics::Surface *innerSurface() { return &_innerSurface; }
@@ -318,12 +318,12 @@ public:
 
 	void setPalette(const byte *palette, uint size);
 
-	SimpleWindow *drawBanner(char *message);
+	MacDialogWindow *drawBanner(char *message);
 
 	int delay(uint32 ms);
 
-	SimpleWindow *createWindow(Common::Rect bounds, SimpleWindowStyle style = kStyleNormal);
-	SimpleWindow *createDialog(int dialogId);
+	MacDialogWindow *createWindow(Common::Rect bounds, MacDialogWindowStyle style = kStyleNormal);
+	MacDialogWindow *createDialog(int dialogId);
 };
 
 class MacLoomGui : public MacGui {
@@ -383,7 +383,7 @@ public:
 	bool handleMenu(int id, Common::String &name);
 
 	void runAboutDialog();
-	void clearAboutDialog(SimpleWindow *window);
+	void clearAboutDialog(MacDialogWindow *window);
 	bool runOpenDialog();
 	bool runSaveDialog();
 	bool runOptionsDialog();


Commit: 8f14ebbaa1421dcaf1bda57a7d5d3b20fe0f8f9f
    https://github.com/scummvm/scummvm/commit/8f14ebbaa1421dcaf1bda57a7d5d3b20fe0f8f9f
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Mac GUI cleanups

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index e0b52a8d938..5523128c8a2 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -208,13 +208,41 @@ Common::KeyState ScummEngine::mac_showOldStyleBannerAndPause(const char *msg, in
 	return ks;
 }
 
-// Very simple window class and widgets. It is perhaps unfortunate that we have
-// two different sets of widget classes, but they behave quite differently.
+// ===========================================================================
+// Macintosh GUI
+// ===========================================================================
+
+// ---------------------------------------------------------------------------
+// Simple window and widget classes. These are meant to emulate the look and
+// feel of an 80s Macintosh.
+// ---------------------------------------------------------------------------
+
+// ---------------------------------------------------------------------------
+// Base widget
+// ---------------------------------------------------------------------------
 
-bool MacGui::MacWidget::findWidget(int x, int y) {
+bool MacGui::MacWidget::findWidget(int x, int y) const {
 	return _enabled && _bounds.contains(x, y);
 }
 
+void MacGui::MacWidget::setEnabled(bool enabled) {
+	_enabled = enabled;
+	if (_window->isVisible())
+		draw(true);
+}
+
+void MacGui::MacWidget::setPressed(bool pressed) {
+	_pressed = pressed;
+	if (_window->isVisible())
+		draw();
+}
+
+void MacGui::MacWidget::setValue(int value) {
+	_value = value;
+	if (_window->isVisible())
+		draw();
+}
+
 int MacGui::MacWidget::drawText(Common::String text, int x, int y, int w, Color color, Graphics::TextAlign align) {
 	if (text.empty())
 		return 0;
@@ -291,6 +319,10 @@ int MacGui::MacWidget::drawText(Common::String text, int x, int y, int w, Color
 	return lineWidth;
 }
 
+// ---------------------------------------------------------------------------
+// Button widget
+// ---------------------------------------------------------------------------
+
 void MacGui::MacButton::draw(bool fullRedraw) {
 	Graphics::Surface *s = _window->innerSurface();
 	Color fg, bg;
@@ -394,6 +426,10 @@ void MacGui::MacButton::draw(bool fullRedraw) {
 	_window->markRectAsDirty(_bounds);
 }
 
+// ---------------------------------------------------------------------------
+// Checkbox widget
+// ---------------------------------------------------------------------------
+
 MacGui::MacCheckbox::MacCheckbox(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::String text, bool enabled) : MacWidget(window, bounds, text, enabled) {
 	// The DITL may define a larger than necessary area for the checkbox,
 	// so we need to calculate the hit bounds.
@@ -437,12 +473,20 @@ void MacGui::MacCheckbox::action() {
 	draw();
 }
 
+// ---------------------------------------------------------------------------
+// Static text widget
+// ---------------------------------------------------------------------------
+
 void MacGui::MacText::draw(bool fullRedraw) {
 	_window->innerSurface()->fillRect(_bounds, kWhite);
 	drawText(_text, _bounds.left, _bounds.top, _bounds.width(), kBlack);
 	_window->markRectAsDirty(_bounds);
 }
 
+// ---------------------------------------------------------------------------
+// Image widget
+// ---------------------------------------------------------------------------
+
 MacGui::MacPicture::MacPicture(MacGui::MacDialogWindow *window, Common::Rect bounds, int id, bool enabled) : MacWidget(window, bounds, "Picture", enabled) {
 	_picture = _window->_gui->loadPict(id);
 }
@@ -458,6 +502,13 @@ void MacGui::MacPicture::draw(bool fullRedraw) {
 	_window->drawSprite(_picture, _bounds.left, _bounds.top);
 }
 
+// ---------------------------------------------------------------------------
+// Dialog window
+//
+// This can either be used as a modal dialog (options, etc.), or as a framed
+// drawing area (about and pause). It can not be dragged.
+// ---------------------------------------------------------------------------
+
 MacGui::MacDialogWindow::MacDialogWindow(MacGui *gui, OSystem *system, Graphics::Surface *from, Common::Rect bounds, MacDialogWindowStyle style) : _gui(gui), _system(system), _from(from), _bounds(bounds) {
 	_pauseToken = _gui->_vm->pauseEngine();
 
@@ -532,7 +583,7 @@ void MacGui::MacDialogWindow::show() {
 	_dirtyRects.clear();
 }
 
-int MacGui::MacDialogWindow::findWidget(int x, int y) {
+int MacGui::MacDialogWindow::findWidget(int x, int y) const {
 	for (uint i = 0; i < _widgets.size(); i++) {
 		if (_widgets[i]->findWidget(x, y))
 			return i;
@@ -602,9 +653,9 @@ int MacGui::MacDialogWindow::runDialog() {
 
 	int pressedWidget = -1;
 
-	// Run the dialog until something happens to a widget. It's up to the
-	// caller to repeat the calls to runDialog() until the dialog is
-	// finished.
+	// Run the dialog until something interesting happens to a widget. It's
+	// up to the caller to repeat the calls to runDialog() until the dialog
+	// has ended.
 
 	while (!_gui->_vm->shouldQuit()) {
 		Common::Event event;
@@ -772,8 +823,8 @@ void MacGui::MacDialogWindow::drawTextBox(Common::Rect r, const TextLine *lines,
 }
 
 // ===========================================================================
-// Macintosh user interface for the Macintosh versions of Loom and Indiana
-// Jones and the Last Crusade.
+// Base class for Macintosh game user interface. Handles menus, fonts, image
+// loading, etc.
 // ===========================================================================
 
 MacGui::MacGui(ScummEngine *vm, Common::String resourceFile) : _vm(vm), _system(_vm->_system), _surface(_vm->_macScreen), _resourceFile(resourceFile) {
@@ -844,7 +895,6 @@ void MacGui::initialize() {
 
 			if (!name.empty())
 				menu->setAction(subItem, id++);
-
 		}
 	}
 
@@ -894,7 +944,7 @@ const Graphics::Font *MacGui::getFont(FontId fontId) {
 	return font;
 }
 
-bool MacGui::getFontParams(FontId fontId, int &id, int &size, int &slant) {
+bool MacGui::getFontParams(FontId fontId, int &id, int &size, int &slant) const {
 	switch (fontId) {
 	case FontId::kAboutFontHeaderOutside:
 		id = _gameFontId;
@@ -1449,7 +1499,7 @@ const Graphics::Font *MacLoomGui::getFontByScummId(int32 id) {
 	}
 }
 
-bool MacLoomGui::getFontParams(FontId fontId, int &id, int &size, int &slant) {
+bool MacLoomGui::getFontParams(FontId fontId, int &id, int &size, int &slant) const {
 	if (MacGui::getFontParams(fontId, id, size, slant))
 		return true;
 
@@ -2994,7 +3044,7 @@ const Graphics::Font *MacIndy3Gui::getFontByScummId(int32 id) {
 	}
 }
 
-bool MacIndy3Gui::getFontParams(FontId fontId, int &id, int &size, int &slant) {
+bool MacIndy3Gui::getFontParams(FontId fontId, int &id, int &size, int &slant) const {
 	if (MacGui::getFontParams(fontId, id, size, slant))
 		return true;
 
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index e60b34dea9d..b90e18706de 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -76,9 +76,6 @@ protected:
 		kTransparency = 255
 	};
 
-	Common::String getDialogString(Common::SeekableReadStream *res, int len);
-
-public:
 	enum TextStyle {
 		kStyleHeader,
 		kStyleBold,
@@ -99,6 +96,9 @@ public:
 		kStyleRounded
 	};
 
+	Common::String getDialogString(Common::SeekableReadStream *res, int len);
+
+public:
 	class MacDialogWindow;
 
 	class MacWidget {
@@ -110,36 +110,22 @@ public:
 		bool _pressed = false;
 		int _value = 0;
 
+		int drawText(Common::String text, int x, int y, int w, Color color, Graphics::TextAlign align = Graphics::kTextAlignLeft);
+
 	public:
 		MacWidget(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::String text, bool enabled) : _window(window), _bounds(bounds), _text(text), _enabled(enabled) {}
 		virtual ~MacWidget() {};
 
-		bool isEnabled() { return _enabled; }
-
-		void setEnabled(bool enabled) {
-			_enabled = enabled;
-			if (_window->isVisible())
-				draw(true);
-		}
+		bool isEnabled() const { return _enabled; }
+		void setEnabled(bool enabled);
 
-		void setPressed(bool pressed) {
-			_pressed = pressed;
-			if (_window->isVisible())
-				draw();
-		}
+		void setPressed(bool pressed);
 
-		void setValue(int value) {
-			_value = value;
-			if (_window->isVisible())
-				draw();
-		}
-
-		int getValue() { return _value; }
+		void setValue(int value);
+		int getValue() const { return _value; }
 
 		virtual void makeDefaultWidget() {}
-		virtual bool findWidget(int x, int y);
-
-		int drawText(Common::String text, int x, int y, int w, Color color, Graphics::TextAlign align = Graphics::kTextAlignLeft);
+		virtual bool findWidget(int x, int y) const;
 
 		virtual void draw(bool fullRedraw = false) = 0;
 		virtual void action() {}
@@ -175,7 +161,7 @@ public:
 	class MacText : public MacWidget {
 	public:
 		MacText(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::String text, bool enabled) : MacWidget(window, bounds, text, true) {}
-		bool findWidget(int x, int y) { return false; }
+		bool findWidget(int x, int y) const { return false; }
 		void draw(bool fullRedraw = false);
 	};
 
@@ -222,17 +208,17 @@ public:
 		Graphics::Surface *surface() { return &_surface; }
 		Graphics::Surface *innerSurface() { return &_innerSurface; }
 
-		bool isVisible() { return _visible; }
+		bool isVisible() const { return _visible; }
 
 		void show();
 		int runDialog();
 
 		void setDefaultWidget(int nr) { _widgets[nr]->makeDefaultWidget(); }
 		void setWidgetEnabled(int nr, bool enabled) { _widgets[nr]->setEnabled(enabled); }
-		bool isWidgetEnabled(int nr) { return _widgets[nr]->isEnabled(); }
-		int getWidgetValue(int nr) { return _widgets[nr]->getValue(); }
+		bool isWidgetEnabled(int nr) const { return _widgets[nr]->isEnabled(); }
+		int getWidgetValue(int nr) const { return _widgets[nr]->getValue(); }
 		void setWidgetValue(int nr, int value) { _widgets[nr]->setValue(value); }
-		int findWidget(int x, int y);
+		int findWidget(int x, int y) const;
 		void redrawWidget(int nr) { _widgets[nr]->draw(); }
 
 		void addButton(Common::Rect bounds, Common::String text, bool enabled);
@@ -243,7 +229,7 @@ public:
 		void addSubstitution(Common::String text) { _substitutions.push_back(text); }
 		void replaceSubstitution(int nr, Common::String text) { _substitutions[nr] = text; }
 
-		bool hasSubstitution(uint n) { return n < _substitutions.size(); }
+		bool hasSubstitution(uint n) const { return n < _substitutions.size(); }
 		Common::String &getSubstitution(uint n) { return _substitutions[n]; }
 
 		void markRectAsDirty(Common::Rect r);
@@ -291,7 +277,7 @@ public:
 
 	const Graphics::Font *getFont(FontId fontId);
 	virtual const Graphics::Font *getFontByScummId(int32 id) = 0;
-	virtual bool getFontParams(FontId fontId, int &id, int &size, int &slant);
+	virtual bool getFontParams(FontId fontId, int &id, int &size, int &slant) const;
 
 	Graphics::Surface *loadPict(int id);
 	Graphics::Surface *decodePictV1(Common::SeekableReadStream *res);
@@ -340,7 +326,7 @@ public:
 	const Common::String name() const { return "Loom"; }
 
 	const Graphics::Font *getFontByScummId(int32 id);
-	bool getFontParams(FontId fontId, int &id, int &size, int &slant);
+	bool getFontParams(FontId fontId, int &id, int &size, int &slant) const;
 
 	void setupCursor(int &width, int &height, int &hotspotX, int &hotspotY, int &animate);
 
@@ -371,7 +357,7 @@ public:
 	Graphics::Surface _textArea;
 
 	const Graphics::Font *getFontByScummId(int32 id);
-	bool getFontParams(FontId fontId, int &id, int &size, int &slant);
+	bool getFontParams(FontId fontId, int &id, int &size, int &slant) const;
 
 	void setupCursor(int &width, int &height, int &hotspotX, int &hotspotY, int &animate);
 


Commit: dfc5e9380210f23ff2d108fa02d9c543f4ef4efb
    https://github.com/scummvm/scummvm/commit/dfc5e9380210f23ff2d108fa02d9c543f4ef4efb
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Mac GUI cleanups

Changed paths:
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index b90e18706de..42ea1f68d04 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -76,6 +76,26 @@ protected:
 		kTransparency = 255
 	};
 
+	enum FontId {
+		kSystemFont,
+
+		kAboutFontRegular,
+		kAboutFontBold,
+		kAboutFontExtraBold,
+		kAboutFontHeaderInside,
+		kAboutFontHeaderOutside,
+
+		kIndy3FontSmall,
+		kIndy3FontMedium,
+		kIndy3VerbFontRegular,
+		kIndy3VerbFontBold,
+		kIndy3VerbFontOutline,
+
+		kLoomFontSmall,
+		kLoomFontMedium,
+		kLoomFontLarge
+	};
+
 	enum TextStyle {
 		kStyleHeader,
 		kStyleBold,
@@ -96,8 +116,20 @@ protected:
 		kStyleRounded
 	};
 
+	virtual bool getFontParams(FontId fontId, int &id, int &size, int &slant) const;
+
 	Common::String getDialogString(Common::SeekableReadStream *res, int len);
 
+	virtual bool handleMenu(int id, Common::String &name);
+
+	virtual void runAboutDialog() = 0;
+	virtual bool runOpenDialog() = 0;
+	virtual bool runSaveDialog() = 0;
+	virtual bool runOptionsDialog() = 0;
+
+	bool runQuitDialog();
+	bool runRestartDialog();
+
 public:
 	class MacDialogWindow;
 
@@ -246,52 +278,22 @@ public:
 		void drawTextBox(Common::Rect r, const TextLine *lines, int arc = 9);
 	};
 
-	enum FontId {
-		kSystemFont,
-
-		kAboutFontRegular,
-		kAboutFontBold,
-		kAboutFontExtraBold,
-		kAboutFontHeaderInside,
-		kAboutFontHeaderOutside,
-
-		kIndy3FontSmall,
-		kIndy3FontMedium,
-		kIndy3VerbFontRegular,
-		kIndy3VerbFontBold,
-		kIndy3VerbFontOutline,
-
-		kLoomFontSmall,
-		kLoomFontMedium,
-		kLoomFontLarge
-	};
-
 	MacGui(ScummEngine *vm, Common::String resourceFile);
 	virtual ~MacGui();
 
 	virtual const Common::String name() const = 0;
 
+	virtual bool handleEvent(Common::Event &event);
 	static void menuCallback(int id, Common::String &name, void *data);
 
 	virtual void initialize();
 
 	const Graphics::Font *getFont(FontId fontId);
 	virtual const Graphics::Font *getFontByScummId(int32 id) = 0;
-	virtual bool getFontParams(FontId fontId, int &id, int &size, int &slant) const;
 
 	Graphics::Surface *loadPict(int id);
 	Graphics::Surface *decodePictV1(Common::SeekableReadStream *res);
 
-	virtual bool handleMenu(int id, Common::String &name);
-
-	virtual void runAboutDialog() = 0;
-	virtual bool runOpenDialog() = 0;
-	virtual bool runSaveDialog() = 0;
-	virtual bool runOptionsDialog() = 0;
-
-	bool runQuitDialog();
-	bool runRestartDialog();
-
 	virtual bool isVerbGuiActive() const { return false; }
 	virtual void reset() {}
 	virtual void resetAfterLoad() = 0;
@@ -300,7 +302,6 @@ public:
 	void updateWindowManager();
 
 	virtual void setupCursor(int &width, int &height, int &hotspotX, int &hotspotY, int &animate) = 0;
-	virtual bool handleEvent(Common::Event &event);
 
 	void setPalette(const byte *palette, uint size);
 
@@ -313,23 +314,24 @@ public:
 };
 
 class MacLoomGui : public MacGui {
-private:
-	Graphics::Surface *_practiceBox = nullptr;
-	int _practiceBoxX;
-	int _practiceBoxY;
-	int _practiceBoxNotes;
-
 public:
 	MacLoomGui(ScummEngine *vm, Common::String resourceFile);
 	~MacLoomGui();
 
 	const Common::String name() const { return "Loom"; }
 
+	bool handleEvent(Common::Event &event);
+
 	const Graphics::Font *getFontByScummId(int32 id);
-	bool getFontParams(FontId fontId, int &id, int &size, int &slant) const;
 
 	void setupCursor(int &width, int &height, int &hotspotX, int &hotspotY, int &animate);
 
+	void resetAfterLoad();
+	void update(int delta);
+
+protected:
+	bool getFontParams(FontId fontId, int &id, int &size, int &slant) const;
+
 	bool handleMenu(int id, Common::String &name);
 
 	void runAboutDialog();
@@ -337,9 +339,11 @@ public:
 	bool runSaveDialog();
 	bool runOptionsDialog();
 
-	void resetAfterLoad();
-	void update(int delta);
-	bool handleEvent(Common::Event &event);
+private:
+	Graphics::Surface *_practiceBox = nullptr;
+	int _practiceBoxX;
+	int _practiceBoxY;
+	int _practiceBoxNotes;
 };
 
 class MacIndy3Gui : public MacGui {
@@ -357,7 +361,6 @@ public:
 	Graphics::Surface _textArea;
 
 	const Graphics::Font *getFontByScummId(int32 id);
-	bool getFontParams(FontId fontId, int &id, int &size, int &slant) const;
 
 	void setupCursor(int &width, int &height, int &hotspotX, int &hotspotY, int &animate);
 
@@ -366,15 +369,6 @@ public:
 	void initTextAreaForActor(Actor *a, byte color);
 	void printCharToTextArea(int chr, int x, int y, int color);
 
-	bool handleMenu(int id, Common::String &name);
-
-	void runAboutDialog();
-	void clearAboutDialog(MacDialogWindow *window);
-	bool runOpenDialog();
-	bool runSaveDialog();
-	bool runOptionsDialog();
-	bool runIqPointsDialog();
-
 	// There is a distinction between the GUI being allowed and being
 	// active. Allowed means that it's allowed to draw verbs, but not that
 	// it necessarily is. Active means that there are verbs on screen. From
@@ -392,8 +386,16 @@ public:
 	void update(int delta);
 	bool handleEvent(Common::Event &event);
 
-	int getInventoryScrollOffset() const;
-	void setInventoryScrollOffset(int n) const;
+protected:
+	bool getFontParams(FontId fontId, int &id, int &size, int &slant) const;
+
+	bool handleMenu(int id, Common::String &name);
+
+	void runAboutDialog();
+	bool runOpenDialog();
+	bool runSaveDialog();
+	bool runOptionsDialog();
+	bool runIqPointsDialog();
 
 private:
 	bool _visible = false;
@@ -408,6 +410,11 @@ private:
 	void updateMouseHeldTimer(int delta);
 	void drawVerbs();
 
+	void clearAboutDialog(MacDialogWindow *window);
+
+	int getInventoryScrollOffset() const;
+	void setInventoryScrollOffset(int n) const;
+
 	class Widget {
 	private:
 		int _timer = 0;


Commit: 457db6cd838d175c7b0e70be18ed5d1ca6de7ae2
    https://github.com/scummvm/scummvm/commit/457db6cd838d175c7b0e70be18ed5d1ca6de7ae2
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Fix and actually use the MacCheckbox _hitBounds

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 5523128c8a2..7e41160950f 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -249,6 +249,8 @@ int MacGui::MacWidget::drawText(Common::String text, int x, int y, int w, Color
 
 	const Graphics::Font *font = _window->_gui->getFont(kSystemFont);
 
+	// Apply text substitutions
+
 	for (uint i = 0; i < text.size() - 1; i++) {
 		if (text[i] == '^') {
 			uint nr = text[i + 1] - '0';
@@ -259,6 +261,8 @@ int MacGui::MacWidget::drawText(Common::String text, int x, int y, int w, Color
 		}
 	}
 
+	// Word-wrap text
+
 	Common::StringArray lines;
 	int start = 0;
 	int maxLineWidth = 0;
@@ -293,15 +297,14 @@ int MacGui::MacWidget::drawText(Common::String text, int x, int y, int w, Color
 	if (lineWidth > maxLineWidth)
 		maxLineWidth = lineWidth;
 
+	// Draw the text. Disabled text is implemented as a filter on top of
+	// the already drawn text.
+
 	int y0 = y;
 
 	for (uint i = 0; i < lines.size(); i++) {
 		font->drawString(_window->innerSurface(), lines[i], x, y0, w, color, align);
 
-		// Implementing drawing of disabled text is too much of a
-		// hassle. Simply draw a white checkerboard pattern on top
-		// of it.
-
 		if (!_enabled) {
 			Common::Rect textBox = font->getBoundingBox(lines[i], x, y0, w, align);
 
@@ -438,10 +441,14 @@ MacGui::MacCheckbox::MacCheckbox(MacGui::MacDialogWindow *window, Common::Rect b
 
 	_hitBounds.left = _bounds.left;
 	_hitBounds.top = _bounds.bottom - _bounds.height() / 2 - 8;
-	_hitBounds.bottom = _bounds.top + 18;
+	_hitBounds.bottom = _hitBounds.top + 16;
 	_hitBounds.right = _bounds.left + 18 + font->getStringWidth(_text) + 2;
 }
 
+bool MacGui::MacCheckbox::findWidget(int x, int y) const {
+	return _enabled && _hitBounds.contains(x, y);
+}
+
 void MacGui::MacCheckbox::draw(bool fullRedraw) {
 	Graphics::Surface *s = _window->innerSurface();
 	Common::Rect box(_hitBounds.left + 2, _hitBounds.top + 2, _hitBounds.left + 14, _hitBounds.top + 14);
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 42ea1f68d04..0c5e718fbea 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -182,6 +182,8 @@ public:
 
 	public:
 		MacCheckbox(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::String text, bool enabled);
+
+		bool findWidget(int x, int y) const;
 		void draw(bool fullRedraw = false);
 		void action();
 	};


Commit: ec52c86969a49d2a988a0074e156102cdbece454
    https://github.com/scummvm/scummvm/commit/ec52c86969a49d2a988a0074e156102cdbece454
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Disable Mac picture widgets by default

I plan on using the "enabled" flag to indicate that a picture widget can
be dragged. Though I still need to figure out the best way of saving the
background under the dragable widget.

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 7e41160950f..d268e9c0d4d 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -612,7 +612,7 @@ void MacGui::MacDialogWindow::addText(Common::Rect bounds, Common::String text,
 }
 
 void MacGui::MacDialogWindow::addPicture(Common::Rect bounds, int id, bool enabled) {
-	_widgets.push_back(new MacPicture(this, bounds, id, enabled));
+	_widgets.push_back(new MacPicture(this, bounds, id, false));
 }
 
 void MacGui::MacDialogWindow::markRectAsDirty(Common::Rect r) {
@@ -1899,6 +1899,10 @@ bool MacLoomGui::runOptionsDialog() {
 	if (!sound)
 		window->setWidgetEnabled(3, false);
 
+	// Enable the slider drag handles
+	window->setWidgetEnabled(5, true);
+	window->setWidgetEnabled(9, true);
+
 	// TODO: I don't know where it gets the "Machine Speed" from. It doesn't
 	// appear to be VAR_MACHINE_SPEED, because I think that's only set to 1
 	// or 0, and may be the "Full Animation" setting.
@@ -3536,6 +3540,9 @@ bool MacIndy3Gui::runOptionsDialog() {
 	if (!sound)
 		window->setWidgetEnabled(3, false);
 
+	// Enable the slider handle
+	window->setWidgetEnabled(5, true);
+
 	window->addSubstitution(Common::String::format("%d", _vm->VAR(_vm->VAR_MACHINE_SPEED)));
 
 	// When quitting, the default action is not to not apply options


Commit: 8475cbda0b7877ea414f84129976717396228df4
    https://github.com/scummvm/scummvm/commit/8475cbda0b7877ea414f84129976717396228df4
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Attempt to save background before drawing Mac slider widget

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index d268e9c0d4d..57e9daddbaa 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -503,9 +503,19 @@ MacGui::MacPicture::~MacPicture() {
 		_picture->free();
 		delete _picture;
 	}
+
+	if (_background) {
+		_background->free();
+		delete _background;
+	}
 }
 
 void MacGui::MacPicture::draw(bool fullRedraw) {
+	if (_enabled && !_background) {
+		_background = new Graphics::Surface();
+		_background->create(_picture->w, _picture->h, Graphics::PixelFormat::createFormatCLUT8());
+		_background->copyRectToSurface(*(_window->innerSurface()), 0, 0, _bounds);
+	}
 	_window->drawSprite(_picture, _bounds.left, _bounds.top);
 }
 
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 0c5e718fbea..82a75fd6987 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -202,6 +202,7 @@ public:
 	class MacPicture : public MacWidget {
 	private:
 		Graphics::Surface *_picture = nullptr;
+		Graphics::Surface *_background = nullptr;
 
 	public:
 		MacPicture(MacGui::MacDialogWindow *window, Common::Rect bounds, int id, bool enabled);


Commit: 2935f247c36a9a60bd1e4e6b2d354760ea65ca6e
    https://github.com/scummvm/scummvm/commit/2935f247c36a9a60bd1e4e6b2d354760ea65ca6e
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Add Mac GUI slider widget

This is the first, and so far only, widget that doesn't correspond
directly to a single dialog item. Instead, it combines two picture
widgets (which are made invisible to keep them out of the way), neatly
solving my problem with keeping track of the background under the slider
handle.

Also rewrote the Mac dialog event loop for better accuracy (it is now
possible to click and hold down the mouse over a widget, move the mouse
out of it, and then move it back it again), and - hopefully - improved
flexibility.

Plus some other minor cleanups along the way.

Changed paths:
    engines/scumm/charset.cpp
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h
    engines/scumm/scumm.cpp
    engines/scumm/string.cpp


diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp
index 48f361edd7a..52d36a56b67 100644
--- a/engines/scumm/charset.cpp
+++ b/engines/scumm/charset.cpp
@@ -1777,7 +1777,7 @@ void CharsetRendererMac::printChar(int chr, bool ignoreCharsetMask) {
 	bool drawToTextBox = (vs->number == kTextVirtScreen && _vm->_game.id == GID_INDY3);
 
 	if (drawToTextBox)
-		((MacIndy3Gui *)_vm->_macGui)->printCharToTextArea(chr, macLeft, macTop, color);
+		_vm->_macGui->printCharToTextArea(chr, macLeft, macTop, color);
 	else
 		printCharInternal(chr, color, enableShadow, macLeft, macTop);
 
diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 57e9daddbaa..c0058a4c3e3 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -129,7 +129,7 @@ void ScummEngine::mac_drawStripToScreen(VirtScreen *vs, int top, int x, int y, i
 }
 
 void ScummEngine::mac_drawIndy3TextBox() {
-	Graphics::Surface *s = ((MacIndy3Gui *)_macGui)->textArea();
+	Graphics::Surface *s = _macGui->textArea();
 
 	// The first two rows of the text box are padding for font rendering.
 	// They are not drawn to the screen.
@@ -153,7 +153,7 @@ void ScummEngine::mac_drawIndy3TextBox() {
 }
 
 void ScummEngine::mac_undrawIndy3TextBox() {
-	Graphics::Surface *s = ((MacIndy3Gui *)_macGui)->textArea();
+	Graphics::Surface *s = _macGui->textArea();
 
 	int x = 96;
 	int y = 32;
@@ -222,25 +222,24 @@ Common::KeyState ScummEngine::mac_showOldStyleBannerAndPause(const char *msg, in
 // ---------------------------------------------------------------------------
 
 bool MacGui::MacWidget::findWidget(int x, int y) const {
-	return _enabled && _bounds.contains(x, y);
+	return _visible && _enabled && _bounds.contains(x, y);
 }
 
-void MacGui::MacWidget::setEnabled(bool enabled) {
-	_enabled = enabled;
-	if (_window->isVisible())
-		draw(true);
+void MacGui::MacWidget::setRedraw(bool fullRedraw) {
+	if (fullRedraw)
+		_fullRedraw = true;
+	else
+		_redraw = true;
 }
 
-void MacGui::MacWidget::setPressed(bool pressed) {
-	_pressed = pressed;
-	if (_window->isVisible())
-		draw();
+void MacGui::MacWidget::setEnabled(bool enabled) {
+	_enabled = enabled;
+	setRedraw(true);
 }
 
 void MacGui::MacWidget::setValue(int value) {
 	_value = value;
-	if (_window->isVisible())
-		draw();
+	setRedraw();
 }
 
 int MacGui::MacWidget::drawText(Common::String text, int x, int y, int w, Color color, Graphics::TextAlign align) {
@@ -326,7 +325,15 @@ int MacGui::MacWidget::drawText(Common::String text, int x, int y, int w, Color
 // Button widget
 // ---------------------------------------------------------------------------
 
-void MacGui::MacButton::draw(bool fullRedraw) {
+void MacGui::MacButton::draw(bool drawFocused) {
+	if (!_visible)
+		return;
+
+	if (!_redraw && !_fullRedraw)
+		return;
+
+	debug(1, "MacGui::MacButton: Drawing button %d (_fullRedraw = %d, drawFocused = %d, _value = %d)", _id, _fullRedraw, drawFocused, _value);
+
 	Graphics::Surface *s = _window->innerSurface();
 	Color fg, bg;
 	int x0, x1, x2, x3;
@@ -343,7 +350,7 @@ void MacGui::MacButton::draw(bool fullRedraw) {
 	s->vLine(_bounds.left, y0, y1, kBlack);
 	s->vLine(_bounds.right - 1, y0, y1, kBlack);
 
-	if (_pressed) {
+	if (drawFocused || (_window->getFocusedWidget() == this && _bounds.contains(_window->getMousePos()))) {
 		fg = kWhite;
 		bg = kBlack;
 	} else {
@@ -385,7 +392,9 @@ void MacGui::MacButton::draw(bool fullRedraw) {
 
 	drawText(_text, _bounds.left, _bounds.top + (_bounds.height() - font->getFontHeight()) / 2, _bounds.width(), fg, Graphics::kTextAlignCenter);
 
-	if (_isDefault && fullRedraw) {
+	Common::Rect bounds = _bounds;
+
+	if (_window->getDefaultWidget() == this && _fullRedraw) {
 		for (int i = 0; i < 3; i++) {
 			x0 = _bounds.left + 1;
 			x1 = _bounds.right - 2;
@@ -421,12 +430,13 @@ void MacGui::MacButton::draw(bool fullRedraw) {
 			s->hLine(x0, y1, x1, kBlack);
 			s->hLine(x2, y1, x3, kBlack);
 		}
-	}
 
-	// The first time, the whole window is redrawn so we don't have to
-	// mark the default button as any dirtier than a regular one.
+		bounds.grow(4);
+	}
 
-	_window->markRectAsDirty(_bounds);
+	_redraw = false;
+	_fullRedraw = false;
+	_window->markRectAsDirty(bounds);
 }
 
 // ---------------------------------------------------------------------------
@@ -446,14 +456,22 @@ MacGui::MacCheckbox::MacCheckbox(MacGui::MacDialogWindow *window, Common::Rect b
 }
 
 bool MacGui::MacCheckbox::findWidget(int x, int y) const {
-	return _enabled && _hitBounds.contains(x, y);
+	return _visible && _enabled && _hitBounds.contains(x, y);
 }
 
-void MacGui::MacCheckbox::draw(bool fullRedraw) {
+void MacGui::MacCheckbox::draw(bool drawFocused) {
+	if (!_visible)
+		return;
+
+	if (!_redraw && !_fullRedraw)
+		return;
+
+	debug(1, "MacGui::MacCheckbox: Drawing checkbox %d (_fullRedraw = %d, drawFocused = %d, _value = %d)", _id, _fullRedraw, drawFocused, _value);
+
 	Graphics::Surface *s = _window->innerSurface();
 	Common::Rect box(_hitBounds.left + 2, _hitBounds.top + 2, _hitBounds.left + 14, _hitBounds.top + 14);
 
-	if (fullRedraw) {
+	if (_fullRedraw) {
 		_window->innerSurface()->fillRect(_bounds, kWhite);
 
 		int x = _hitBounds.left + 18;
@@ -461,33 +479,50 @@ void MacGui::MacCheckbox::draw(bool fullRedraw) {
 
 		drawText(_text, x, y, _hitBounds.right - x, kBlack);
 		_window->markRectAsDirty(_bounds);
-	} else {
+	} else
 		_window->markRectAsDirty(box);
-	}
 
 	s->fillRect(box, kBlack);
-	box.grow(_pressed ? -2 : -1);
+	if (drawFocused || (_window->getFocusedWidget() == this && _hitBounds.contains(_window->getMousePos()))) {
+		box.grow(-2);
+	} else {
+		box.grow(-1);
+	}
 	s->fillRect(box, kWhite);
 
 	if (_value && _enabled) {
 		s->drawLine(box.left, box.top, box.right - 1, box.bottom - 1, kBlack);
 		s->drawLine(box.left, box.bottom - 1, box.right - 1, box.top, kBlack);
 	}
+
+	_redraw = false;
+	_fullRedraw = false;
 }
 
-void MacGui::MacCheckbox::action() {
+void MacGui::MacCheckbox::handleMouseUp() {
 	_value = _value ? 0 : 1;
-	draw();
+	setRedraw();
 }
 
 // ---------------------------------------------------------------------------
 // Static text widget
 // ---------------------------------------------------------------------------
 
-void MacGui::MacText::draw(bool fullRedraw) {
+void MacGui::MacText::draw(bool drawFocused) {
+	if (!_visible)
+		return;
+
+	if (!_redraw && !_fullRedraw)
+		return;
+
+	debug(1, "MacGui::MacText: Drawing text %d (_fullRedraw = %d, drawFocused = %d, _value = %d)", _id, _fullRedraw, drawFocused, _value);
+
 	_window->innerSurface()->fillRect(_bounds, kWhite);
 	drawText(_text, _bounds.left, _bounds.top, _bounds.width(), kBlack);
 	_window->markRectAsDirty(_bounds);
+
+	_redraw = false;
+	_fullRedraw = false;
 }
 
 // ---------------------------------------------------------------------------
@@ -503,20 +538,105 @@ MacGui::MacPicture::~MacPicture() {
 		_picture->free();
 		delete _picture;
 	}
+}
 
-	if (_background) {
-		_background->free();
-		delete _background;
-	}
+void MacGui::MacPicture::draw(bool drawFocused) {
+	if (!_visible)
+		return;
+
+	if (!_redraw && !_fullRedraw)
+		return;
+
+	debug(1, "MacGui::MacPicture: Drawing picture %d (_fullRedraw = %d, drawFocused = %d, _value = %d)", _id, _fullRedraw, drawFocused, _value);
+
+	_window->drawSprite(_picture, _bounds.left, _bounds.top);
+
+	_redraw = false;
+	_fullRedraw = false;
 }
 
-void MacGui::MacPicture::draw(bool fullRedraw) {
-	if (_enabled && !_background) {
-		_background = new Graphics::Surface();
-		_background->create(_picture->w, _picture->h, Graphics::PixelFormat::createFormatCLUT8());
-		_background->copyRectToSurface(*(_window->innerSurface()), 0, 0, _bounds);
+// ---------------------------------------------------------------------------
+// Slider widget
+// ---------------------------------------------------------------------------
+
+bool MacGui::MacSlider::findWidget(int x, int y) const {
+	if (!_visible || !_enabled)
+		return false;
+
+	// Once we start dragging the handle, any mouse position is considered
+	// within the widget.
+
+	if (_window->getFocusedWidget() == this)
+		return true;
+
+	return _bounds.contains(x, y);
+}
+
+void MacGui::MacSlider::setValue(int value) {
+	MacWidget::setValue(CLIP(value, _minValue, _maxValue));
+
+	int valueRange = _maxValue - _minValue;
+	int valueOffset = _value - _minValue;
+
+	int posRange = (_maxX - _rightMargin) - (_minX + _leftMargin);
+	int posOffset = (valueRange / 2 + posRange * valueOffset) / valueRange;
+
+	_handleX = _minX + _leftMargin + posOffset;
+	setRedraw();
+}
+
+void MacGui::MacSlider::draw(bool drawFocused) {
+	if (!_redraw && !_fullRedraw)
+		return;
+
+	debug(1, "MacGui::MacPicture: Drawing slider %d (_fullRedraw = %d, drawFocused = %d, _value = %d)", _id, _fullRedraw, drawFocused, _value);
+
+	Graphics::Surface *bgSprite = _background->getPicture();
+	Graphics::Surface *hSprite = _handle->getPicture();
+
+	if (_fullRedraw)
+		_window->drawSprite(bgSprite, _bounds.left, _bounds.top);
+
+	int handleY = _handle->getBounds().top - _bounds.top;
+
+	if (_lastHandleX != -1 && !_fullRedraw) {
+		Graphics::Surface bg = bgSprite->getSubArea(Common::Rect(_lastHandleX, handleY, _lastHandleX + hSprite->w, handleY + hSprite->h));
+
+		_window->drawSprite(&bg, _bounds.left + _lastHandleX, _bounds.top + handleY);
 	}
-	_window->drawSprite(_picture, _bounds.left, _bounds.top);
+
+	_window->drawSprite(hSprite, _bounds.left + _handleX, _bounds.top + handleY);
+	_lastHandleX = _handleX;
+
+	_redraw = false;
+	_fullRedraw = false;
+}
+
+void MacGui::MacSlider::handleMouseDown() {
+	int mouseX = _window->getMousePos().x - _bounds.left;
+	int handleWidth = _handle->getBounds().width();
+
+	if (mouseX >= _handleX && mouseX < _handleX + handleWidth)
+		_grabOffset = _window->getMousePos().x - _bounds.left - _handleX;
+	else
+		_grabOffset = handleWidth / 2;
+
+	handleMouseMove();
+}
+
+void MacGui::MacSlider::handleMouseUp() {
+	int posRange = (_maxX - _rightMargin) - (_minX + _rightMargin);
+	int posOffset = _handleX - (_minX + _leftMargin);
+
+	int valueRange = _maxValue - _minValue;
+	int valueOffset = (posRange / 2 + valueRange * posOffset) / posRange;
+
+	setValue(_minValue + valueOffset);
+}
+
+void MacGui::MacSlider::handleMouseMove() {
+	_handleX = CLIP<int>(_window->getMousePos().x - _bounds.left - _grabOffset, _minX, _maxX);
+	setRedraw();
 }
 
 // ---------------------------------------------------------------------------
@@ -600,6 +720,26 @@ void MacGui::MacDialogWindow::show() {
 	_dirtyRects.clear();
 }
 
+void MacGui::MacDialogWindow::setFocusedWidget(int x, int y) {
+	int nr = findWidget(x, y);
+	if (nr >= 0) {
+		_focusedWidget = _widgets[nr];
+		_focusClick.x = x;
+		_focusClick.y = y;
+		_focusedWidget->setRedraw();
+	} else
+		clearFocusedWidget();
+}
+
+void MacGui::MacDialogWindow::clearFocusedWidget() {
+	if (_focusedWidget) {
+		_focusedWidget->setRedraw();
+		_focusedWidget = nullptr;
+		_focusClick.x = -1;
+		_focusClick.y = -1;
+	}
+}
+
 int MacGui::MacDialogWindow::findWidget(int x, int y) const {
 	for (uint i = 0; i < _widgets.size(); i++) {
 		if (_widgets[i]->findWidget(x, y))
@@ -625,11 +765,24 @@ void MacGui::MacDialogWindow::addPicture(Common::Rect bounds, int id, bool enabl
 	_widgets.push_back(new MacPicture(this, bounds, id, false));
 }
 
+void MacGui::MacDialogWindow::addSlider(int backgroundId, int handleId, bool enabled, int minX, int maxX, int minValue, int maxValue, int leftMargin, int rightMargin) {
+	MacPicture *background = (MacPicture *)_widgets[backgroundId];
+	MacPicture *handle = (MacPicture *)_widgets[handleId];
+
+	background->setVisible(false);
+	handle->setVisible(false);
+
+	_widgets.push_back(new MacSlider(this, background, handle, enabled, minX, maxX, minValue, maxValue, leftMargin, rightMargin));
+}
+
 void MacGui::MacDialogWindow::markRectAsDirty(Common::Rect r) {
 	_dirtyRects.push_back(r);
 }
 
 void MacGui::MacDialogWindow::update(bool fullRedraw) {
+	for (uint i = 0; i < _widgets.size(); i++)
+		_widgets[i]->draw();
+
 	if (fullRedraw) {
 		_dirtyRects.clear();
 		markRectAsDirty(Common::Rect(0, 0, _innerSurface.w, _innerSurface.h));
@@ -657,18 +810,16 @@ void MacGui::MacDialogWindow::fillPattern(Common::Rect r, uint16 pattern) {
 	markRectAsDirty(r);
 }
 
-void MacGui::MacDialogWindow::drawSprite(const Graphics::Surface *sprite, int x, int y) {
-	_innerSurface.copyRectToSurface(*sprite, x, y, Common::Rect(0, 0, sprite->w, sprite->h));
-	markRectAsDirty(Common::Rect(x, y, x + sprite->w, y + sprite->h));
-}
-
 int MacGui::MacDialogWindow::runDialog() {
-	show();
+	if (!_visible) {
+		show();
 
-	for (uint i = 0; i < _widgets.size(); i++)
-		_widgets[i]->draw(true);
-
-	int pressedWidget = -1;
+		for (uint i = 0; i < _widgets.size(); i++) {
+			_widgets[i]->setId(i);
+			_widgets[i]->setRedraw(true);
+			_widgets[i]->draw();
+		}
+	}
 
 	// Run the dialog until something interesting happens to a widget. It's
 	// up to the caller to repeat the calls to runDialog() until the dialog
@@ -676,33 +827,74 @@ int MacGui::MacDialogWindow::runDialog() {
 
 	while (!_gui->_vm->shouldQuit()) {
 		Common::Event event;
+		bool buttonPressed = false;
+		int widgetId = -1;
 
 		while (_system->getEventManager()->pollEvent(event)) {
 			if (Common::isMouseEvent(event)) {
 				event.mouse.x -= (_bounds.left + _margin);
 				event.mouse.y -= (_bounds.top + _margin);
+
+				_oldMousePos = _mousePos;
+
+				_mousePos.x = event.mouse.x;
+				_mousePos.y = event.mouse.y;
 			}
 
 			switch (event.type) {
 			case Common::EVENT_LBUTTONDOWN:
-				pressedWidget = findWidget(event.mouse.x, event.mouse.y);
-				if (pressedWidget != -1)
-					_widgets[pressedWidget]->setPressed(true);
+				buttonPressed = true;
+				setFocusedWidget(event.mouse.x, event.mouse.y);
+				if (_focusedWidget)
+					_focusedWidget->handleMouseDown();
 				break;
 
 			case Common::EVENT_LBUTTONUP:
-				if (pressedWidget != -1) {
-					_widgets[pressedWidget]->setPressed(false);
-					_widgets[pressedWidget]->action();
-					return pressedWidget;
+				buttonPressed = false;
+
+				if (_focusedWidget && _focusedWidget->findWidget(event.mouse.x, event.mouse.y)) {
+					widgetId = _focusedWidget->getId();
+					_focusedWidget->handleMouseUp();
+					clearFocusedWidget();
+					return widgetId;
 				}
+
+				clearFocusedWidget();
 				break;
 
 			case Common::EVENT_MOUSEMOVE:
-				if (pressedWidget != -1) {
-					if (!_widgets[pressedWidget]->findWidget(event.mouse.x, event.mouse.y)) {
-						_widgets[pressedWidget]->setPressed(false);
-						pressedWidget = -1;
+				if (_focusedWidget) {
+					if (_focusedWidget->findWidget(_oldMousePos.x, _oldMousePos.y) != _focusedWidget->findWidget(_mousePos.x, _mousePos.y)) {
+						_focusedWidget->setRedraw();
+					}
+
+					_focusedWidget->handleMouseMove();
+				}
+				break;
+
+			case Common::EVENT_KEYDOWN:
+				if (!buttonPressed && event.kbd.keycode == Common::KEYCODE_RETURN) {
+					MacWidget *widget = getDefaultWidget();
+					if (widget && widget->isEnabled()) {
+						widget->setRedraw();
+						widget->draw(true);
+						update();
+
+						for (int i = 0; i < 10; i++) {
+							_system->delayMillis(10);
+							_system->updateScreen();
+						}
+
+						widget->setRedraw();
+						widget->draw();
+						update();
+
+						for (int i = 0; i < 10; i++) {
+							_system->delayMillis(10);
+							_system->updateScreen();
+						}
+
+						return widget->getId();
 					}
 				}
 				break;
@@ -720,6 +912,11 @@ int MacGui::MacDialogWindow::runDialog() {
 	return -1;
 }
 
+void MacGui::MacDialogWindow::drawSprite(const Graphics::Surface *sprite, int x, int y) {
+	_innerSurface.copyRectToSurface(*sprite, x, y, Common::Rect(0, 0, sprite->w, sprite->h));
+	markRectAsDirty(Common::Rect(x, y, x + sprite->w, y + sprite->h));
+}
+
 void MacGui::MacDialogWindow::drawSprite(const Graphics::Surface *sprite, int x, int y, Common::Rect(clipRect)) {
 	Common::Rect subRect(0, 0, sprite->w, sprite->h);
 
@@ -1161,7 +1358,7 @@ Graphics::Surface *MacGui::decodePictV1(Common::SeekableReadStream *res) {
 			break;
 
 		default:
-			debug("decodePictV1: Unknown opcode: 0x%02x", opcode);
+			warning("decodePictV1: Unknown opcode: 0x%02x", opcode);
 			break;
 		}
 	}
@@ -1496,8 +1693,7 @@ MacLoomGui::MacLoomGui(ScummEngine *vm, Common::String resourceFile) : MacGui(vm
 	// The practice box can be moved, but this is its default position on
 	// a large screen, and it's not saved.
 
-	_practiceBoxX = 215;
-	_practiceBoxY = 376;
+	_practiceBoxPos = Common::Point(215, 376);
 }
 
 MacLoomGui::~MacLoomGui() {
@@ -1885,19 +2081,23 @@ bool MacLoomGui::runOptionsDialog() {
 	// 1 - Cancel button
 	// 2 - Sound checkbox
 	// 3 - Music checkbox
-	// 4 - Picture
-	// 5 - Picture
+	// 4 - Picture (text speed background)
+	// 5 - Picture (text speed handle)
 	// 6 - Scrolling checkbox
 	// 7 - Full Animation checkbox
-	// 8 - Picture
-	// 9 - Picture
+	// 8 - Picture (music quality background)
+	// 9 - Picture (music quality handle)
 	// 10 - "Machine Speed:  ^0" text
+	// 11 - Text speed slider (manually created)
+	// 12 - Music quality slider (manually created)
 
 	// TODO: Get these from the SCUMM engine
 	int sound = 0;
 	int music = 0;
 	int scrolling = 0;
 	int fullAnimation = 0;
+	int textSpeed = 75;
+	int musicQuality = 2;
 
 	MacDialogWindow *window = createDialog(1000);
 
@@ -1909,9 +2109,12 @@ bool MacLoomGui::runOptionsDialog() {
 	if (!sound)
 		window->setWidgetEnabled(3, false);
 
-	// Enable the slider drag handles
-	window->setWidgetEnabled(5, true);
-	window->setWidgetEnabled(9, true);
+	// TODO: What is the range of the text speed setting?
+	window->addSlider(4, 5, true, 5, 105, 50, 100);
+	window->setWidgetValue(11, textSpeed);
+
+	window->addSlider(8, 9, true, 5, 69, 0, 2, 6, 4);
+	window->setWidgetValue(12, musicQuality);
 
 	// TODO: I don't know where it gets the "Machine Speed" from. It doesn't
 	// appear to be VAR_MACHINE_SPEED, because I think that's only set to 1
@@ -1933,9 +2136,8 @@ bool MacLoomGui::runOptionsDialog() {
 		if (clicked == 1)
 			break;
 
-		if (clicked == 2) {
+		if (clicked == 2)
 			window->setWidgetEnabled(3, window->getWidgetValue(2) != 0);
-		}
 	}
 
 	if (ret) {
@@ -1943,6 +2145,8 @@ bool MacLoomGui::runOptionsDialog() {
 		debug("music: %d", window->getWidgetValue(3));
 		debug("scrolling: %d", window->getWidgetValue(6));
 		debug("full animation: %d", window->getWidgetValue(7));
+		debug("text speed: %d", window->getWidgetValue(11));
+		debug("music quality: %d", window->getWidgetValue(12));
 	}
 
 	delete window;
@@ -2026,7 +2230,7 @@ void MacLoomGui::update(int delta) {
 				}
 			}
 
-			_system->copyRectToScreen(_practiceBox->getBasePtr(0, 0), _practiceBox->pitch, _practiceBoxX, _practiceBoxY, w, h);
+			_system->copyRectToScreen(_practiceBox->getBasePtr(0, 0), _practiceBox->pitch, _practiceBoxPos.x, _practiceBoxPos.y, w, h);
 		} else {
 			if (_practiceBox) {
 				debug(1, "MacLoomGui: Deleting practice mode box");
@@ -2038,7 +2242,7 @@ void MacLoomGui::update(int delta) {
 				delete _practiceBox;
 				_practiceBox = nullptr;
 
-				_system->copyRectToScreen(_surface->getBasePtr(_practiceBoxX, _practiceBoxY), _surface->pitch, _practiceBoxX, _practiceBoxY, w, h);
+				_system->copyRectToScreen(_surface->getBasePtr(_practiceBoxPos.x, _practiceBoxPos.y), _surface->pitch, _practiceBoxPos.x, _practiceBoxPos.y, w, h);
 			}
 		}
 	}
@@ -2064,10 +2268,10 @@ bool MacLoomGui::handleEvent(Common::Event &event) {
 
 	Common::Rect bounds;
 
-	bounds.left = _practiceBoxX;
-	bounds.top = _practiceBoxY;
-	bounds.right = _practiceBoxX + _practiceBox->w;
-	bounds.bottom = _practiceBoxY + _practiceBox->h;
+	bounds.left = _practiceBoxPos.x;
+	bounds.top = _practiceBoxPos.y;
+	bounds.right = _practiceBoxPos.x + _practiceBox->w;
+	bounds.bottom = _practiceBoxPos.y + _practiceBox->h;
 
 	if (!bounds.contains(event.mouse))
 		return false;
@@ -2120,12 +2324,13 @@ bool MacLoomGui::handleEvent(Common::Event &event) {
 			newY = CLIP(newY, 23, _surface->h - _practiceBox->h);
 
 			// For some reason, X coordinates can only change in
-			// increments of 16 pixels.
+			// increments of 16 pixels. As an enhancement, we allow
+			// any X coordinate.
 
 			if (!_vm->_enableEnhancements)
 				newX &= ~0xF;
 
-			if (newX != _practiceBoxX || newY != _practiceBoxY) {
+			if (newX != _practiceBoxPos.x || newY != _practiceBoxPos.y) {
 				int w = _practiceBox->w;
 				int h = _practiceBox->h;
 
@@ -2136,14 +2341,13 @@ bool MacLoomGui::handleEvent(Common::Event &event) {
 				// of the copyRectToScreen() calls completely,
 				// so I doubt it's worth it.
 
-				_system->copyRectToScreen(_surface->getBasePtr(_practiceBoxX, _practiceBoxY), _surface->pitch, _practiceBoxX, _practiceBoxY, w, h);
+				_system->copyRectToScreen(_surface->getBasePtr(_practiceBoxPos.x, _practiceBoxPos.y), _surface->pitch, _practiceBoxPos.x, _practiceBoxPos.y, w, h);
 				_system->copyRectToScreen(_practiceBox->getBasePtr(0, 0), _practiceBox->pitch, newX, newY, w, h);
 
-				_practiceBoxX = newX;
-				_practiceBoxY = newY;
+				_practiceBoxPos = Common::Point(newX, newY);
 			}
 
-			_system->delayMillis(25);
+			_system->delayMillis(20);
 			_system->updateScreen();
 		}
 	}
@@ -3530,16 +3734,18 @@ bool MacIndy3Gui::runOptionsDialog() {
 	// 1 - Cancel button
 	// 2 - Sound checkbox
 	// 3 - Music checkbox
-	// 4 - Picture
-	// 5 - Picture
+	// 4 - Picture (text speed background)
+	// 5 - Picture (text speed handle)
 	// 6 - "Machine speed rating:" text
 	// 7 - "^0" text
 	// 8 - Scrolling checkbox
+	// 9 - Text speed slider (manually created)
 
 	// TODO: Get these from the SCUMM engine
 	int sound = 0;
 	int music = 0;
 	int scrolling = 0;
+	int textSpeed = 75;
 
 	MacDialogWindow *window = createDialog(1000);
 
@@ -3550,8 +3756,9 @@ bool MacIndy3Gui::runOptionsDialog() {
 	if (!sound)
 		window->setWidgetEnabled(3, false);
 
-	// Enable the slider handle
-	window->setWidgetEnabled(5, true);
+	// TODO: What is the range of the text speed setting?
+	window->addSlider(4, 5, true, 5, 105, 50, 100);
+	window->setWidgetValue(9, textSpeed);
 
 	window->addSubstitution(Common::String::format("%d", _vm->VAR(_vm->VAR_MACHINE_SPEED)));
 
@@ -3569,15 +3776,15 @@ bool MacIndy3Gui::runOptionsDialog() {
 		if (clicked == 1)
 			break;
 
-		if (clicked == 2) {
+		if (clicked == 2)
 			window->setWidgetEnabled(3, window->getWidgetValue(2) != 0);
-		}
 	}
 
 	if (ret) {
 		debug("sound: %d", window->getWidgetValue(2));
 		debug("music: %d", window->getWidgetValue(3));
 		debug("scrolling: %d", window->getWidgetValue(8));
+		debug("textSpeed: %d", window->getWidgetValue(9));
 	}
 
 	delete window;
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 82a75fd6987..1a33829e578 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -136,10 +136,15 @@ public:
 	class MacWidget {
 	protected:
 		MacGui::MacDialogWindow *_window;
+		int _id = -1;
+
 		Common::Rect _bounds;
-		Common::String _text;
+		bool _visible = true;
+		bool _redraw = false;
+		bool _fullRedraw = false;
 		bool _enabled = true;
-		bool _pressed = false;
+
+		Common::String _text;
 		int _value = 0;
 
 		int drawText(Common::String text, int x, int y, int w, Color color, Graphics::TextAlign align = Graphics::kTextAlignLeft);
@@ -148,31 +153,37 @@ public:
 		MacWidget(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::String text, bool enabled) : _window(window), _bounds(bounds), _text(text), _enabled(enabled) {}
 		virtual ~MacWidget() {};
 
+		void setId(int id) { _id = id; }
+		int getId() const { return _id; }
+
+		// Visibility never changes after initialization, so it does
+		// not trigger a redraw.
+		void setVisible(bool visible) { _visible = visible; }
+		bool isVisible() const { return _visible; }
+
+		Common::Rect getBounds() const { return _bounds; }
+
+		void setRedraw(bool fullRedraw = false);
+
 		bool isEnabled() const { return _enabled; }
 		void setEnabled(bool enabled);
 
-		void setPressed(bool pressed);
-
-		void setValue(int value);
+		virtual void setValue(int value);
 		int getValue() const { return _value; }
 
-		virtual void makeDefaultWidget() {}
 		virtual bool findWidget(int x, int y) const;
 
-		virtual void draw(bool fullRedraw = false) = 0;
-		virtual void action() {}
+		virtual void draw(bool drawFocused = false) = 0;
+		virtual void handleMouseDown() {}
+		virtual void handleMouseUp() {}
+		virtual void handleMouseMove() {}
 	};
 
 	class MacButton : public MacWidget {
-	private:
-		bool _isDefault = false;
-
 	public:
 		MacButton(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::String text, bool enabled) : MacWidget(window, bounds, text, enabled) {}
 
-		void makeDefaultWidget() { _isDefault = true; }
-
-		void draw(bool fullRedraw = false);
+		void draw(bool drawFocused = false);
 	};
 
 	class MacCheckbox : public MacWidget {
@@ -184,8 +195,8 @@ public:
 		MacCheckbox(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::String text, bool enabled);
 
 		bool findWidget(int x, int y) const;
-		void draw(bool fullRedraw = false);
-		void action();
+		void draw(bool drawFocused = false);
+		void handleMouseUp();
 	};
 
 	// The dialogs add texts as disabled, but we don't want it to be drawn
@@ -196,19 +207,50 @@ public:
 	public:
 		MacText(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::String text, bool enabled) : MacWidget(window, bounds, text, true) {}
 		bool findWidget(int x, int y) const { return false; }
-		void draw(bool fullRedraw = false);
+		void draw(bool drawFocused = false);
 	};
 
 	class MacPicture : public MacWidget {
 	private:
 		Graphics::Surface *_picture = nullptr;
-		Graphics::Surface *_background = nullptr;
 
 	public:
 		MacPicture(MacGui::MacDialogWindow *window, Common::Rect bounds, int id, bool enabled);
 		~MacPicture();
 
-		void draw(bool fullRedraw = false);
+		Graphics::Surface *getPicture() const { return _picture; }
+
+		void draw(bool drawFocused = false);
+	};
+
+	class MacSlider : public MacWidget {
+	private:
+		MacPicture *_background;
+		MacPicture *_handle;
+		int _minX;
+		int _maxX;
+		int _handleX;
+		int _grabOffset;
+		int _lastHandleX = -1;
+		int _minValue;
+		int _maxValue;
+		int _leftMargin;
+		int _rightMargin;
+
+	public:
+		 MacSlider(MacGui::MacDialogWindow *window, MacPicture *background, MacPicture *handle, bool enabled, int minX, int maxX, int minValue, int maxValue, int leftMargin, int rightMargin)
+			: MacWidget(window, background->getBounds(), "Slider", enabled),
+			_background(background), _handle(handle), _minX(minX),
+			_maxX(maxX), _minValue(minValue), _maxValue(maxValue),
+			_leftMargin(leftMargin), _rightMargin(rightMargin) {}
+
+		bool findWidget(int x, int y) const;
+		void setValue(int value);
+		void draw(bool drawFocused = false);
+
+		void handleMouseDown();
+		void handleMouseUp();
+		void handleMouseMove();
 	};
 
 	class MacDialogWindow {
@@ -227,9 +269,16 @@ public:
 		Graphics::Surface _surface;
 		Graphics::Surface _innerSurface;
 
-		Common::StringArray _substitutions;
-
 		Common::Array<MacWidget *> _widgets;
+
+		MacWidget *_defaultWidget = nullptr;
+
+		MacWidget *_focusedWidget = nullptr;
+		Common::Point _focusClick;
+		Common::Point _oldMousePos;
+		Common::Point _mousePos;
+
+		Common::StringArray _substitutions;
 		Common::Array<Common::Rect> _dirtyRects;
 
 		void copyToScreen(Graphics::Surface *s = nullptr) const;
@@ -248,18 +297,28 @@ public:
 		void show();
 		int runDialog();
 
-		void setDefaultWidget(int nr) { _widgets[nr]->makeDefaultWidget(); }
+		void setDefaultWidget(int nr) { _defaultWidget = _widgets[nr]; }
+		MacWidget *getDefaultWidget() const { return _defaultWidget; }
+
+		void setFocusedWidget(int x, int y);
+		void clearFocusedWidget();
+		MacWidget *getFocusedWidget() const { return _focusedWidget; }
+		Common::Point getFocusClick() const { return _focusClick; }
+		Common::Point getMousePos() const { return _mousePos; }
+
 		void setWidgetEnabled(int nr, bool enabled) { _widgets[nr]->setEnabled(enabled); }
 		bool isWidgetEnabled(int nr) const { return _widgets[nr]->isEnabled(); }
+		void setWidgetVisible(int nr, bool visible) { _widgets[nr]->setVisible(visible); }
 		int getWidgetValue(int nr) const { return _widgets[nr]->getValue(); }
 		void setWidgetValue(int nr, int value) { _widgets[nr]->setValue(value); }
 		int findWidget(int x, int y) const;
-		void redrawWidget(int nr) { _widgets[nr]->draw(); }
+		void redrawWidget(int nr) { _widgets[nr]->setRedraw(true); }
 
 		void addButton(Common::Rect bounds, Common::String text, bool enabled);
 		void addCheckbox(Common::Rect bounds, Common::String text, bool enabled);
 		void addText(Common::Rect bounds, Common::String text, bool enabled);
 		void addPicture(Common::Rect bounds, int id, bool enabled);
+		void addSlider(int backgroundId, int handleId, bool enabled, int minX, int maxX, int minValue, int maxValue, int leftMargin = 0, int rightMargin = 0);
 
 		void addSubstitution(Common::String text) { _substitutions.push_back(text); }
 		void replaceSubstitution(int nr, Common::String text) { _substitutions[nr] = text; }
@@ -306,6 +365,11 @@ public:
 
 	virtual void setupCursor(int &width, int &height, int &hotspotX, int &hotspotY, int &animate) = 0;
 
+	virtual Graphics::Surface *textArea() { return nullptr; }
+	virtual void clearTextArea() {}
+	virtual void initTextAreaForActor(Actor *a, byte color) {}
+	virtual void printCharToTextArea(int chr, int x, int y, int color) {}
+
 	void setPalette(const byte *palette, uint size);
 
 	MacDialogWindow *drawBanner(char *message);
@@ -344,8 +408,7 @@ protected:
 
 private:
 	Graphics::Surface *_practiceBox = nullptr;
-	int _practiceBoxX;
-	int _practiceBoxY;
+	Common::Point _practiceBoxPos;
 	int _practiceBoxNotes;
 };
 
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index c1f0b755e99..9bedbc3bb6a 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -1699,8 +1699,7 @@ void ScummEngine::resetScumm() {
 	}
 
 	if (_macGui) {
-		if (_game.id == GID_INDY3)
-			((MacIndy3Gui *)_macGui)->clearTextArea();
+		_macGui->clearTextArea();
 		_macGui->reset();
 	}
 
diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp
index 0c152ffb92f..4bbb6699c93 100644
--- a/engines/scumm/string.cpp
+++ b/engines/scumm/string.cpp
@@ -1141,7 +1141,7 @@ void ScummEngine::CHARSET_1() {
 
 		if (createTextBox) {
 			if (!_keepText)
-				((MacIndy3Gui *)_macGui)->initTextAreaForActor(a, _charset->getColor());
+				_macGui->initTextAreaForActor(a, _charset->getColor());
 			createTextBox = false;
 			drawTextBox = true;
 		}


Commit: d6eb449a0d7fb0f827491e09ce86fb1f8861ea67
    https://github.com/scummvm/scummvm/commit/d6eb449a0d7fb0f827491e09ce86fb1f8861ea67
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Change talk speed range to 0-9 for Mac GUI

This is what Bosca tell me it shold be. The old range was just for
testing.

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index c0058a4c3e3..9aac2c66bdc 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -2096,7 +2096,7 @@ bool MacLoomGui::runOptionsDialog() {
 	int music = 0;
 	int scrolling = 0;
 	int fullAnimation = 0;
-	int textSpeed = 75;
+	int textSpeed = 5;
 	int musicQuality = 2;
 
 	MacDialogWindow *window = createDialog(1000);
@@ -2109,8 +2109,7 @@ bool MacLoomGui::runOptionsDialog() {
 	if (!sound)
 		window->setWidgetEnabled(3, false);
 
-	// TODO: What is the range of the text speed setting?
-	window->addSlider(4, 5, true, 5, 105, 50, 100);
+	window->addSlider(4, 5, true, 5, 105, 0, 9);
 	window->setWidgetValue(11, textSpeed);
 
 	window->addSlider(8, 9, true, 5, 69, 0, 2, 6, 4);
@@ -3745,7 +3744,7 @@ bool MacIndy3Gui::runOptionsDialog() {
 	int sound = 0;
 	int music = 0;
 	int scrolling = 0;
-	int textSpeed = 75;
+	int textSpeed = 5;
 
 	MacDialogWindow *window = createDialog(1000);
 
@@ -3756,8 +3755,7 @@ bool MacIndy3Gui::runOptionsDialog() {
 	if (!sound)
 		window->setWidgetEnabled(3, false);
 
-	// TODO: What is the range of the text speed setting?
-	window->addSlider(4, 5, true, 5, 105, 50, 100);
+	window->addSlider(4, 5, true, 5, 105, 0, 9);
 	window->setWidgetValue(9, textSpeed);
 
 	window->addSubstitution(Common::String::format("%d", _vm->VAR(_vm->VAR_MACHINE_SPEED)));


Commit: a0ef5cebfa69ee63742147b8c4e783b834e85ed9
    https://github.com/scummvm/scummvm/commit/a0ef5cebfa69ee63742147b8c4e783b834e85ed9
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Add mouse wheel support to Indy 3 Mac inventory as an enhancement

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 9aac2c66bdc..c0c8ec59edf 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -2718,7 +2718,21 @@ void MacIndy3Gui::Inventory::setRedraw(bool redraw) {
 }
 
 bool MacIndy3Gui::Inventory::handleEvent(Common::Event &event) {
-	if (!_enabled || !_verbid || event.type != Common::EVENT_LBUTTONDOWN)
+	if (!_enabled || !_verbid)
+		return false;
+
+	if (_vm->_enableEnhancements) {
+		if ((event.type == Common::EVENT_WHEELUP || event.type == Common::EVENT_WHEELDOWN) && _bounds.contains(event.mouse.x, event.mouse.y)) {
+			if (event.type == Common::EVENT_WHEELUP) {
+				_scrollBar->scroll(kScrollUp);
+			} else {
+				_scrollBar->scroll(kScrollDown);
+			}
+			return true;
+		}
+	}
+
+	if (event.type != Common::EVENT_LBUTTONDOWN)
 		return false;
 
 	for (int i = 0; i < ARRAYSIZE(_slots); i++) {
@@ -3996,7 +4010,7 @@ bool MacIndy3Gui::handleEvent(Common::Event &event) {
 	}
 
 	// It probably doesn't make much of a difference, but if a widget
-	// responds to an event, and marks itself as wanting to be redraw,
+	// responds to an event, and marks itself as wanting to be redrawn,
 	// we do that redrawing immediately, not on the next update.
 
 	for (auto &it: _widgets) {


Commit: 00c66e2abc7c0f53b70f841e23b214b524be05f0
    https://github.com/scummvm/scummvm/commit/00c66e2abc7c0f53b70f841e23b214b524be05f0
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Add token base class for all Mac widgets

That way I can at least pretend that I don't have two completely
separate widget implementations.

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index c0c8ec59edf..e464fb12ba9 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -2386,11 +2386,7 @@ ScummEngine *MacIndy3Gui::Widget::_vm = nullptr;
 Graphics::Surface *MacIndy3Gui::Widget::_surface = nullptr;
 MacIndy3Gui *MacIndy3Gui::Widget::_gui = nullptr;
 
-MacIndy3Gui::Widget::Widget(int x, int y, int width, int height) {
-	_bounds.left = x;
-	_bounds.top = y;
-	_bounds.right = x + width;
-	_bounds.bottom = y + height;
+MacIndy3Gui::Widget::Widget(int x, int y, int width, int height) : MacGuiObject(Common::Rect(x, y, x + width, y + height), false) {
 }
 
 void MacIndy3Gui::Widget::reset() {
@@ -2875,8 +2871,8 @@ void MacIndy3Gui::Inventory::draw() {
 			ScrollButton *s = _scrollButtons[i];
 			const uint16 *arrow = (s->_direction == kScrollUp) ? upArrow : downArrow;
 
-			drawShadowFrame(s->_bounds, kWhite, kTransparency);
-			drawBitmap(s->_bounds, arrow, kBlack);
+			drawShadowFrame(s->getBounds(), kWhite, kTransparency);
+			drawBitmap(s->getBounds(), arrow, kBlack);
 			s->draw();
 		}
 	}
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 1a33829e578..ae380e90697 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -36,6 +36,19 @@ namespace Scumm {
 class ScummEngine;
 class Actor;
 
+class MacGuiObject {
+protected:
+	bool _redraw = false;
+	bool _enabled = false;
+	Common::Rect _bounds;
+
+public:
+	MacGuiObject(Common::Rect bounds, bool enabled) : _bounds(bounds), _enabled(enabled) {}
+	virtual ~MacGuiObject() {}
+
+	Common::Rect getBounds() const { return _bounds; }
+};
+
 class MacGui {
 protected:
 	ScummEngine *_vm = nullptr;
@@ -133,16 +146,13 @@ protected:
 public:
 	class MacDialogWindow;
 
-	class MacWidget {
+	class MacWidget : public MacGuiObject {
 	protected:
 		MacGui::MacDialogWindow *_window;
 		int _id = -1;
 
-		Common::Rect _bounds;
 		bool _visible = true;
-		bool _redraw = false;
 		bool _fullRedraw = false;
-		bool _enabled = true;
 
 		Common::String _text;
 		int _value = 0;
@@ -150,7 +160,7 @@ public:
 		int drawText(Common::String text, int x, int y, int w, Color color, Graphics::TextAlign align = Graphics::kTextAlignLeft);
 
 	public:
-		MacWidget(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::String text, bool enabled) : _window(window), _bounds(bounds), _text(text), _enabled(enabled) {}
+		MacWidget(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::String text, bool enabled) : MacGuiObject(bounds, enabled), _window(window), _text(text) {}
 		virtual ~MacWidget() {};
 
 		void setId(int id) { _id = id; }
@@ -161,8 +171,6 @@ public:
 		void setVisible(bool visible) { _visible = visible; }
 		bool isVisible() const { return _visible; }
 
-		Common::Rect getBounds() const { return _bounds; }
-
 		void setRedraw(bool fullRedraw = false);
 
 		bool isEnabled() const { return _enabled; }
@@ -481,21 +489,15 @@ private:
 	int getInventoryScrollOffset() const;
 	void setInventoryScrollOffset(int n) const;
 
-	class Widget {
+	class Widget : public MacGuiObject {
 	private:
 		int _timer = 0;
 
-	protected:
-		bool _redraw = false;
-		bool _enabled = false;
-
 	public:
 		static ScummEngine *_vm;
 		static MacIndy3Gui *_gui;
 		static Graphics::Surface *_surface;
 
-		Common::Rect _bounds;
-
 		Widget(int x, int y, int width, int height);
 		virtual ~Widget() {}
 


Commit: 7e51ddfcc7c0d3b070c75530533f7fc3bcb23674
    https://github.com/scummvm/scummvm/commit/7e51ddfcc7c0d3b070c75530533f7fc3bcb23674
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Simplify animation for Mac GUI default button when pressing Enter

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index e464fb12ba9..07741b62863 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -876,24 +876,16 @@ int MacGui::MacDialogWindow::runDialog() {
 				if (!buttonPressed && event.kbd.keycode == Common::KEYCODE_RETURN) {
 					MacWidget *widget = getDefaultWidget();
 					if (widget && widget->isEnabled()) {
-						widget->setRedraw();
-						widget->draw(true);
-						update();
-
-						for (int i = 0; i < 10; i++) {
-							_system->delayMillis(10);
-							_system->updateScreen();
-						}
-
-						widget->setRedraw();
-						widget->draw();
-						update();
-
-						for (int i = 0; i < 10; i++) {
-							_system->delayMillis(10);
-							_system->updateScreen();
+						for (int i = 0; i < 2; i++) {
+							widget->setRedraw();
+							widget->draw(i == 0);
+							update();
+
+							for (int j = 0; j < 10; j++) {
+								_system->delayMillis(10);
+								_system->updateScreen();
+							}
 						}
-
 						return widget->getId();
 					}
 				}


Commit: 3ab70467c5da65993b05947e43f0f1cbf850dfa7
    https://github.com/scummvm/scummvm/commit/3ab70467c5da65993b05947e43f0f1cbf850dfa7
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Simplify Mac Button drawing

The rounded corners are still a hard-coded mess, but at least now it's a
reusable hard-coded mess.

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 07741b62863..b4cce1766f2 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -336,19 +336,17 @@ void MacGui::MacButton::draw(bool drawFocused) {
 
 	Graphics::Surface *s = _window->innerSurface();
 	Color fg, bg;
-	int x0, x1, x2, x3;
-	int y0, y1;
 
-	x0 = _bounds.left + 3;
-	x1 = _bounds.right - 4;
+	int x0 = _bounds.left;
+	int x1 = _bounds.right - 1;
 
-	y0 = _bounds.top + 3;
-	y1 = _bounds.bottom - 4;
+	int y0 = _bounds.top;
+	int y1 = _bounds.bottom - 1;
 
-	s->hLine(x0, _bounds.top, x1, kBlack);
-	s->hLine(x0, _bounds.bottom - 1, x1, kBlack);
-	s->vLine(_bounds.left, y0, y1, kBlack);
-	s->vLine(_bounds.right - 1, y0, y1, kBlack);
+	s->hLine(x0 + 3, y0, x1 - 3, kBlack);
+	s->hLine(x0 + 3, y1, x1 - 3, kBlack);
+	s->vLine(x0, y0 + 3, y1 - 3, kBlack);
+	s->vLine(x1, y0 + 3, y1 - 3, kBlack);
 
 	if (drawFocused || (_window->getFocusedWidget() == this && _bounds.contains(_window->getMousePos()))) {
 		fg = kWhite;
@@ -364,29 +362,14 @@ void MacGui::MacButton::draw(bool drawFocused) {
 	s->fillRect(Common::Rect(_bounds.left + 1, _bounds.top + 1, _bounds.right - 1, _bounds.bottom - 1), bg);
 
 	// ScummVM's rounded rectangles aren't a complete match for QuickDraw's
-	// rounded rectangles, so we draw the corners manually.
+	// rounded rectangles, and for arcs this small they don't look very good.
+	// So we draw the corners manually.
 	//
 	// Unfortunately, these hard-coded ones only work for most buttons.
 
-	int innerCorner[][2] = {
-		{ 1, 2 },
-		{ 1, 1 }
-	};
-
-	for (int i = 0; i < ARRAYSIZE(innerCorner); i++) {
-		x0 = _bounds.left + innerCorner[i][0];
-		x1 = _bounds.left + innerCorner[i][1];
-		x2 = _bounds.right - innerCorner[i][1] - 1;
-		x3 = _bounds.right - innerCorner[i][0] - 1;
+	CornerLine innerCorner[] = { { 0, 0 }, { 1, 2 }, { 1, 1 }, { 0, -1 } };
 
-		y0 = _bounds.top + i + 1;
-		y1 = _bounds.bottom - i - 2;
-
-		s->hLine(x0, y0, x1, kBlack);
-		s->hLine(x2, y0, x3, kBlack);
-		s->hLine(x0, y1, x1, kBlack);
-		s->hLine(x2, y1, x3, kBlack);
-	}
+	drawCorners(_bounds, innerCorner);
 
 	const Graphics::Font *font = _window->_gui->getFont(kSystemFont);
 
@@ -395,43 +378,24 @@ void MacGui::MacButton::draw(bool drawFocused) {
 	Common::Rect bounds = _bounds;
 
 	if (_window->getDefaultWidget() == this && _fullRedraw) {
-		for (int i = 0; i < 3; i++) {
-			x0 = _bounds.left + 1;
-			x1 = _bounds.right - 2;
-
-			y0 = _bounds.top + 2;
-			y1 = _bounds.bottom - 3;
-
-			s->hLine(x0, _bounds.top - 4 + i, x1, kBlack);
-			s->hLine(x0, _bounds.bottom + 3 - i, x1, kBlack);
-			s->vLine(_bounds.left - 4 + i, y0, y1, kBlack);
-			s->vLine(_bounds.right + 3 - i, y0, y1, kBlack);
-		}
-
-		int outerCorner[][2] = {
-			{ -1, 0 },
-			{ -2, 0 },
-			{ -3, 1 },
-			{ -3, -1 },
-			{ -4, -1 }
-		};
+		bounds.grow(4);
 
-		for (int i = 0; i < ARRAYSIZE(outerCorner); i++) {
-			x0 = _bounds.left + outerCorner[i][0];
-			x1 = _bounds.left + outerCorner[i][1];
-			x2 = _bounds.right - outerCorner[i][1] - 1;
-			x3 = _bounds.right - outerCorner[i][0] - 1;
+		x0 = bounds.left;
+		x1 = bounds.right - 1;
 
-			y0 = _bounds.top - 3 + i;
-			y1 = _bounds.bottom + 2 - i;
+		y0 = bounds.top;
+		y1 = bounds.bottom - 1;
 
-			s->hLine(x0, y0, x1, kBlack);
-			s->hLine(x2, y0, x3, kBlack);
-			s->hLine(x0, y1, x1, kBlack);
-			s->hLine(x2, y1, x3, kBlack);
+		for (int i = 0; i < 3; i++) {
+			s->hLine(x0 + 6, y0 + i, x1 - 6, kBlack);
+			s->hLine(x0 + 6, y1 - i, x1 - 6, kBlack);
+			s->vLine(x0 + i, y0 + 6, y1 - 6, kBlack);
+			s->vLine(x1 - i, y0 + 6, y1 - 6, kBlack);
 		}
 
-		bounds.grow(4);
+		CornerLine outerCorner[] = { { 5, 1 }, { 3, 3 }, { 2, 4 }, { 1, 5 }, { 1, 3 }, { 0, 4 }, { 0, -1 } };
+
+		drawCorners(bounds, outerCorner);
 	}
 
 	_redraw = false;
@@ -439,6 +403,28 @@ void MacGui::MacButton::draw(bool drawFocused) {
 	_window->markRectAsDirty(bounds);
 }
 
+void MacGui::MacButton::drawCorners(Common::Rect r, CornerLine *corner) {
+	Graphics::Surface *s = _window->innerSurface();
+
+	for (int i = 0; corner[i].length >= 0; i++) {
+		if (corner[i].length == 0)
+			continue;
+
+		int x0 = r.left + corner[i].start;
+		int x1 = r.left + corner[i].start + corner[i].length - 1;
+		int x2 = r.right - 1 - corner[i].start;
+		int x3 = r.right - 1 - corner[i].start - corner[i].length + 1;
+
+		int y0 = r.top + i;
+		int y1 = r.bottom - i - 1;
+
+		s->hLine(x0, y0, x1, kBlack);
+		s->hLine(x2, y0, x3, kBlack);
+		s->hLine(x0, y1, x1, kBlack);
+		s->hLine(x2, y1, x3, kBlack);
+	}
+}
+
 // ---------------------------------------------------------------------------
 // Checkbox widget
 // ---------------------------------------------------------------------------
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index ae380e90697..22a59744c66 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -188,10 +188,18 @@ public:
 	};
 
 	class MacButton : public MacWidget {
+	private:
+		struct CornerLine {
+			int start;
+			int length;
+		};
+
 	public:
 		MacButton(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::String text, bool enabled) : MacWidget(window, bounds, text, enabled) {}
 
 		void draw(bool drawFocused = false);
+
+		void drawCorners(Common::Rect r, CornerLine *corner);
 	};
 
 	class MacCheckbox : public MacWidget {


Commit: 26b5c50195e2389ac10f339c6cacbf0d1780bbd6
    https://github.com/scummvm/scummvm/commit/26b5c50195e2389ac10f339c6cacbf0d1780bbd6
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Clean up Mac GUI things, and plug a few memory leaks

I've also added hard-coded button corners for the smaller buttons. It's
still a hack, but a useful one.

Changed paths:
    engines/scumm/charset.cpp
    engines/scumm/charset.h
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp
index 52d36a56b67..ac2ee927246 100644
--- a/engines/scumm/charset.cpp
+++ b/engines/scumm/charset.cpp
@@ -1622,7 +1622,7 @@ CharsetRendererMac::CharsetRendererMac(ScummEngine *vm, const Common::String &fo
 		const Graphics::Font *font = _vm->_macGui->getFontByScummId(0);
 
 		_glyphSurface = new Graphics::Surface();
-		_glyphSurface->create(font->getFontHeight(), font->getMaxCharWidth(), Graphics::PixelFormat::createFormatCLUT8());
+		_glyphSurface->create(font->getMaxCharWidth(), font->getFontHeight(), Graphics::PixelFormat::createFormatCLUT8());
 	}
 }
 
@@ -1637,28 +1637,8 @@ void CharsetRendererMac::setCurID(int32 id) {
 	if  (id == -1)
 		return;
 
-	_useRealCharWidth = (id & 0x80) != 0;
-	id = id & 0x7F;
-
-	// Indiana Jones and the Last Crusade uses font id 1 in a number of
-	// places. In the DOS version, this is a bolder font than font 0, but
-	// by the looks of it the Mac version uses the same font for both
-	// cases. In ScummVM, we match id 0 and 1 to font 0 and id 2 (which is
-	// only used to print the text box caption) to font 1.
-	if (_vm->_game.id == GID_INDY3) {
-		if (id == 0 || id == 1) {
-			id = 0;
-		} else if (id == 2) {
-			id = 1;
-		}
-	}
-
-	if (id < 0 || id > 1) {
-		warning("CharsetRendererMac::setCurID(%d) - invalid charset", id);
-		id = 0;
-	}
-
 	_curId = id;
+	_font = _vm->_macGui->getFontByScummId(_curId);
 }
 
 int CharsetRendererMac::getStringWidth(int arg, const byte *text) {
@@ -1683,36 +1663,20 @@ int CharsetRendererMac::getStringWidth(int arg, const byte *text) {
 }
 
 int CharsetRendererMac::getDrawWidthIntern(uint16 chr) const {
-	const Graphics::Font *font = _vm->_macGui->getFontByScummId(_curId);
-
-	return font->getCharWidth(chr);
+	return _font->getCharWidth(chr);
 }
 
-// HACK: Usually, we want the approximate width and height in the unscaled
-//       graphics resolution. But for font 1 in Indiana Jones and the Last
-//       crusade we want the actual dimensions for drawing the text boxes.
-
 int CharsetRendererMac::getFontHeight() const {
-	const Graphics::Font *font = _vm->_macGui->getFontByScummId(_curId);
-	int height = font->getFontHeight();
-
-        // If we ever need the height for font 1 in Last Crusade (we don't at
-	// the moment), we need the actual height.
-	if (_curId == 0 || _vm->_game.id != GID_INDY3)
-		height /= 2;
-
-	return height;
+	return _font->getFontHeight() / 2;
 }
 
 int CharsetRendererMac::getCharWidth(uint16 chr) const {
-	int width = getDrawWidthIntern(chr);
-	return _useRealCharWidth ? width : width / 2;
+	return _font->getCharWidth(chr) / 2;
 }
 
 void CharsetRendererMac::printChar(int chr, bool ignoreCharsetMask) {
 	// This function does most of the heavy lifting printing the game
-	// text. It's the only function that needs to be able to handle
-	// disabled text.
+	// text.
 
 	// If this is the beginning of a line, assume the position will be
 	// correct without any padding.
@@ -1761,17 +1725,17 @@ void CharsetRendererMac::printChar(int chr, bool ignoreCharsetMask) {
 		if ((chr >= 16 && chr <= 23) || chr == 60 || chr == 95) {
 			enableShadow = true;
 		}
-	}
 
-	// HACK: Apparently, note names are never drawn in light gray. Only
-	//       white for known notes, and dark gray for unknown ones. This
-	//       hack ensures that we won't be left with a mix of white and
-	//       light gray note names, because apparently the game never
-	//       changes them back to light gray once the draft is done?
+		// HACK: Apparently, note names are never drawn in light gray.
+		// Only white for known notes, and dark gray for unknown ones.
+		// This hack ensures that we won't be left with a mix of white
+		// and light gray note names, because apparently the game never
+		// changes them back to light gray once the draft is done?
 
-	if (_vm->_game.id == GID_LOOM) {
-		if (chr >= 16 && chr <= 23 && _color == 7)
-			color = 15;
+		if (_vm->_game.id == GID_LOOM) {
+			if (chr >= 16 && chr <= 23 && _color == 7)
+				color = 15;
+		}
 	}
 
 	bool drawToTextBox = (vs->number == kTextVirtScreen && _vm->_game.id == GID_INDY3);
@@ -1789,9 +1753,6 @@ void CharsetRendererMac::printChar(int chr, bool ignoreCharsetMask) {
 	//       redrawn along with its name. It's enough to redraw it on the
 	//       text surface. We can assume the correct color is already on
 	//       screen.
-	//
-	//       Note that this will not affect the Practice Mode box, since
-	//       this note names are drawn by drawChar(), not printChar().
 
 	if (_vm->_game.id == GID_LOOM) {
 		if (chr >= 16 && chr <= 23) {
@@ -1817,18 +1778,16 @@ void CharsetRendererMac::printChar(int chr, bool ignoreCharsetMask) {
 	if (!_useCorrectFontSpacing && !drawToTextBox && (width & 1))
 		width++;
 
-	const Graphics::Font *font = _vm->_macGui->getFontByScummId(_curId);
-
 	if (enableShadow) {
 		left = macLeft / 2;
 		right = (macLeft + width + 3) / 2;
 		top = macTop / 2;
-		bottom = (macTop + font->getFontHeight() + 3) / 2;
+		bottom = (macTop + _font->getFontHeight() + 3) / 2;
 	} else {
 		left = (macLeft + 1) / 2;
 		right = (macLeft + width + 1) / 2;
 		top = (macTop + 1) / 2;
-		bottom = (macTop + font->getFontHeight() + 1) / 2;
+		bottom = (macTop + _font->getFontHeight() + 1) / 2;
 	}
 
 	if (_firstChar) {
@@ -1893,8 +1852,6 @@ void CharsetRendererMac::printCharInternal(int chr, int color, bool shadow, int
 		y++;
 	}
 
-	const Graphics::Font *font = _vm->_macGui->getFontByScummId(_curId);
-
 	if (shadow) {
 		byte shadowColor = getTextShadowColor();
 
@@ -1905,72 +1862,49 @@ void CharsetRendererMac::printCharInternal(int chr, int color, bool shadow, int
 			// particularly good anyway). This seems to match the
 			// original look for normal text.
 
-			font->drawChar(&_vm->_textSurface, chr, x + 1, y - 1, 0);
-			font->drawChar(&_vm->_textSurface, chr, x - 1, y + 1, 0);
-			font->drawChar(&_vm->_textSurface, chr, x + 2, y + 2, 0);
+			_font->drawChar(&_vm->_textSurface, chr, x + 1, y - 1, 0);
+			_font->drawChar(&_vm->_textSurface, chr, x - 1, y + 1, 0);
+			_font->drawChar(&_vm->_textSurface, chr, x + 2, y + 2, 0);
 
 			if (color != -1) {
-				font->drawChar(_vm->_macScreen, chr, x + 1, y - 1, shadowColor);
-				font->drawChar(_vm->_macScreen, chr, x - 1, y + 1, shadowColor);
-				font->drawChar(_vm->_macScreen, chr, x + 2, y + 2, shadowColor);
+				_font->drawChar(_vm->_macScreen, chr, x + 1, y - 1, shadowColor);
+				_font->drawChar(_vm->_macScreen, chr, x - 1, y + 1, shadowColor);
+				_font->drawChar(_vm->_macScreen, chr, x + 2, y + 2, shadowColor);
 			}
 		} else {
 			// Indy 3 uses simpler shadowing, and doesn't need the
 			// "draw only on text surface" hack.
 
-			font->drawChar(&_vm->_textSurface, chr, x + 1, y + 1, 0);
-			font->drawChar(_vm->_macScreen, chr, x + 1, y + 1, shadowColor);
+			_font->drawChar(&_vm->_textSurface, chr, x + 1, y + 1, 0);
+			_font->drawChar(_vm->_macScreen, chr, x + 1, y + 1, shadowColor);
 		}
 	}
 
-	font->drawChar(&_vm->_textSurface, chr, x, y, 0);
+	_font->drawChar(&_vm->_textSurface, chr, x, y, 0);
 
 	if (color != -1) {
 		color = getTextColor();
 
 		if (_vm->_renderMode == Common::kRenderMacintoshBW && color != 0 && color != 15) {
 			_glyphSurface->fillRect(Common::Rect(_glyphSurface->w, _glyphSurface->h), 0);
-			font->drawChar(_glyphSurface, chr, 0, 0, 15);
-
-			byte *src = (byte *)_glyphSurface->getBasePtr(0, 0);
-			byte *dst = (byte *)_vm->_macScreen->getBasePtr(x, y);
+			_font->drawChar(_glyphSurface, chr, 0, 0, 15);
 
-			for (int h = 0; h < _glyphSurface->h; h++) {
-				bool pixel = ((y + h + 1) & 1) == 0;
+			for (int y0 = 0; y0 < _glyphSurface->h; y0++) {
+				for (int x0 = 0; x0 < _glyphSurface->w; x0++) {
+					if (_glyphSurface->getPixel(x0, y0)) {
+						int x1 = x + x0;
+						int y1 = y + y0;
 
-				for (int w = 0; w < _glyphSurface->w; w++) {
-					if (src[w]) {
-						if (pixel)
-							dst[w] = 15;
-						else
-							dst[w] = 0;
+						_vm->_macScreen->setPixel(x1, y1, ((x1 + y1) & 1) ? 0 : 15);
 					}
-					pixel = !pixel;
 				}
-				src += _glyphSurface->pitch;
-				dst += _vm->_macScreen->pitch;
 			}
 		} else {
-			font->drawChar(_vm->_macScreen, chr, x, y, color);
+			_font->drawChar(_vm->_macScreen, chr, x, y, color);
 		}
 	}
 }
 
-void CharsetRendererMac::drawChar(int chr, Graphics::Surface &s, int x, int y) {
-	// This function is used for drawing most of the text outside of what
-	// the game scripts request. It's used for the text box captions in
-	// Indiana Jones and the Last Crusade, and for the practice mode box
-	// in Loom.
-	int color = _color;
-
-	if (_vm->_renderMode == Common::kRenderMacintoshBW)
-		color = 15;
-
-	const Graphics::Font *font = _vm->_macGui->getFontByScummId(_curId);
-
-	font->drawChar(&s, chr, x, y, color);
-}
-
 void CharsetRendererMac::setColor(byte color) {
 	_color = color;
 	_enableShadow = false;
diff --git a/engines/scumm/charset.h b/engines/scumm/charset.h
index 46f24367b88..20aa278b4b2 100644
--- a/engines/scumm/charset.h
+++ b/engines/scumm/charset.h
@@ -285,15 +285,12 @@ public:
 
 class CharsetRendererMac : public CharsetRendererCommon {
 protected:
-	bool _useRealCharWidth;
+	const Graphics::Font *_font;
 	bool _useCorrectFontSpacing;
 	bool _pad;
 	int _lastTop;
 
-	const Graphics::Font *getFont(int id) const;
-
 	int getDrawWidthIntern(uint16 chr) const;
-
 	void printCharInternal(int chr, int color, bool shadow, int x, int y);
 
 	byte getTextColor();
@@ -311,7 +308,6 @@ public:
 	int getFontHeight() const override;
 	int getCharWidth(uint16 chr) const override;
 	void printChar(int chr, bool ignoreCharsetMask) override;
-	void drawChar(int chr, Graphics::Surface &s, int x, int y) override;
 	void setColor(byte color) override;
 };
 
diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index b4cce1766f2..0fdc7867cb7 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -190,10 +190,10 @@ Common::KeyState ScummEngine::mac_showOldStyleBannerAndPause(const char *msg, in
 
 	// Pause the engine
 	PauseToken pt = pauseEngine();
-
 	Common::KeyState ks = Common::KEYCODE_INVALID;
-	bool leftBtnPressed = false, rightBtnPressed = false;
+
 	if (waitTime) {
+		bool leftBtnPressed = false, rightBtnPressed = false;
 		waitForBannerInput(waitTime, ks, leftBtnPressed, rightBtnPressed);
 	}
 
@@ -318,7 +318,7 @@ int MacGui::MacWidget::drawText(Common::String text, int x, int y, int w, Color
 		y0 += font->getFontHeight();
 	}
 
-	return lineWidth;
+	return maxLineWidth;
 }
 
 // ---------------------------------------------------------------------------
@@ -334,42 +334,55 @@ void MacGui::MacButton::draw(bool drawFocused) {
 
 	debug(1, "MacGui::MacButton: Drawing button %d (_fullRedraw = %d, drawFocused = %d, _value = %d)", _id, _fullRedraw, drawFocused, _value);
 
+	// ScummVM's rounded rectangles aren't a complete match for QuickDraw's
+	// rounded rectangles, and for arcs this small they don't look very
+	// good. So we draw the corners manually.
+
+	CornerLine buttonCorner[] = { { 0, 0 }, { 1, 2 }, { 1, 1 }, { 0, -1 } };
+	CornerLine smallButtonCorner[] = { { 0, 0 }, { 1, 2 }, { 1, 1 }, { 0, -1 } };
+	CornerLine frameCorner[] = { { 5, 1 }, { 3, 3 }, { 2, 4 }, { 1, 5 }, { 1, 3 }, { 0, 4 }, { 0, -1 } };
+
 	Graphics::Surface *s = _window->innerSurface();
 	Color fg, bg;
 
+	if (drawFocused || (_window->getFocusedWidget() == this && _bounds.contains(_window->getMousePos()))) {
+		fg = kWhite;
+		bg = kBlack;
+	} else {
+		fg = kBlack;
+		bg = kWhite;
+	}
+
 	int x0 = _bounds.left;
 	int x1 = _bounds.right - 1;
 
 	int y0 = _bounds.top;
 	int y1 = _bounds.bottom - 1;
 
-	s->hLine(x0 + 3, y0, x1 - 3, kBlack);
-	s->hLine(x0 + 3, y1, x1 - 3, kBlack);
-	s->vLine(x0, y0 + 3, y1 - 3, kBlack);
-	s->vLine(x1, y0 + 3, y1 - 3, kBlack);
+	CornerLine *corner;
+	int cornerSize;
 
-	if (drawFocused || (_window->getFocusedWidget() == this && _bounds.contains(_window->getMousePos()))) {
-		fg = kWhite;
-		bg = kBlack;
+	if (_bounds.height() >= 20) {
+		corner = buttonCorner;
+		cornerSize = 3;
 	} else {
-		fg = kBlack;
-		bg = kWhite;
+		corner = smallButtonCorner;
+		cornerSize = 2;
 	}
 
+	s->hLine(x0 + cornerSize, y0, x1 - cornerSize, kBlack);
+	s->hLine(x0 + cornerSize, y1, x1 - cornerSize, kBlack);
+	s->vLine(x0, y0 + cornerSize, y1 - cornerSize, kBlack);
+	s->vLine(x1, y0 + cornerSize, y1 - cornerSize, kBlack);
+
 	// The way the corners are rounded, we can fill this entire rectangle
 	// in one go.
 
-	s->fillRect(Common::Rect(_bounds.left + 1, _bounds.top + 1, _bounds.right - 1, _bounds.bottom - 1), bg);
-
-	// ScummVM's rounded rectangles aren't a complete match for QuickDraw's
-	// rounded rectangles, and for arcs this small they don't look very good.
-	// So we draw the corners manually.
-	//
-	// Unfortunately, these hard-coded ones only work for most buttons.
-
-	CornerLine innerCorner[] = { { 0, 0 }, { 1, 2 }, { 1, 1 }, { 0, -1 } };
+	Common::Rect inside = _bounds;
+	inside.grow(-1);
+	s->fillRect(inside, bg);
 
-	drawCorners(_bounds, innerCorner);
+	drawCorners(_bounds, corner);
 
 	const Graphics::Font *font = _window->_gui->getFont(kSystemFont);
 
@@ -393,13 +406,12 @@ void MacGui::MacButton::draw(bool drawFocused) {
 			s->vLine(x1 - i, y0 + 6, y1 - 6, kBlack);
 		}
 
-		CornerLine outerCorner[] = { { 5, 1 }, { 3, 3 }, { 2, 4 }, { 1, 5 }, { 1, 3 }, { 0, 4 }, { 0, -1 } };
-
-		drawCorners(bounds, outerCorner);
+		drawCorners(bounds, frameCorner);
 	}
 
 	_redraw = false;
 	_fullRedraw = false;
+
 	_window->markRectAsDirty(bounds);
 }
 
@@ -648,7 +660,7 @@ MacGui::MacDialogWindow::MacDialogWindow(MacGui *gui, OSystem *system, Graphics:
 	_dirtyRects.clear();
 
 	Graphics::Surface *s = surface();
-	Common::Rect r = Common::Rect(0, 0, s->w, s->h);
+	Common::Rect r = Common::Rect(s->w, s->h);
 
 	r.grow(-1);
 	s->fillRect(r, kWhite);
@@ -695,7 +707,7 @@ MacGui::MacDialogWindow::~MacDialogWindow() {
 
 void MacGui::MacDialogWindow::copyToScreen(Graphics::Surface *s) const {
 	if (s) {
-		_from->copyRectToSurface(*s, _bounds.left, _bounds.top, Common::Rect(0, 0, _bounds.width(), _bounds.height()));
+		_from->copyRectToSurface(*s, _bounds.left, _bounds.top, Common::Rect(_bounds.width(), _bounds.height()));
 	}
 	_system->copyRectToScreen(_from->getBasePtr(_bounds.left, _bounds.top), _from->pitch, _bounds.left, _bounds.top, _bounds.width(), _bounds.height());
 }
@@ -771,7 +783,7 @@ void MacGui::MacDialogWindow::update(bool fullRedraw) {
 
 	if (fullRedraw) {
 		_dirtyRects.clear();
-		markRectAsDirty(Common::Rect(0, 0, _innerSurface.w, _innerSurface.h));
+		markRectAsDirty(Common::Rect(_innerSurface.w, _innerSurface.h));
 	}
 
 	for (uint i = 0; i < _dirtyRects.size(); i++) {
@@ -800,10 +812,20 @@ int MacGui::MacDialogWindow::runDialog() {
 	if (!_visible) {
 		show();
 
+		Common::Rect windowBounds(_innerSurface.w, _innerSurface.h);
+
 		for (uint i = 0; i < _widgets.size(); i++) {
 			_widgets[i]->setId(i);
-			_widgets[i]->setRedraw(true);
-			_widgets[i]->draw();
+
+			// We don't deal with off-window widgets. Not even
+			// partly off-window.
+
+			if (windowBounds.contains(_widgets[i]->getBounds())) {
+				_widgets[i]->setRedraw(true);
+				_widgets[i]->draw();
+			} else {
+				_widgets[i]->setVisible(false);
+			}
 		}
 	}
 
@@ -891,12 +913,12 @@ int MacGui::MacDialogWindow::runDialog() {
 }
 
 void MacGui::MacDialogWindow::drawSprite(const Graphics::Surface *sprite, int x, int y) {
-	_innerSurface.copyRectToSurface(*sprite, x, y, Common::Rect(0, 0, sprite->w, sprite->h));
+	_innerSurface.copyRectToSurface(*sprite, x, y, Common::Rect(sprite->w, sprite->h));
 	markRectAsDirty(Common::Rect(x, y, x + sprite->w, y + sprite->h));
 }
 
 void MacGui::MacDialogWindow::drawSprite(const Graphics::Surface *sprite, int x, int y, Common::Rect(clipRect)) {
-	Common::Rect subRect(0, 0, sprite->w, sprite->h);
+	Common::Rect subRect(sprite->w, sprite->h);
 
 	if (x < clipRect.left) {
 		subRect.left += (clipRect.left - x);
@@ -1035,11 +1057,10 @@ void MacGui::initialize() {
 	_windowManager = new Graphics::MacWindowManager(Graphics::kWMModeNoDesktop | Graphics::kWMModeAutohideMenu | Graphics::kWMModalMenuMode | Graphics::kWMModeNoCursorOverride);
 	_windowManager->setEngine(_vm);
 	_windowManager->setScreen(640, 400);
-	_windowManager->setMenuHotzone(Common::Rect(0, 0, 640, 23));
+	_windowManager->setMenuHotzone(Common::Rect(640, 23));
 	_windowManager->setMenuDelay(250000);
 
 	Common::MacResManager resource;
-	Common::SeekableReadStream *res;
 	Graphics::MacMenu *menu = _windowManager->addMenu();
 
 	resource.open(_resourceFile);
@@ -1059,7 +1080,7 @@ void MacGui::initialize() {
 	menu->setCommandsCallback(menuCallback, this);
 
 	for (int i = 129; i <= 130; i++) {
-		res = resource.getResource(MKTAG('M', 'E', 'N', 'U'), i);
+		Common::SeekableReadStream *res = resource.getResource(MKTAG('M', 'E', 'N', 'U'), i);
 
 		if (!res)
 			continue;
@@ -1069,6 +1090,9 @@ void MacGui::initialize() {
 		Common::String string = menuDef->operator[](1);
 		int id = menu->addMenuItem(nullptr, name);
 		menu->createSubMenuFromString(id, string.c_str(), 0);
+
+		delete menuDef;
+		delete res;
 	}
 
 	resource.close();
@@ -1211,7 +1235,9 @@ Graphics::Surface *MacGui::loadPict(int id) {
 		s = decodePictV1(res);
 	}
 
+	delete res;
 	resource.close();
+
 	return s;
 }
 
@@ -1556,7 +1582,6 @@ MacGui::MacDialogWindow *MacGui::createWindow(Common::Rect bounds, MacDialogWind
 MacGui::MacDialogWindow *MacGui::createDialog(int dialogId) {
 	Common::MacResManager resource;
 	Common::SeekableReadStream *res;
-	int button = 0;
 
 	resource.open(_resourceFile);
 
@@ -1584,6 +1609,8 @@ MacGui::MacDialogWindow *MacGui::createDialog(int dialogId) {
 		bounds.translate(86, 88);
 	}
 
+	delete res;
+
 	MacDialogWindow *window = createWindow(bounds);
 
 	res = resource.getResource(MKTAG('D', 'I', 'T', 'L'), dialogId);
@@ -1621,7 +1648,6 @@ MacGui::MacDialogWindow *MacGui::createDialog(int dialogId) {
 				// Button
 				str = getDialogString(res, len);
 				window->addButton(r, str, enabled);
-				button++;
 				break;
 
 			case 5:
@@ -1659,7 +1685,9 @@ MacGui::MacDialogWindow *MacGui::createDialog(int dialogId) {
 		}
 	}
 
+	delete res;
 	resource.close();
+
 	return window;
 }
 
@@ -1685,6 +1713,7 @@ const Graphics::Font *MacLoomGui::getFontByScummId(int32 id) {
 	switch (id) {
 	case 0:
 		return getFont(kLoomFontLarge);
+
 	default:
 		error("MacLoomGui::getFontByScummId: Invalid font id %d", id);
 	}
@@ -1747,8 +1776,8 @@ void MacLoomGui::setupCursor(int &width, int &height, int &hotspotX, int &hotspo
 		_windowManager->replaceCursor(Graphics::kMacCursorCustom, &macCursor);
 	}
 
-	resource.close();
 	delete curs;
+	resource.close();
 }
 
 bool MacLoomGui::handleMenu(int id, Common::String &name) {
@@ -1865,7 +1894,7 @@ void MacLoomGui::runAboutDialog() {
 	window->show();
 
 	int scene = 0;
-	int status;
+	int status = 0;
 
 	Common::Rect r(0, 0, 404, 154);
 	int growth = -2;
@@ -2025,16 +2054,15 @@ void MacLoomGui::runAboutDialog() {
 	}
 
 	if (status != 2)
-		status = delay(-1);
+		delay(-1);
 
 	_windowManager->popCursor();
 
 	lucasFilm->free();
-	delete lucasFilm;
-
 	loom->free();
-	delete loom;
 
+	delete lucasFilm;
+	delete loom;
 	delete window;
 }
 
@@ -2175,6 +2203,8 @@ void MacLoomGui::update(int delta) {
 			int w = 64;
 			int h = 24;
 
+			bool bw = (_vm->_renderMode == Common::kRenderMacintoshBW);
+
 			if (!_practiceBox) {
 				debug(1, "MacLoomGui: Creating practice mode box");
 
@@ -2182,12 +2212,14 @@ void MacLoomGui::update(int delta) {
 				_practiceBox->create(w, h, Graphics::PixelFormat
 ::createFormatCLUT8());
 
-				_practiceBox->fillRect(Common::Rect(0, 0, 62, 22), kBlack);
+				_practiceBox->fillRect(Common::Rect(62, 22), kBlack);
+
+				Color color = bw ? kWhite : kLightGray;
 
-				_practiceBox->hLine(2, 1, w - 3, kLightGray);
-				_practiceBox->hLine(2, h - 2, w - 3, kLightGray);
-				_practiceBox->vLine(1, 2, h - 3, kLightGray);
-				_practiceBox->vLine(w - 2, 2, h - 3, kLightGray);
+				_practiceBox->hLine(2, 1, w - 3, color);
+				_practiceBox->hLine(2, h - 2, w - 3, color);
+				_practiceBox->vLine(1, 2, h - 3, color);
+				_practiceBox->vLine(w - 2, 2, h - 3, color);
 				_practiceBoxNotes = 0;
 			}
 
@@ -2202,8 +2234,9 @@ void MacLoomGui::update(int delta) {
 				for (int i = 0; i < 4; i++) {
 					int note = (notes >> (4 * i)) & 0x0F;
 
-					if (note >= 2 && note <= 9)
-						font->drawChar(_practiceBox, 14 + note, 9 + i * 13, 5, colors[note - 2]);
+					if (note >= 2 && note <= 9) {
+						font->drawChar(_practiceBox, 14 + note, 9 + i * 13, 5, bw ? kWhite : colors[note - 2]);
+					}
 				}
 			}
 
@@ -3248,9 +3281,16 @@ void MacIndy3Gui::setupCursor(int &width, int &height, int &hotspotX, int &hotsp
 }
 
 const Graphics::Font *MacIndy3Gui::getFontByScummId(int32 id) {
+	// The game seems to use font 0 most of the time, but during the intro
+	// it switches to font 1 to print "BARNETT COLLEGE,", "NEW YORK," and
+	// "1938". By the look of it, these map to the same font.
+	//
+	// This is different from the DOS version, where font 1 is bolder.
 	switch (id) {
 	case 0:
+	case 1:
 		return getFont(kIndy3FontMedium);
+
 	default:
 		error("MacIndy3Gui::getFontByScummId: Invalid font id %d", id);
 	}
@@ -3309,7 +3349,6 @@ void MacIndy3Gui::initTextAreaForActor(Actor *a, byte color) {
 	_textArea.fillRect(Common::Rect(width, height), kBlack);
 
 	int nameWidth = 0;
-//	byte color = _charset->getColor();
 
 	if (a) {
 		const Graphics::Font *font = getFont(kIndy3FontSmall);
@@ -3410,7 +3449,7 @@ void MacIndy3Gui::runAboutDialog() {
 	// cut out enough of the background so that each time they are drawn,
 	// the visible remains of the previous frame is overdrawn.
 
-	Graphics::Surface train = pict->getSubArea(Common::Rect(0, 0, 249, 93));
+	Graphics::Surface train = pict->getSubArea(Common::Rect(249, 93));
 
 	Graphics::Surface trolley[3];
 
@@ -3430,7 +3469,7 @@ void MacIndy3Gui::runAboutDialog() {
 	// 10 fps.
 
 	int scene = 0;
-	int status;
+	int status = 0;
 
 	int trainX = -2;
 	int trolleyX = width + 1;
@@ -3612,7 +3651,7 @@ void MacIndy3Gui::runAboutDialog() {
 	}
 
 	if (status != 2)
-		status = delay(-1);
+		delay(-1);
 
 	_windowManager->popCursor();
 
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 22a59744c66..042cff827d1 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -273,7 +273,6 @@ public:
 	private:
 		OSystem *_system;
 		Common::Rect _bounds;
-		Common::Rect _innerBounds;
 		int _margin;
 
 		bool _visible = false;


Commit: 0d8cded7690ef60bad4e02418e3c1c22909ba30e
    https://github.com/scummvm/scummvm/commit/0d8cded7690ef60bad4e02418e3c1c22909ba30e
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Move MacGuiObject into MacGui

Changed paths:
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 042cff827d1..cc85928068a 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -36,19 +36,6 @@ namespace Scumm {
 class ScummEngine;
 class Actor;
 
-class MacGuiObject {
-protected:
-	bool _redraw = false;
-	bool _enabled = false;
-	Common::Rect _bounds;
-
-public:
-	MacGuiObject(Common::Rect bounds, bool enabled) : _bounds(bounds), _enabled(enabled) {}
-	virtual ~MacGuiObject() {}
-
-	Common::Rect getBounds() const { return _bounds; }
-};
-
 class MacGui {
 protected:
 	ScummEngine *_vm = nullptr;
@@ -146,6 +133,19 @@ protected:
 public:
 	class MacDialogWindow;
 
+	class MacGuiObject {
+	protected:
+		bool _redraw = false;
+		bool _enabled = false;
+		Common::Rect _bounds;
+
+	public:
+		MacGuiObject(Common::Rect bounds, bool enabled) : _bounds(bounds), _enabled(enabled) {}
+		virtual ~MacGuiObject() {}
+
+		Common::Rect getBounds() const { return _bounds; }
+	};
+
 	class MacWidget : public MacGuiObject {
 	protected:
 		MacGui::MacDialogWindow *_window;


Commit: 2f846ca92c1b7f35626cfd5c8c245c8937fc4c8d
    https://github.com/scummvm/scummvm/commit/2f846ca92c1b7f35626cfd5c8c245c8937fc4c8d
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Fix regression in Indy 3 Mac IQ dialog

And re-introduce crash in the file dialog. I have to think of a better
way of dealing with that, even though we don't use the dialog.

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 0fdc7867cb7..91ad6a00ee4 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -756,6 +756,8 @@ void MacGui::MacDialogWindow::addCheckbox(Common::Rect bounds, Common::String te
 }
 
 void MacGui::MacDialogWindow::addText(Common::Rect bounds, Common::String text, bool enabled) {
+	// Adjust the text position slightly
+	bounds.left++;
 	_widgets.push_back(new MacText(this, bounds, text, enabled));
 }
 
@@ -816,16 +818,8 @@ int MacGui::MacDialogWindow::runDialog() {
 
 		for (uint i = 0; i < _widgets.size(); i++) {
 			_widgets[i]->setId(i);
-
-			// We don't deal with off-window widgets. Not even
-			// partly off-window.
-
-			if (windowBounds.contains(_widgets[i]->getBounds())) {
-				_widgets[i]->setRedraw(true);
-				_widgets[i]->draw();
-			} else {
-				_widgets[i]->setVisible(false);
-			}
+			_widgets[i]->setRedraw(true);
+			_widgets[i]->draw();
 		}
 	}
 
@@ -1659,7 +1653,6 @@ MacGui::MacDialogWindow *MacGui::createDialog(int dialogId) {
 			case 8:
 				// Static text
 				str = getDialogString(res, len);
-				r.left++;
 				window->addText(r, str, enabled);
 				break;
 


Commit: 4593b51860375905239fb18052a13c014eb9102a
    https://github.com/scummvm/scummvm/commit/4593b51860375905239fb18052a13c014eb9102a
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Clip Mac widgets to the inner surface of the dialog

Widgets that are clipped out of existence are made invisible to avoid
crashes. This happens with the Last Crusade open dialog. The strings in
the IQ dialog are also clipped, but only slightly.

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 91ad6a00ee4..834b2570cf2 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -221,6 +221,19 @@ Common::KeyState ScummEngine::mac_showOldStyleBannerAndPause(const char *msg, in
 // Base widget
 // ---------------------------------------------------------------------------
 
+MacGui::MacWidget::MacWidget(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::String text, bool enabled) : MacGuiObject(bounds, enabled), _window(window), _text(text) {
+	// Widgets are clipped to the inner surface of the dialog. If a widget
+	// is clipped out of existence, make it invisible to avoid crashes.
+
+	Graphics::Surface *s = _window->innerSurface();
+
+	_bounds.clip(Common::Rect(s->w, s->h));
+
+	if (_bounds.width() <= 0 || _bounds.height() <= 0)
+		_visible = false;
+}
+
+
 bool MacGui::MacWidget::findWidget(int x, int y) const {
 	return _visible && _enabled && _bounds.contains(x, y);
 }
@@ -1398,6 +1411,9 @@ bool MacGui::handleMenu(int id, Common::String &name) {
 	case 203:	// Pause
 		return true;
 
+	// In the original, the Edit menu is active during save dialogs, though
+	// only Cut, Copy and Paste.
+
 	case 300:	// Undo
 	case 301:	// Cut
 	case 302:	// Copy
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index cc85928068a..68816a3b295 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -160,7 +160,7 @@ public:
 		int drawText(Common::String text, int x, int y, int w, Color color, Graphics::TextAlign align = Graphics::kTextAlignLeft);
 
 	public:
-		MacWidget(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::String text, bool enabled) : MacGuiObject(bounds, enabled), _window(window), _text(text) {}
+		MacWidget(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::String text, bool enabled);
 		virtual ~MacWidget() {};
 
 		void setId(int id) { _id = id; }


Commit: 66cc66049671472432992f13d67937e997a2914e
    https://github.com/scummvm/scummvm/commit/66cc66049671472432992f13d67937e997a2914e
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Make Indy 3/Loom Mac menu depend on original GUI setting

The rest of the Mac GUI is non-negotiable. Supporting the old
Franken-GUI for Indy 3 would be madness, now that we've removed the
hacks that made it work. But I can see how someone would find the
Macintosh menu distracting. Particularly when it's activated on
mouse-over.

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 834b2570cf2..99d6b301798 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -1064,60 +1064,63 @@ void MacGui::initialize() {
 	_windowManager = new Graphics::MacWindowManager(Graphics::kWMModeNoDesktop | Graphics::kWMModeAutohideMenu | Graphics::kWMModalMenuMode | Graphics::kWMModeNoCursorOverride);
 	_windowManager->setEngine(_vm);
 	_windowManager->setScreen(640, 400);
-	_windowManager->setMenuHotzone(Common::Rect(640, 23));
-	_windowManager->setMenuDelay(250000);
 
-	Common::MacResManager resource;
-	Graphics::MacMenu *menu = _windowManager->addMenu();
+	if (_vm->isUsingOriginalGUI()) {
+		_windowManager->setMenuHotzone(Common::Rect(640, 23));
+		_windowManager->setMenuDelay(250000);
 
-	resource.open(_resourceFile);
+		Common::MacResManager resource;
+		Graphics::MacMenu *menu = _windowManager->addMenu();
 
-	// Add the Apple menu
+		resource.open(_resourceFile);
 
-	const Graphics::MacMenuData menuSubItems[] = {
-		{ 0, NULL, 0, 0, false }
-	};
+		// Add the Apple menu
 
-	// TODO: This can be found in the STRS resource
-	Common::String aboutMenuDef = "About " + name() + "...<B;(-";
+		const Graphics::MacMenuData menuSubItems[] = {
+			{ 0, NULL, 0, 0, false }
+		};
 
-	menu->addStaticMenus(menuSubItems);
-	menu->createSubMenuFromString(0, aboutMenuDef.c_str(), 0);
+		// TODO: This can be found in the STRS resource
+		Common::String aboutMenuDef = "About " + name() + "...<B;(-";
 
-	menu->setCommandsCallback(menuCallback, this);
+		menu->addStaticMenus(menuSubItems);
+		menu->createSubMenuFromString(0, aboutMenuDef.c_str(), 0);
 
-	for (int i = 129; i <= 130; i++) {
-		Common::SeekableReadStream *res = resource.getResource(MKTAG('M', 'E', 'N', 'U'), i);
+		menu->setCommandsCallback(menuCallback, this);
 
-		if (!res)
-			continue;
+		for (int i = 129; i <= 130; i++) {
+			Common::SeekableReadStream *res = resource.getResource(MKTAG('M', 'E', 'N', 'U'), i);
 
-		Common::StringArray *menuDef = Graphics::MacMenu::readMenuFromResource(res);
-		Common::String name = menuDef->operator[](0);
-		Common::String string = menuDef->operator[](1);
-		int id = menu->addMenuItem(nullptr, name);
-		menu->createSubMenuFromString(id, string.c_str(), 0);
+			if (!res)
+				continue;
 
-		delete menuDef;
-		delete res;
-	}
+			Common::StringArray *menuDef = Graphics::MacMenu::readMenuFromResource(res);
+			Common::String name = menuDef->operator[](0);
+			Common::String string = menuDef->operator[](1);
+			int id = menu->addMenuItem(nullptr, name);
+			menu->createSubMenuFromString(id, string.c_str(), 0);
 
-	resource.close();
+			delete menuDef;
+			delete res;
+		}
 
-	// Assign sensible IDs to the menu items
+		resource.close();
 
-	int numberOfMenus = menu->numberOfMenus();
+		// Assign sensible IDs to the menu items
 
-	for (int i = 0; i < numberOfMenus; i++) {
-		Graphics::MacMenuItem *item = menu->getMenuItem(i);
-		int numberOfMenuItems = menu->numberOfMenuItems(item);
-		int id = 100 * (i + 1);
-		for (int j = 0; j < numberOfMenuItems; j++) {
-			Graphics::MacMenuItem *subItem = menu->getSubMenuItem(item, j);
-			Common::String name = menu->getName(subItem);
+		int numberOfMenus = menu->numberOfMenus();
 
-			if (!name.empty())
-				menu->setAction(subItem, id++);
+		for (int i = 0; i < numberOfMenus; i++) {
+			Graphics::MacMenuItem *item = menu->getMenuItem(i);
+			int numberOfMenuItems = menu->numberOfMenuItems(item);
+			int id = 100 * (i + 1);
+			for (int j = 0; j < numberOfMenuItems; j++) {
+				Graphics::MacMenuItem *subItem = menu->getSubMenuItem(item, j);
+				Common::String name = menu->getName(subItem);
+
+				if (!name.empty())
+					menu->setAction(subItem, id++);
+			}
 		}
 	}
 
@@ -2340,7 +2343,7 @@ bool MacLoomGui::handleEvent(Common::Event &event) {
 			// menu hotzone, so don't allow that.
 
 			newX = CLIP(newX, 0, _surface->w - _practiceBox->w);
-			newY = CLIP(newY, 23, _surface->h - _practiceBox->h);
+			newY = CLIP(newY, _vm->isUsingOriginalGUI() ? 23 : 0, _surface->h - _practiceBox->h);
 
 			// For some reason, X coordinates can only change in
 			// increments of 16 pixels. As an enhancement, we allow


Commit: ded4903861c38abac393e26e47045faea3934434
    https://github.com/scummvm/scummvm/commit/ded4903861c38abac393e26e47045faea3934434
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Remove MacText text position hack

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 99d6b301798..b504c346b07 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -255,7 +255,7 @@ void MacGui::MacWidget::setValue(int value) {
 	setRedraw();
 }
 
-int MacGui::MacWidget::drawText(Common::String text, int x, int y, int w, Color color, Graphics::TextAlign align) {
+int MacGui::MacWidget::drawText(Common::String text, int x, int y, int w, Color color, Graphics::TextAlign align, int deltax) {
 	if (text.empty())
 		return 0;
 
@@ -315,7 +315,7 @@ int MacGui::MacWidget::drawText(Common::String text, int x, int y, int w, Color
 	int y0 = y;
 
 	for (uint i = 0; i < lines.size(); i++) {
-		font->drawString(_window->innerSurface(), lines[i], x, y0, w, color, align);
+		font->drawString(_window->innerSurface(), lines[i], x, y0, w, color, align, deltax);
 
 		if (!_enabled) {
 			Common::Rect textBox = font->getBoundingBox(lines[i], x, y0, w, align);
@@ -529,7 +529,7 @@ void MacGui::MacText::draw(bool drawFocused) {
 	debug(1, "MacGui::MacText: Drawing text %d (_fullRedraw = %d, drawFocused = %d, _value = %d)", _id, _fullRedraw, drawFocused, _value);
 
 	_window->innerSurface()->fillRect(_bounds, kWhite);
-	drawText(_text, _bounds.left, _bounds.top, _bounds.width(), kBlack);
+	drawText(_text, _bounds.left, _bounds.top, _bounds.width(), kBlack, Graphics::kTextAlignLeft, 1);
 	_window->markRectAsDirty(_bounds);
 
 	_redraw = false;
@@ -769,8 +769,6 @@ void MacGui::MacDialogWindow::addCheckbox(Common::Rect bounds, Common::String te
 }
 
 void MacGui::MacDialogWindow::addText(Common::Rect bounds, Common::String text, bool enabled) {
-	// Adjust the text position slightly
-	bounds.left++;
 	_widgets.push_back(new MacText(this, bounds, text, enabled));
 }
 
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 68816a3b295..e88a96ec5e0 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -157,7 +157,7 @@ public:
 		Common::String _text;
 		int _value = 0;
 
-		int drawText(Common::String text, int x, int y, int w, Color color, Graphics::TextAlign align = Graphics::kTextAlignLeft);
+		int drawText(Common::String text, int x, int y, int w, Color color, Graphics::TextAlign align = Graphics::kTextAlignLeft, int deltax = 0);
 
 	public:
 		MacWidget(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::String text, bool enabled);


Commit: 16e6b8938418d5cdef454728bb547aea34fe20fb
    https://github.com/scummvm/scummvm/commit/16e6b8938418d5cdef454728bb547aea34fe20fb
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Fix Mac GUI potentially uninitialized variables (hopefully)

I think they were false alarms to begin with, though.

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index b504c346b07..7730a6be70d 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -1341,6 +1341,7 @@ Graphics::Surface *MacGui::decodePictV1(Common::SeekableReadStream *res) {
 						} else {
 							// Copy values
 							repeat = false;
+							value = 0;
 						}
 
 						for (int j = 0; j <= count; j++) {
@@ -1910,7 +1911,7 @@ void MacLoomGui::runAboutDialog() {
 	int growth = -2;
 	int pattern;
 	bool darkenOnly = false;
-	int waitFrames;
+	int waitFrames = 0;
 
 	int innerBounce = 72;
 	int targetTop = 48;
@@ -1950,7 +1951,7 @@ void MacLoomGui::runAboutDialog() {
 					growth = -growth;
 			}
 		} else {
-			if (--waitFrames == 0)
+			if (--waitFrames <= 0)
 				changeScene = true;
 		}
 
@@ -2302,8 +2303,8 @@ bool MacLoomGui::handleEvent(Common::Event &event) {
 
 	while (!_vm->shouldQuit()) {
 		bool dragging = false;
-		int dragX;
-		int dragY;
+		int dragX = 0;
+		int dragY = 0;
 
 		while (_system->getEventManager()->pollEvent(event)) {
 			switch (event.type) {
@@ -3486,7 +3487,7 @@ void MacIndy3Gui::runAboutDialog() {
 	int trolleyFrame = 1;
 	int trolleyFrameDelta = 1;
 	int trolleyWaitFrames = 20;	// ~2 seconds
-	int waitFrames;
+	int waitFrames = 0;
 
 	// TODO: These strings are part of the STRS resource, but I don't know
 	// how to safely read them from there yet. So hard-coded it is for now.
@@ -3575,7 +3576,7 @@ void MacIndy3Gui::runAboutDialog() {
 		case 5:
 		case 6:
 		case 7:
-			if (--waitFrames == 0)
+			if (--waitFrames <= 0)
 				changeScene = true;
 			break;
 


Commit: 3cad2d6af31140bcab4b1bd50d1ca87157001dba
    https://github.com/scummvm/scummvm/commit/3cad2d6af31140bcab4b1bd50d1ca87157001dba
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Guard against bad font IDs in Mac charset renderer

I don't know if this can happen with savegames from any released version
of ScummVM, or if it's just my savegames while working on the Mac GUI.
But let's err on the side of caution and remap bad font IDs to the
sensible default font.

Changed paths:
    engines/scumm/charset.cpp


diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp
index ac2ee927246..ed363c85906 100644
--- a/engines/scumm/charset.cpp
+++ b/engines/scumm/charset.cpp
@@ -1637,6 +1637,16 @@ void CharsetRendererMac::setCurID(int32 id) {
 	if  (id == -1)
 		return;
 
+	// This should only happen, if it happens at all, with older savegames.
+	// Font 0 is the sensible default font for both Loom and Indy 3.
+
+	int numFonts = (_vm->_game.id == GID_LOOM) ? 1 : 2;
+
+	if (id >= numFonts) {
+		warning("CharsetRenderMac::setCurId: Invalid font id %d, using 0 instead", id);
+		id = 0;
+	}
+
 	_curId = id;
 	_font = _vm->_macGui->getFontByScummId(_curId);
 }


Commit: 4d81ee243e7770592490a1ccc4dd6242c73f0279
    https://github.com/scummvm/scummvm/commit/4d81ee243e7770592490a1ccc4dd6242c73f0279
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Add work-in-progress Mac edit text widget

Don't expect it to work yet. But I'm at a reasonably stable point, so I
want to commit it before I mess it up again.

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 7730a6be70d..a9db5bd23be 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -510,7 +510,7 @@ void MacGui::MacCheckbox::draw(bool drawFocused) {
 	_fullRedraw = false;
 }
 
-void MacGui::MacCheckbox::handleMouseUp() {
+void MacGui::MacCheckbox::handleMouseUp(Common::Event &event) {
 	_value = _value ? 0 : 1;
 	setRedraw();
 }
@@ -536,6 +536,274 @@ void MacGui::MacText::draw(bool drawFocused) {
 	_fullRedraw = false;
 }
 
+// ---------------------------------------------------------------------------
+// Editable text widget
+//
+// The current edit position is stored in _caretPos. This holds the character
+// the caret is placed right after, so 0 is the first character.
+//
+// The length of the current selection is stored in _selectionLen. Selections
+// can extend left or right of the _caretPos. This simplifies mouse selection
+// enormously.
+// ---------------------------------------------------------------------------
+
+MacGui::MacEditText::MacEditText(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::String text, bool enabled) : MacWidget(window, bounds, text, enabled) {
+	_font = _window->_gui->getFont(kSystemFont);
+
+	// This widget gets its own text surface, to ensure that the text is
+	// properly clipped to the widget's bounds. Technically, the other
+	// widgets that draw text could use this too, but there we assume that
+	// the texts are chosen to fit without clipping.
+
+	_textSurface = _window->innerSurface()->getSubArea(_bounds);
+}
+
+bool MacGui::MacEditText::findWidget(int x, int y) const {
+	if (!_visible || !_enabled)
+		return false;
+
+	// Once we start dragging the handle, any mouse position is considered
+	// within the widget.
+
+	if (_window->getFocusedWidget() == this)
+		return true;
+
+	return _bounds.contains(x, y);
+}
+
+int MacGui::MacEditText::getTextPosFromMouse(int x) {
+	int clickPos = -1;
+
+	x -= _bounds.left;
+	int textX = _textPos;
+
+	for (uint i = 0; i < _text.size(); i++) {
+		int charWidth = _font->getCharWidth(_text[i]);
+
+		if (x >= textX && x < textX + charWidth) {
+			clickPos = i;
+			break;
+		}
+
+		textX += charWidth;
+	}
+
+	return clickPos;
+}
+
+void MacGui::MacEditText::deleteSelection() {
+	_text.erase(_caretPos, _selectLen);
+	_selectLen = 0;
+}
+
+void MacGui::MacEditText::draw(bool drawFocused) {
+	if (!_visible)
+		return;
+
+	if (!_redraw && !_fullRedraw)
+		return;
+
+	Graphics::Surface *s = _window->innerSurface();
+	Common::Rect bounds = _bounds;
+
+	bounds.grow(1);
+
+	s->fillRect(_bounds, kWhite);
+	s->frameRect(bounds, kRed);
+
+	int caretX;
+
+	if (_selectLen == 0) {
+		// Make sure the caret is visible
+
+		caretX = _textPos;
+
+		for (int i = 0; i < _caretPos; i++)
+			caretX += _font->getCharWidth(_text[i]);
+
+		int delta = 0;
+
+		if (caretX < 0)
+			delta = caretX;
+		else if (caretX >= _textSurface.w)
+			delta = caretX - _textSurface.w + 1;
+
+		if (delta) {
+			_textPos -= delta;
+			caretX -= delta;
+		}
+	}
+
+	int x = _textPos;
+	int y = 0;
+
+	for (int i = 0; i < (int)_text.size() && x < _textSurface.w; i++) {
+		Color color = kBlack;
+		int charWidth = _font->getCharWidth(_text[i]);
+
+		int selectStart, selectEnd;
+
+		if (_selectLen != 0) {
+			if (_selectLen < 0) {
+				selectStart = _caretPos + _selectLen;
+				selectEnd = _caretPos - 1;
+			} else {
+				selectStart = _caretPos;
+				selectEnd = _caretPos + _selectLen - 1;
+			}
+		}
+
+		if (x + charWidth >= 0) {
+			if (_selectLen != 0 && i >= selectStart && i <= selectEnd) {
+				_textSurface.fillRect(Common::Rect(x, 0, x + charWidth, _textSurface.h), kBlack);
+				color = kWhite;
+			}
+
+			_font->drawChar(&_textSurface, _text[i], x, y, color);
+		}
+
+		x += charWidth;
+	}
+
+	if (_selectLen == 0) {
+		_textSurface.vLine(caretX, 0, _textSurface.h - 1, kRed);
+	}
+
+	_window->markRectAsDirty(bounds);
+
+	_redraw = false;
+	_fullRedraw = false;
+}
+
+void MacGui::MacEditText::handleMouseDown(Common::Event &event) {
+	uint32 now = _window->_system->getMillis();
+	int x = event.mouse.x - _bounds.left;
+
+	_caretPos = getTextPosFromMouse(event.mouse.x);
+	_selectLen = 0;
+
+	if (now - _lastClickTime < 500 && ABS(x - _lastClickX) < 5) {
+		_selectLen = 0;
+
+		if (!_text.empty()) {
+			int startPos = _caretPos;
+			int endPos = _caretPos;
+
+			if (_text[_caretPos] == ' ') {
+				while (startPos >= 0) {
+					if (_text[startPos] != ' ') {
+						startPos++;
+						break;
+					}
+					startPos--;
+				}
+
+				while (endPos < (int)_text.size()) {
+					if (_text[endPos] != ' ') {
+						endPos--;
+						break;
+					}
+					endPos++;
+				}
+			} else {
+				while (startPos >= 0) {
+					if (_text[startPos] == ' ') {
+						startPos++;
+						break;
+					}
+					startPos--;
+				}
+
+				while (endPos < (int)_text.size()) {
+					if (_text[endPos] == ' ') {
+						endPos--;
+						break;
+					}
+					endPos++;
+				}
+			}
+
+			_caretPos = startPos;
+			_selectLen = endPos - startPos + 1;
+		}
+
+		_lastClickTime = 0;
+		_lastClickX = 0;
+	} else {
+		_lastClickTime = now;
+		_lastClickX = x;
+		_selectLen = 0;
+	}
+
+	_redraw = true;
+}
+
+bool MacGui::MacEditText::handleKeyDown(Common::Event &event) {
+	if (event.kbd.flags & (Common::KBD_CTRL | Common::KBD_ALT | Common::KBD_META))
+		return false;
+
+	switch (event.kbd.keycode) {
+	case Common::KEYCODE_LEFT:
+		if (_caretPos > 0) {
+			_caretPos--;
+		}
+		_selectLen = 0;
+		return true;
+
+	case Common::KEYCODE_RIGHT:
+		if (_caretPos < (int)_text.size()) {
+			if (_selectLen > 0)
+				_caretPos += _selectLen;
+			_caretPos++;
+		}
+		return true;
+
+	case Common::KEYCODE_BACKSPACE:
+		if (_selectLen != 0) {
+			deleteSelection();
+		} else if (_caretPos > 0) {
+			_caretPos--;
+			_text.deleteChar(_caretPos);
+		}
+		return true;
+
+	case Common::KEYCODE_DELETE:
+		if (_selectLen != 0) {
+			deleteSelection();
+		} else if (_caretPos < (int)_text.size()) {
+			_text.deleteChar(_caretPos);
+		}
+		return true;
+
+	default:
+		break;
+	}
+
+	int c = event.kbd.ascii;
+
+	if (c >= 32 && c <= 127) {
+		if (_selectLen != 0)
+			deleteSelection();
+		_text.insertChar(event.kbd.ascii, _caretPos);
+		_caretPos++;
+		return true;
+	}
+
+	return false;
+}
+
+void MacGui::MacEditText::handleMouseMove(Common::Event &event) {
+	int oldSelectLen = _selectLen;
+
+	int pos = getTextPosFromMouse(event.mouse.x);
+
+	_selectLen = pos - _caretPos;
+
+	if (_selectLen != oldSelectLen) {
+		setRedraw();
+	}
+}
+
 // ---------------------------------------------------------------------------
 // Image widget
 // ---------------------------------------------------------------------------
@@ -623,19 +891,19 @@ void MacGui::MacSlider::draw(bool drawFocused) {
 	_fullRedraw = false;
 }
 
-void MacGui::MacSlider::handleMouseDown() {
-	int mouseX = _window->getMousePos().x - _bounds.left;
+void MacGui::MacSlider::handleMouseDown(Common::Event &event) {
+	int mouseX = event.mouse.x;
 	int handleWidth = _handle->getBounds().width();
 
 	if (mouseX >= _handleX && mouseX < _handleX + handleWidth)
-		_grabOffset = _window->getMousePos().x - _bounds.left - _handleX;
+		_grabOffset = event.mouse.x - _bounds.left - _handleX;
 	else
 		_grabOffset = handleWidth / 2;
 
-	handleMouseMove();
+	handleMouseMove(event);
 }
 
-void MacGui::MacSlider::handleMouseUp() {
+void MacGui::MacSlider::handleMouseUp(Common::Event &event) {
 	int posRange = (_maxX - _rightMargin) - (_minX + _rightMargin);
 	int posOffset = _handleX - (_minX + _leftMargin);
 
@@ -645,8 +913,8 @@ void MacGui::MacSlider::handleMouseUp() {
 	setValue(_minValue + valueOffset);
 }
 
-void MacGui::MacSlider::handleMouseMove() {
-	_handleX = CLIP<int>(_window->getMousePos().x - _bounds.left - _grabOffset, _minX, _maxX);
+void MacGui::MacSlider::handleMouseMove(Common::Event &event) {
+	_handleX = CLIP<int>(event.mouse.x - _bounds.left - _grabOffset, _minX, _maxX);
 	setRedraw();
 }
 
@@ -772,6 +1040,10 @@ void MacGui::MacDialogWindow::addText(Common::Rect bounds, Common::String text,
 	_widgets.push_back(new MacText(this, bounds, text, enabled));
 }
 
+void MacGui::MacDialogWindow::addEditText(Common::Rect bounds, Common::String text, bool enabled) {
+	_widgets.push_back(new MacEditText(this, bounds, text, enabled));
+}
+
 void MacGui::MacDialogWindow::addPicture(Common::Rect bounds, int id, bool enabled) {
 	_widgets.push_back(new MacPicture(this, bounds, id, false));
 }
@@ -859,7 +1131,7 @@ int MacGui::MacDialogWindow::runDialog() {
 				buttonPressed = true;
 				setFocusedWidget(event.mouse.x, event.mouse.y);
 				if (_focusedWidget)
-					_focusedWidget->handleMouseDown();
+					_focusedWidget->handleMouseDown(event);
 				break;
 
 			case Common::EVENT_LBUTTONUP:
@@ -867,7 +1139,7 @@ int MacGui::MacDialogWindow::runDialog() {
 
 				if (_focusedWidget && _focusedWidget->findWidget(event.mouse.x, event.mouse.y)) {
 					widgetId = _focusedWidget->getId();
-					_focusedWidget->handleMouseUp();
+					_focusedWidget->handleMouseUp(event);
 					clearFocusedWidget();
 					return widgetId;
 				}
@@ -881,12 +1153,17 @@ int MacGui::MacDialogWindow::runDialog() {
 						_focusedWidget->setRedraw();
 					}
 
-					_focusedWidget->handleMouseMove();
+					_focusedWidget->handleMouseMove(event);
 				}
 				break;
 
 			case Common::EVENT_KEYDOWN:
-				if (!buttonPressed && event.kbd.keycode == Common::KEYCODE_RETURN) {
+				// Ignore keyboard while mouse is pressed
+				if (buttonPressed)
+					break;
+
+				// Handle default button
+				if (event.kbd.keycode == Common::KEYCODE_RETURN) {
 					MacWidget *widget = getDefaultWidget();
 					if (widget && widget->isEnabled()) {
 						for (int i = 0; i < 2; i++) {
@@ -899,9 +1176,20 @@ int MacGui::MacDialogWindow::runDialog() {
 								_system->updateScreen();
 							}
 						}
+
 						return widget->getId();
 					}
 				}
+
+				// Otherwise, give widgets a chance to react
+				// to key presses.
+				for (uint i = 0; i < _widgets.size(); i++) {
+					if (_widgets[i]->handleKeyDown(event)) {
+						_widgets[i]->setRedraw();
+						break;
+					}
+				}
+
 				break;
 
 			default:
@@ -1676,7 +1964,7 @@ MacGui::MacDialogWindow *MacGui::createDialog(int dialogId) {
 
 			case 16:
 				// Editable text
-				window->innerSurface()->frameRect(r, kGreen);
+				window->addEditText(r, "Lorem ipsum dolor sit amet, consectetur adipisicing elit", enabled);
 				res->skip(len);
 				break;
 
@@ -2555,7 +2843,7 @@ bool MacIndy3Gui::Button::handleEvent(Common::Event &event) {
 	bool caughtEvent = false;
 
 	if (event.type == Common::EVENT_KEYDOWN) {
-		if (!(event.kbd.flags & Common::KBD_NON_STICKY) && event.kbd.keycode == vs->key)
+		if (!(event.kbd.flags & (Common::KBD_CTRL | Common::KBD_ALT | Common::KBD_META)) && event.kbd.keycode == vs->key)
 			caughtEvent = true;
 	} else if (event.type == Common::EVENT_LBUTTONDOWN) {
 		if (_bounds.contains(event.mouse))
@@ -3912,6 +4200,10 @@ void MacIndy3Gui::resetAfterLoad() {
 		if (_vm->_verbs[i].verbid >= 102 && _vm->_verbs[i].verbid <= 108)
 			_vm->killVerb(i);
 	}
+
+	int charsetId = _vm->_charset->getCurID();
+	if (charsetId < 0 || charsetId > 1)
+		_vm->_charset->setCurID(0);
 }
 
 void MacIndy3Gui::update(int delta) {
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index e88a96ec5e0..f6fd4c84ccd 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -182,9 +182,11 @@ public:
 		virtual bool findWidget(int x, int y) const;
 
 		virtual void draw(bool drawFocused = false) = 0;
-		virtual void handleMouseDown() {}
-		virtual void handleMouseUp() {}
-		virtual void handleMouseMove() {}
+
+		virtual void handleMouseDown(Common::Event &event) {}
+		virtual void handleMouseUp(Common::Event &event) {}
+		virtual void handleMouseMove(Common::Event &event) {}
+		virtual bool handleKeyDown(Common::Event &event) { return false; }
 	};
 
 	class MacButton : public MacWidget {
@@ -212,7 +214,7 @@ public:
 
 		bool findWidget(int x, int y) const;
 		void draw(bool drawFocused = false);
-		void handleMouseUp();
+		void handleMouseUp(Common::Event &event);
 	};
 
 	// The dialogs add texts as disabled, but we don't want it to be drawn
@@ -226,6 +228,36 @@ public:
 		void draw(bool drawFocused = false);
 	};
 
+	class MacEditText : public MacWidget {
+	private:
+		int _textPos = 0;
+		int _selectLen = 0;
+		int _caretPos = 0;
+
+		uint32 _lastClickTime = 0;
+		uint32 _lastClickX = 0;
+
+		uint32 _lastBlinkTime = 0;
+		bool _caretVisible = false;
+
+		const Graphics::Font *_font;
+		Graphics::Surface _textSurface;
+
+		int getTextPosFromMouse(int x);
+		void deleteSelection();
+
+	public:
+		MacEditText(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::String text, bool enabled);
+
+		bool findWidget(int x, int y) const;
+
+		void draw(bool drawFocused = false);
+
+		void handleMouseDown(Common::Event &event);
+		bool handleKeyDown(Common::Event &event);
+		void handleMouseMove(Common::Event &event);
+	};
+
 	class MacPicture : public MacWidget {
 	private:
 		Graphics::Surface *_picture = nullptr;
@@ -264,14 +296,13 @@ public:
 		void setValue(int value);
 		void draw(bool drawFocused = false);
 
-		void handleMouseDown();
-		void handleMouseUp();
-		void handleMouseMove();
+		void handleMouseDown(Common::Event &event);
+		void handleMouseUp(Common::Event &event);
+		void handleMouseMove(Common::Event &event);
 	};
 
 	class MacDialogWindow {
 	private:
-		OSystem *_system;
 		Common::Rect _bounds;
 		int _margin;
 
@@ -299,6 +330,7 @@ public:
 		void copyToScreen(Graphics::Surface *s = nullptr) const;
 
 	public:
+		OSystem *_system;
 		MacGui *_gui;
 
 		MacDialogWindow(MacGui *gui, OSystem *system, Graphics::Surface *from, Common::Rect bounds, MacDialogWindowStyle style = kStyleNormal);
@@ -332,6 +364,7 @@ public:
 		void addButton(Common::Rect bounds, Common::String text, bool enabled);
 		void addCheckbox(Common::Rect bounds, Common::String text, bool enabled);
 		void addText(Common::Rect bounds, Common::String text, bool enabled);
+		void addEditText(Common::Rect bounds, Common::String text, bool enabled);
 		void addPicture(Common::Rect bounds, int id, bool enabled);
 		void addSlider(int backgroundId, int handleId, bool enabled, int minX, int maxX, int minValue, int maxValue, int leftMargin = 0, int rightMargin = 0);
 


Commit: 22a3f619ff3759bea89bc32e474140d59d051e52
    https://github.com/scummvm/scummvm/commit/22a3f619ff3759bea89bc32e474140d59d051e52
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Hopefully fix Windows build errors

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index a9db5bd23be..23c77c3afc8 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -611,7 +611,7 @@ void MacGui::MacEditText::draw(bool drawFocused) {
 	s->fillRect(_bounds, kWhite);
 	s->frameRect(bounds, kRed);
 
-	int caretX;
+	int caretX = 0;
 
 	if (_selectLen == 0) {
 		// Make sure the caret is visible
@@ -641,7 +641,8 @@ void MacGui::MacEditText::draw(bool drawFocused) {
 		Color color = kBlack;
 		int charWidth = _font->getCharWidth(_text[i]);
 
-		int selectStart, selectEnd;
+		int selectStart = -1;
+		int selectEnd = -1;
 
 		if (_selectLen != 0) {
 			if (_selectLen < 0) {
@@ -1838,12 +1839,9 @@ MacGui::MacDialogWindow *MacGui::drawBanner(char *message) {
 int MacGui::delay(uint32 ms) {
 	uint32 to;
 
-	if (ms == (uint32)-1)
-		to = 0xFFFFFFFF;
-	else
-		to = _system->getMillis() + ms;
+	to = _system->getMillis() + ms;
 
-	while (_system->getMillis() < to) {
+	while (ms == 0 || _system->getMillis() < to) {
 		Common::Event event;
 
 		while (_system->getEventManager()->pollEvent(event)) {
@@ -2353,7 +2351,7 @@ void MacLoomGui::runAboutDialog() {
 	}
 
 	if (status != 2)
-		delay(-1);
+		delay();
 
 	_windowManager->popCursor();
 
@@ -3950,7 +3948,7 @@ void MacIndy3Gui::runAboutDialog() {
 	}
 
 	if (status != 2)
-		delay(-1);
+		delay();
 
 	_windowManager->popCursor();
 
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index f6fd4c84ccd..7076ea82722 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -235,7 +235,7 @@ public:
 		int _caretPos = 0;
 
 		uint32 _lastClickTime = 0;
-		uint32 _lastClickX = 0;
+		int _lastClickX = 0;
 
 		uint32 _lastBlinkTime = 0;
 		bool _caretVisible = false;
@@ -422,7 +422,7 @@ public:
 
 	MacDialogWindow *drawBanner(char *message);
 
-	int delay(uint32 ms);
+	int delay(uint32 ms = 0);
 
 	MacDialogWindow *createWindow(Common::Rect bounds, MacDialogWindowStyle style = kStyleNormal);
 	MacDialogWindow *createDialog(int dialogId);


Commit: 9d0e5968640854bd9c0e4ac401328803a963f0c9
    https://github.com/scummvm/scummvm/commit/9d0e5968640854bd9c0e4ac401328803a963f0c9
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Fix a few more selection behaviors in Mac text widget

What remains to be done, at the very least, is: Blinking caret,
select-dragging past the left and right edges of the widget, maximum
string length, clipboard handling, input of non-ASCII characters, and
smarter redrawing.

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 23c77c3afc8..9a3c0cd6923 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -571,8 +571,14 @@ bool MacGui::MacEditText::findWidget(int x, int y) const {
 	return _bounds.contains(x, y);
 }
 
-int MacGui::MacEditText::getTextPosFromMouse(int x) {
-	int clickPos = -1;
+int MacGui::MacEditText::getTextPosFromMouse(int x, int y) {
+	if (y < _bounds.top)
+		return 0;
+
+	if (y >= _bounds.bottom)
+		return _text.size();
+
+	int textPos = _text.size();
 
 	x -= _bounds.left;
 	int textX = _textPos;
@@ -581,18 +587,30 @@ int MacGui::MacEditText::getTextPosFromMouse(int x) {
 		int charWidth = _font->getCharWidth(_text[i]);
 
 		if (x >= textX && x < textX + charWidth) {
-			clickPos = i;
+			textPos = i;
 			break;
 		}
 
 		textX += charWidth;
 	}
 
-	return clickPos;
+	return textPos;
 }
 
 void MacGui::MacEditText::deleteSelection() {
-	_text.erase(_caretPos, _selectLen);
+	int startPos;
+	int len;
+
+	if (_selectLen < 0) {
+		startPos = _caretPos + _selectLen;
+		len = -_selectLen;
+	} else {
+		startPos = _caretPos;
+		len = _selectLen;
+	}
+
+	_text.erase(startPos, len);
+	_caretPos = startPos;
 	_selectLen = 0;
 }
 
@@ -606,17 +624,17 @@ void MacGui::MacEditText::draw(bool drawFocused) {
 	Graphics::Surface *s = _window->innerSurface();
 	Common::Rect bounds = _bounds;
 
-	bounds.grow(1);
+	bounds.grow(3);
 
 	s->fillRect(_bounds, kWhite);
-	s->frameRect(bounds, kRed);
+	s->frameRect(bounds, kBlack);
 
 	int caretX = 0;
 
 	if (_selectLen == 0) {
 		// Make sure the caret is visible
 
-		caretX = _textPos;
+		caretX = _textPos - 1;
 
 		for (int i = 0; i < _caretPos; i++)
 			caretX += _font->getCharWidth(_text[i]);
@@ -680,7 +698,7 @@ void MacGui::MacEditText::handleMouseDown(Common::Event &event) {
 	uint32 now = _window->_system->getMillis();
 	int x = event.mouse.x - _bounds.left;
 
-	_caretPos = getTextPosFromMouse(event.mouse.x);
+	_caretPos = getTextPosFromMouse(event.mouse.x, event.mouse.y);
 	_selectLen = 0;
 
 	if (now - _lastClickTime < 500 && ABS(x - _lastClickX) < 5) {
@@ -745,18 +763,27 @@ bool MacGui::MacEditText::handleKeyDown(Common::Event &event) {
 
 	switch (event.kbd.keycode) {
 	case Common::KEYCODE_LEFT:
-		if (_caretPos > 0) {
-			_caretPos--;
+		if (_selectLen < 0) {
+			_caretPos = _caretPos + _selectLen;
+			_selectLen = -_selectLen;
 		}
+
+		if (_caretPos > 0)
+			_caretPos--;
+
 		_selectLen = 0;
 		return true;
 
 	case Common::KEYCODE_RIGHT:
-		if (_caretPos < (int)_text.size()) {
-			if (_selectLen > 0)
-				_caretPos += _selectLen;
-			_caretPos++;
+		if (_selectLen > 0) {
+			_caretPos += _selectLen;
+			_selectLen = -_selectLen;
 		}
+
+		if (_caretPos < (int)_text.size())
+			_caretPos++;
+
+		_selectLen = 0;
 		return true;
 
 	case Common::KEYCODE_BACKSPACE:
@@ -796,7 +823,7 @@ bool MacGui::MacEditText::handleKeyDown(Common::Event &event) {
 void MacGui::MacEditText::handleMouseMove(Common::Event &event) {
 	int oldSelectLen = _selectLen;
 
-	int pos = getTextPosFromMouse(event.mouse.x);
+	int pos = getTextPosFromMouse(event.mouse.x, event.mouse.y);
 
 	_selectLen = pos - _caretPos;
 
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 7076ea82722..e7bd4ba60ee 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -235,6 +235,8 @@ public:
 		int _caretPos = 0;
 
 		uint32 _lastClickTime = 0;
+		uint32 _lastScrollTime = 0;
+
 		int _lastClickX = 0;
 
 		uint32 _lastBlinkTime = 0;
@@ -243,7 +245,7 @@ public:
 		const Graphics::Font *_font;
 		Graphics::Surface _textSurface;
 
-		int getTextPosFromMouse(int x);
+		int getTextPosFromMouse(int x, int y);
 		void deleteSelection();
 
 	public:


Commit: a06163b767d12564c5343274dbfe5f4f7028db18
    https://github.com/scummvm/scummvm/commit/a06163b767d12564c5343274dbfe5f4f7028db18
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Continue work on editable Mac text widget

I've also made an early mock-up of Loom save/load dialogs, which could
be useful for testing this an possibly other remaining widgets.

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 9a3c0cd6923..6eb09167211 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -555,7 +555,10 @@ MacGui::MacEditText::MacEditText(MacGui::MacDialogWindow *window, Common::Rect b
 	// widgets that draw text could use this too, but there we assume that
 	// the texts are chosen to fit without clipping.
 
-	_textSurface = _window->innerSurface()->getSubArea(_bounds);
+	Common::Rect textBounds = _bounds;
+	textBounds.left++;
+
+	_textSurface = _window->innerSurface()->getSubArea(textBounds);
 }
 
 bool MacGui::MacEditText::findWidget(int x, int y) const {
@@ -572,15 +575,15 @@ bool MacGui::MacEditText::findWidget(int x, int y) const {
 }
 
 int MacGui::MacEditText::getTextPosFromMouse(int x, int y) {
-	if (y < _bounds.top)
+	if (_text.empty() || y < _bounds.top || x < _bounds.left)
 		return 0;
 
-	if (y >= _bounds.bottom)
+	if (x >= _bounds.right || y >= _bounds.bottom)
 		return _text.size();
 
-	int textPos = _text.size();
-
 	x -= _bounds.left;
+
+	int textPos = 0;
 	int textX = _textPos;
 
 	for (uint i = 0; i < _text.size(); i++) {
@@ -622,12 +625,8 @@ void MacGui::MacEditText::draw(bool drawFocused) {
 		return;
 
 	Graphics::Surface *s = _window->innerSurface();
-	Common::Rect bounds = _bounds;
-
-	bounds.grow(3);
 
 	s->fillRect(_bounds, kWhite);
-	s->frameRect(bounds, kBlack);
 
 	int caretX = 0;
 
@@ -652,43 +651,59 @@ void MacGui::MacEditText::draw(bool drawFocused) {
 		}
 	}
 
+	int selectStart = -1;
+	int selectEnd = -1;
+
+	if (_selectLen != 0) {
+		if (_selectLen < 0) {
+			selectStart = _caretPos + _selectLen;
+			selectEnd = _caretPos - 1;
+		} else {
+			selectStart = _caretPos;
+			selectEnd = _caretPos + _selectLen - 1;
+		}
+	}
+
 	int x = _textPos;
 	int y = 0;
 
+	bool firstChar = true;
+	bool firstCharSelected = false;
+	bool lastCharSelected = false;
+
 	for (int i = 0; i < (int)_text.size() && x < _textSurface.w; i++) {
 		Color color = kBlack;
 		int charWidth = _font->getCharWidth(_text[i]);
 
-		int selectStart = -1;
-		int selectEnd = -1;
-
-		if (_selectLen != 0) {
-			if (_selectLen < 0) {
-				selectStart = _caretPos + _selectLen;
-				selectEnd = _caretPos - 1;
-			} else {
-				selectStart = _caretPos;
-				selectEnd = _caretPos + _selectLen - 1;
-			}
-		}
-
 		if (x + charWidth >= 0) {
 			if (_selectLen != 0 && i >= selectStart && i <= selectEnd) {
+				if (firstChar)
+					firstCharSelected = true;
+				lastCharSelected = true;
+
 				_textSurface.fillRect(Common::Rect(x, 0, x + charWidth, _textSurface.h), kBlack);
 				color = kWhite;
-			}
+			} else
+				lastCharSelected = false;
 
 			_font->drawChar(&_textSurface, _text[i], x, y, color);
+			firstChar = false;
 		}
 
 		x += charWidth;
 	}
 
+	if (firstCharSelected)
+		_window->innerSurface()->fillRect(Common::Rect(_bounds.left + 1, _bounds.top, _bounds.left + 2, _bounds.bottom), kGreen);
+
+	if (lastCharSelected)
+		_window->innerSurface()->fillRect(Common::Rect(_bounds.left + x + 1, _bounds.top, _bounds.right, _bounds.bottom), kGreen);
+
 	if (_selectLen == 0) {
 		_textSurface.vLine(caretX, 0, _textSurface.h - 1, kRed);
 	}
 
-	_window->markRectAsDirty(bounds);
+	_window->markRectAsDirty(_bounds);
 
 	_redraw = false;
 	_fullRedraw = false;
@@ -1138,9 +1153,11 @@ int MacGui::MacDialogWindow::runDialog() {
 	// up to the caller to repeat the calls to runDialog() until the dialog
 	// has ended.
 
+	bool buttonPressed = false;
+	uint32 nextMouseRepeat = 0;
+
 	while (!_gui->_vm->shouldQuit()) {
 		Common::Event event;
-		bool buttonPressed = false;
 		int widgetId = -1;
 
 		while (_system->getEventManager()->pollEvent(event)) {
@@ -1157,6 +1174,7 @@ int MacGui::MacDialogWindow::runDialog() {
 			switch (event.type) {
 			case Common::EVENT_LBUTTONDOWN:
 				buttonPressed = true;
+				nextMouseRepeat = _system->getMillis() + 100;
 				setFocusedWidget(event.mouse.x, event.mouse.y);
 				if (_focusedWidget)
 					_focusedWidget->handleMouseDown(event);
@@ -1225,6 +1243,11 @@ int MacGui::MacDialogWindow::runDialog() {
 			}
 		}
 
+		if (_focusedWidget && _system->getMillis() > nextMouseRepeat) {
+			nextMouseRepeat = _system->getMillis() + 100;
+			_focusedWidget->handleMouseHeld();
+		}
+
 		_system->delayMillis(10);
 		update();
 		_system->updateScreen();
@@ -1743,7 +1766,7 @@ bool MacGui::handleMenu(int id, Common::String &name) {
 	return false;
 }
 
-bool MacGui::runQuitDialog() {
+bool MacGui::runOkCancelDialog(Common::String text) {
 	// Widgets:
 	//
 	// 0 - Okay button
@@ -1753,7 +1776,7 @@ bool MacGui::runQuitDialog() {
 	MacDialogWindow *window = createDialog(502);
 
 	window->setDefaultWidget(0);
-	window->addSubstitution("Are you sure you want to quit?");
+	window->addSubstitution(text);
 
 	// When quitting, the default action is to quit
 	bool ret = true;
@@ -1774,35 +1797,12 @@ bool MacGui::runQuitDialog() {
 	return ret;
 }
 
-bool MacGui::runRestartDialog() {
-	// Widgets:
-	//
-	// 0 - Okay button
-	// 1 - Cancel button
-	// 2 - "^0" text
-
-	MacDialogWindow *window = createDialog(502);
-
-	window->setDefaultWidget(0);
-	window->addSubstitution("Are you sure you want to restart this game from the beginning?");
-
-	// When quitting, the default action is to not restart
-	bool ret = false;
-
-	while (!_vm->shouldQuit()) {
-		int clicked = window->runDialog();
-
-		if (clicked == 0) {
-			ret = true;
-			break;
-		}
-
-		if (clicked == 1)
-			break;
-	}
+bool MacGui::runQuitDialog() {
+	return runOkCancelDialog("Are you sure you want to quit?");
+}
 
-	delete window;
-	return ret;
+bool MacGui::runRestartDialog() {
+	return runOkCancelDialog("Are you sure you want to restart this game from the beginning?");
 }
 
 Common::String MacGui::getDialogString(Common::SeekableReadStream *res, int len) {
@@ -2390,18 +2390,90 @@ void MacLoomGui::runAboutDialog() {
 	delete window;
 }
 
+// A standard file picker dialog doesn't really make sense in ScummVM, so we
+// make something that just looks similar to one.
+
 bool MacLoomGui::runOpenDialog() {
-	// Standard file picker dialog. We don't yet have one, and it might
-	// not make sense for ScummVM.
-	warning("MacLoomGui::runOpenDialog()");
-	return true;
+	Common::Rect bounds(88, 28, 448, 208);
+
+	MacDialogWindow *window = createWindow(bounds);
+
+	window->addButton(Common::Rect(254, 135, 334, 155), "Open", true);
+	window->addButton(Common::Rect(254, 104, 334, 124), "Cancel", true);
+	window->addButton(Common::Rect(254, 59, 334, 79), "Delete", true);
+
+	Graphics::Surface *s = window->innerSurface();
+
+	s->frameRect(Common::Rect(14, 13, 217, 159), kBlack);
+	s->frameRect(Common::Rect(216, 13, 232, 159), kBlack);
+	s->hLine(253, 91, 334, kBlack);
+
+	window->setDefaultWidget(0);
+
+	// When quitting, the default action is to not open a saved game
+	bool ret = false;
+
+	while (!_vm->shouldQuit()) {
+		int clicked = window->runDialog();
+
+		if (clicked == 0) {
+			ret = true;
+			break;
+		}
+
+		if (clicked == 1)
+			break;
+	}
+
+	delete window;
+	return ret;
 }
 
 bool MacLoomGui::runSaveDialog() {
-	// Standard file picker dialog. We don't yet have one, and it might
-	// not make sense for ScummVM.
-	warning("MacLoomGui::runSaveDialog()");
-	return true;
+	Common::Rect bounds(110, 27, 470, 231);
+
+	MacDialogWindow *window = createWindow(bounds);
+
+	window->addButton(Common::Rect(254, 159, 334, 179), "Save", true);
+	window->addButton(Common::Rect(254, 128, 334, 148), "Cancel", true);
+	window->addButton(Common::Rect(254, 83, 334, 103), "Delete", true);
+
+	window->addEditText(Common::Rect(16, 164, 229, 180), "Game file", true);
+
+	Graphics::Surface *s = window->innerSurface();
+	const Graphics::Font *font = getFont(kSystemFont);
+
+	s->frameRect(Common::Rect(14, 161, 232, 183), kBlack);
+	s->frameRect(Common::Rect(14, 9, 217, 137), kBlack);
+	s->frameRect(Common::Rect(216, 9, 232, 137), kBlack);
+
+	s->hLine(253, 115, 334, kBlack);
+
+	font->drawString(s, "Save Game File as...", 14, 143, 218, kBlack, Graphics::kTextAlignLeft, 4);
+
+	window->setDefaultWidget(0);
+
+	// When quitting, the default action is to not open a saved game
+	bool ret = false;
+
+	while (!_vm->shouldQuit()) {
+		int clicked = window->runDialog();
+
+		if (clicked == 0) {
+			ret = true;
+			break;
+		}
+
+		if (clicked == 1)
+			break;
+
+		if (clicked == 2) {
+			runOkCancelDialog("Are you sure you want to delete the saved game?");
+		}
+	}
+
+	delete window;
+	return ret;
 }
 
 bool MacLoomGui::runOptionsDialog() {
@@ -3722,7 +3794,6 @@ bool MacIndy3Gui::handleMenu(int id, Common::String &name) {
 	if (MacGui::handleMenu(id, name))
 		return true;
 
-	int dialogId = -1;
 	Common::StringArray substitutions;
 
 	switch (id) {
@@ -3745,12 +3816,6 @@ bool MacIndy3Gui::handleMenu(int id, Common::String &name) {
 		break;
 	}
 
-	if (dialogId != -1) {
-		MacDialogWindow *dialog = createDialog(dialogId);
-		dialog->runDialog();
-		return true;
-	}
-
 	return false;
 }
 
@@ -4017,7 +4082,7 @@ bool MacIndy3Gui::runOpenDialog() {
 	window->addSubstitution(Common::String::format("%d", _vm->VAR(244)));
 	window->addSubstitution(Common::String::format("%d", _vm->VAR(245)));
 
-	// When quitting, the default action is not to open a saved game
+	// When quitting, the default action is to not open a saved game
 	bool ret = false;
 
 	while (!_vm->shouldQuit()) {
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index e7bd4ba60ee..acc5289185e 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -127,6 +127,7 @@ protected:
 	virtual bool runSaveDialog() = 0;
 	virtual bool runOptionsDialog() = 0;
 
+	bool runOkCancelDialog(Common::String text);
 	bool runQuitDialog();
 	bool runRestartDialog();
 
@@ -186,6 +187,7 @@ public:
 		virtual void handleMouseDown(Common::Event &event) {}
 		virtual void handleMouseUp(Common::Event &event) {}
 		virtual void handleMouseMove(Common::Event &event) {}
+		virtual void handleMouseHeld() {}
 		virtual bool handleKeyDown(Common::Event &event) { return false; }
 	};
 


Commit: 95e66a0355f318a8cb1aff84f2b3129dad03770f
    https://github.com/scummvm/scummvm/commit/95e66a0355f318a8cb1aff84f2b3129dad03770f
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Change cursor to Mac "beam" when over a text edit widget

Unfortunately, ScummVM doesn't draw this cursor correctly. It's supposed
to invert the background it's over. For some reason, the original Indy 3
doesn't change the cursor like this in its save dialog, but I think I'll
ignore that.

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 6eb09167211..20b40cc8046 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -233,7 +233,6 @@ MacGui::MacWidget::MacWidget(MacGui::MacDialogWindow *window, Common::Rect bound
 		_visible = false;
 }
 
-
 bool MacGui::MacWidget::findWidget(int x, int y) const {
 	return _visible && _enabled && _bounds.contains(x, y);
 }
@@ -543,8 +542,9 @@ void MacGui::MacText::draw(bool drawFocused) {
 // the caret is placed right after, so 0 is the first character.
 //
 // The length of the current selection is stored in _selectionLen. Selections
-// can extend left or right of the _caretPos. This simplifies mouse selection
-// enormously.
+// can extend left or right of the _caretPos. This makes it slightly more
+// tricky to handle selections after they've been made, but simplifies the
+// actual selection by mouse enormously.
 // ---------------------------------------------------------------------------
 
 MacGui::MacEditText::MacEditText(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::String text, bool enabled) : MacWidget(window, bounds, text, enabled) {
@@ -561,6 +561,16 @@ MacGui::MacEditText::MacEditText(MacGui::MacDialogWindow *window, Common::Rect b
 	_textSurface = _window->innerSurface()->getSubArea(textBounds);
 }
 
+Graphics::MacGUIConstants::MacCursorType MacGui::MacEditText::getCursorType() const {
+	// If the widget is found, that implies that it's active.
+
+	// Note: The text widget in the Indy 3 save dialog does not change the
+	// cursor in the original. But since we're not going to emulate it
+	// that closely, I don't think we need to mark this as an enhancement.
+
+	return Graphics::MacGUIConstants::kMacCursorBeam;
+}
+
 bool MacGui::MacEditText::findWidget(int x, int y) const {
 	if (!_visible || !_enabled)
 		return false;
@@ -575,15 +585,15 @@ bool MacGui::MacEditText::findWidget(int x, int y) const {
 }
 
 int MacGui::MacEditText::getTextPosFromMouse(int x, int y) {
-	if (_text.empty() || y < _bounds.top || x < _bounds.left)
+	if (_text.empty() || y < _bounds.top || x < _bounds.left + 1)
 		return 0;
 
 	if (x >= _bounds.right || y >= _bounds.bottom)
-		return _text.size();
+		return _text.size() - 1;
 
 	x -= _bounds.left;
 
-	int textPos = 0;
+	int textPos = _text.size() - 1;
 	int textX = _textPos;
 
 	for (uint i = 0; i < _text.size(); i++) {
@@ -1018,6 +1028,9 @@ MacGui::MacDialogWindow::MacDialogWindow(MacGui *gui, OSystem *system, Graphics:
 }
 
 MacGui::MacDialogWindow::~MacDialogWindow() {
+	if (_gui->_windowManager->getCursorType() != Graphics::MacGUIConstants::kMacCursorArrow)
+		_gui->_windowManager->replaceCursor(Graphics::MacGUIConstants::kMacCursorArrow);
+
 	copyToScreen(_backup);
 	_backup->free();
 	delete _backup;
@@ -1200,7 +1213,14 @@ int MacGui::MacDialogWindow::runDialog() {
 					}
 
 					_focusedWidget->handleMouseMove(event);
+				} else {
+					int mouseOverWidget = findWidget(_mousePos.x, _mousePos.y);
+					Graphics::MacGUIConstants::MacCursorType cursorType = (mouseOverWidget >= 0) ? _widgets[mouseOverWidget]->getCursorType() : Graphics::MacGUIConstants::kMacCursorArrow;
+
+					if (_gui->_windowManager->getCursorType() != cursorType)
+						_gui->_windowManager->replaceCursor(cursorType);
 				}
+
 				break;
 
 			case Common::EVENT_KEYDOWN:
@@ -1838,11 +1858,11 @@ void MacGui::updateWindowManager() {
 	if (isActive) {
 		if (!_menuIsActive) {
 			_cursorWasVisible = CursorMan.showMouse(true);
-			_windowManager->pushCursor(Graphics::kMacCursorArrow);
+			_windowManager->pushCursor(Graphics::MacGUIConstants::kMacCursorArrow);
 		}
 	} else {
 		if (_menuIsActive) {
-			if (_windowManager->getCursorType() == Graphics::kMacCursorArrow)
+			if (_windowManager->getCursorType() == Graphics::MacGUIConstants::kMacCursorArrow)
 				_windowManager->popCursor();
 			CursorMan.showMouse(_cursorWasVisible);
 		}
@@ -2097,7 +2117,7 @@ void MacLoomGui::setupCursor(int &width, int &height, int &hotspotX, int &hotspo
 		hotspotY = macCursor.getHotspotY();
 		animate = 0;
 
-		_windowManager->replaceCursor(Graphics::kMacCursorCustom, &macCursor);
+		_windowManager->replaceCursor(Graphics::MacGUIConstants::kMacCursorCustom, &macCursor);
 	}
 
 	delete curs;
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index acc5289185e..0b845422a0e 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -23,6 +23,7 @@
 #define SCUMM_GFX_MAC_H
 
 #include "graphics/font.h"
+#include "graphics/macgui/macwindowmanager.h"
 
 class OSystem;
 
@@ -180,6 +181,7 @@ public:
 		virtual void setValue(int value);
 		int getValue() const { return _value; }
 
+		virtual Graphics::MacGUIConstants::MacCursorType getCursorType() const { return Graphics::MacGUIConstants::kMacCursorArrow; }
 		virtual bool findWidget(int x, int y) const;
 
 		virtual void draw(bool drawFocused = false) = 0;
@@ -226,7 +228,9 @@ public:
 	class MacText : public MacWidget {
 	public:
 		MacText(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::String text, bool enabled) : MacWidget(window, bounds, text, true) {}
+
 		bool findWidget(int x, int y) const { return false; }
+
 		void draw(bool drawFocused = false);
 	};
 
@@ -253,6 +257,7 @@ public:
 	public:
 		MacEditText(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::String text, bool enabled);
 
+		Graphics::MacGUIConstants::MacCursorType getCursorType() const;
 		bool findWidget(int x, int y) const;
 
 		void draw(bool drawFocused = false);
@@ -323,6 +328,7 @@ public:
 
 		MacWidget *_defaultWidget = nullptr;
 
+		int _mouseOverWidget = -1;
 		MacWidget *_focusedWidget = nullptr;
 		Common::Point _focusClick;
 		Common::Point _oldMousePos;


Commit: 4fc63332523f5757ea8529ad2a8519a08c742151
    https://github.com/scummvm/scummvm/commit/4fc63332523f5757ea8529ad2a8519a08c742151
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Hide mouse cursor while typing in a Mac edit text widget

Setting the mouse cursor to kMacCursorOff doesn't seem to work, since it
doesn't erase the old cursor from screen.

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 20b40cc8046..59f775691a2 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -786,6 +786,8 @@ bool MacGui::MacEditText::handleKeyDown(Common::Event &event) {
 	if (event.kbd.flags & (Common::KBD_CTRL | Common::KBD_ALT | Common::KBD_META))
 		return false;
 
+	CursorMan.showMouse(false);
+
 	switch (event.kbd.keycode) {
 	case Common::KEYCODE_LEFT:
 		if (_selectLen < 0) {
@@ -1028,6 +1030,9 @@ MacGui::MacDialogWindow::MacDialogWindow(MacGui *gui, OSystem *system, Graphics:
 }
 
 MacGui::MacDialogWindow::~MacDialogWindow() {
+	if (!CursorMan.isVisible())
+		CursorMan.showMouse(true);
+
 	if (_gui->_windowManager->getCursorType() != Graphics::MacGUIConstants::kMacCursorArrow)
 		_gui->_windowManager->replaceCursor(Graphics::MacGUIConstants::kMacCursorArrow);
 
@@ -1207,6 +1212,9 @@ int MacGui::MacDialogWindow::runDialog() {
 				break;
 
 			case Common::EVENT_MOUSEMOVE:
+				if (!CursorMan.isVisible())
+					CursorMan.showMouse(true);
+
 				if (_focusedWidget) {
 					if (_focusedWidget->findWidget(_oldMousePos.x, _oldMousePos.y) != _focusedWidget->findWidget(_mousePos.x, _mousePos.y)) {
 						_focusedWidget->setRedraw();


Commit: bab186c80f967ed2bcdec4979e72d51a736caaa4
    https://github.com/scummvm/scummvm/commit/bab186c80f967ed2bcdec4979e72d51a736caaa4
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Improve Mac "beam" cursor handling

The beam cursor should be drawn inverted. ScummVM does not have support
for such cursors as far as I know (except maybe in the OpenGL backend?)
so we draw it manually.

I still need to deal gracefully with colors other than black and white,
because it's possible to drag the cursor outside the dialog window while
pressing the mouse button to select text.

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 59f775691a2..61d5da1b498 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -561,16 +561,6 @@ MacGui::MacEditText::MacEditText(MacGui::MacDialogWindow *window, Common::Rect b
 	_textSurface = _window->innerSurface()->getSubArea(textBounds);
 }
 
-Graphics::MacGUIConstants::MacCursorType MacGui::MacEditText::getCursorType() const {
-	// If the widget is found, that implies that it's active.
-
-	// Note: The text widget in the Indy 3 save dialog does not change the
-	// cursor in the original. But since we're not going to emulate it
-	// that closely, I don't think we need to mark this as an enhancement.
-
-	return Graphics::MacGUIConstants::kMacCursorBeam;
-}
-
 bool MacGui::MacEditText::findWidget(int x, int y) const {
 	if (!_visible || !_enabled)
 		return false;
@@ -786,8 +776,6 @@ bool MacGui::MacEditText::handleKeyDown(Common::Event &event) {
 	if (event.kbd.flags & (Common::KBD_CTRL | Common::KBD_ALT | Common::KBD_META))
 		return false;
 
-	CursorMan.showMouse(false);
-
 	switch (event.kbd.keycode) {
 	case Common::KEYCODE_LEFT:
 		if (_selectLen < 0) {
@@ -1033,8 +1021,8 @@ MacGui::MacDialogWindow::~MacDialogWindow() {
 	if (!CursorMan.isVisible())
 		CursorMan.showMouse(true);
 
-	if (_gui->_windowManager->getCursorType() != Graphics::MacGUIConstants::kMacCursorArrow)
-		_gui->_windowManager->replaceCursor(Graphics::MacGUIConstants::kMacCursorArrow);
+	CursorMan.showMouse(true);
+	_gui->_windowManager->replaceCursor(Graphics::MacGUIConstants::kMacCursorArrow);
 
 	copyToScreen(_backup);
 	_backup->free();
@@ -1123,6 +1111,72 @@ void MacGui::MacDialogWindow::markRectAsDirty(Common::Rect r) {
 	_dirtyRects.push_back(r);
 }
 
+void MacGui::MacDialogWindow::drawBeamCursor() {
+	int x0 = _beamCursorPos.x - _beamCursorHotspotX;
+	int y0 = _beamCursorPos.y - _beamCursorHotspotY;
+	int x1 = x0 + _beamCursor->w;
+	int y1 = y0 + _beamCursor->h;
+
+	_beamCursor->copyRectToSurface(*(_gui->surface()), 0, 0, Common::Rect(x0, y0, x1, y1));
+
+	const Common::Point beam[] = {
+		Common::Point(0, 0), Common::Point(1, 0), Common::Point(5, 0),
+		Common::Point(6, 0), Common::Point(2, 1), Common::Point(4, 1),
+		Common::Point(3, 2), Common::Point(3, 3), Common::Point(3, 4),
+		Common::Point(3, 5), Common::Point(3, 6), Common::Point(3, 7),
+		Common::Point(3, 8), Common::Point(3, 9), Common::Point(3, 10),
+		Common::Point(3, 11), Common::Point(3, 12), Common::Point(3, 13),
+		Common::Point(2, 14), Common::Point(4, 14), Common::Point(0, 15),
+		Common::Point(1, 15), Common::Point(5, 15), Common::Point(6, 15)
+	};
+
+	for (int i = 0; i < ARRAYSIZE(beam); i++) {
+		uint32 color = _beamCursor->getPixel(beam[i].x, beam[i].y);
+
+		if (color == kBlack)
+			color = kWhite;
+		else
+			color = kBlack;
+
+		_beamCursor->setPixel(beam[i].x, beam[i].y, color);
+	}
+
+	int srcX = 0;
+	int srcY = 0;
+
+	if (x0 < 0) {
+		srcX = -x0;
+		x0 = 0;
+	}
+
+	x1 = MIN(x1, 640);
+
+	if (y0 < 0) {
+		srcY = -y0;
+		y0 = 0;
+	}
+
+	y1 = MIN(y1, 400);
+
+	_system->copyRectToScreen(_beamCursor->getBasePtr(srcX, srcY), _beamCursor->pitch, x0, y0, x1 - x0, y1 - y0);
+}
+
+void MacGui::MacDialogWindow::undrawBeamCursor() {
+		int x0 = _beamCursorPos.x - _beamCursorHotspotX;
+		int y0 = _beamCursorPos.y - _beamCursorHotspotY;
+		int x1 = x0 + _beamCursor->w;
+		int y1 = y0 + _beamCursor->h;
+
+		x0 = MAX(x0, 0);
+		x1 = MIN(x1, 640);
+		y0 = MAX(y0, 0);
+		y1 = MIN(y1, 400);
+
+		Graphics::Surface *screen = _gui->surface();
+
+		_system->copyRectToScreen(screen->getBasePtr(x0, y0), screen->pitch, x0, y0, x1 - x0, y1 - y0);
+}
+
 void MacGui::MacDialogWindow::update(bool fullRedraw) {
 	for (uint i = 0; i < _widgets.size(); i++)
 		_widgets[i]->draw();
@@ -1141,6 +1195,12 @@ void MacGui::MacDialogWindow::update(bool fullRedraw) {
 	}
 
 	_dirtyRects.clear();
+
+	if (_beamCursor) {
+		undrawBeamCursor();
+		_beamCursorPos = _realMousePos;
+		drawBeamCursor();
+	}
 }
 
 void MacGui::MacDialogWindow::fillPattern(Common::Rect r, uint16 pattern) {
@@ -1180,6 +1240,9 @@ int MacGui::MacDialogWindow::runDialog() {
 
 		while (_system->getEventManager()->pollEvent(event)) {
 			if (Common::isMouseEvent(event)) {
+				_realMousePos.x = event.mouse.x;
+				_realMousePos.y = event.mouse.y;
+
 				event.mouse.x -= (_bounds.left + _margin);
 				event.mouse.y -= (_bounds.top + _margin);
 
@@ -1212,8 +1275,8 @@ int MacGui::MacDialogWindow::runDialog() {
 				break;
 
 			case Common::EVENT_MOUSEMOVE:
-				if (!CursorMan.isVisible())
-					CursorMan.showMouse(true);
+				if (_beamCursor && !_beamCursorVisible)
+					_beamCursorVisible = true;
 
 				if (_focusedWidget) {
 					if (_focusedWidget->findWidget(_oldMousePos.x, _oldMousePos.y) != _focusedWidget->findWidget(_mousePos.x, _mousePos.y)) {
@@ -1223,12 +1286,24 @@ int MacGui::MacDialogWindow::runDialog() {
 					_focusedWidget->handleMouseMove(event);
 				} else {
 					int mouseOverWidget = findWidget(_mousePos.x, _mousePos.y);
-					Graphics::MacGUIConstants::MacCursorType cursorType = (mouseOverWidget >= 0) ? _widgets[mouseOverWidget]->getCursorType() : Graphics::MacGUIConstants::kMacCursorArrow;
 
-					if (_gui->_windowManager->getCursorType() != cursorType)
-						_gui->_windowManager->replaceCursor(cursorType);
+					bool useBeamCursor = (mouseOverWidget >= 0 && _widgets[mouseOverWidget]->useBeamCursor());
+
+					if (useBeamCursor && !_beamCursor) {
+						CursorMan.showMouse(false);
+						_beamCursor = new Graphics::Surface();
+						_beamCursor->create(7, 16, Graphics::PixelFormat::createFormatCLUT8());
+						_beamCursorVisible = true;
+						_beamCursorPos = _realMousePos;
+					} else if (!useBeamCursor && _beamCursor) {
+						CursorMan.showMouse(true);
+						undrawBeamCursor();
+						_beamCursor->free();
+						delete _beamCursor;
+						_beamCursor = nullptr;
+						_beamCursorVisible = false;
+					}
 				}
-
 				break;
 
 			case Common::EVENT_KEYDOWN:
@@ -1259,6 +1334,8 @@ int MacGui::MacDialogWindow::runDialog() {
 				// to key presses.
 				for (uint i = 0; i < _widgets.size(); i++) {
 					if (_widgets[i]->handleKeyDown(event)) {
+						if (_beamCursor)
+							_beamCursorVisible = true;
 						_widgets[i]->setRedraw();
 						break;
 					}
@@ -1593,13 +1670,39 @@ Graphics::Surface *MacGui::loadPict(int id) {
 			// The palette doesn't match the game's palette at all, so remap
 			// the colors to the custom area of the palette. It's assumed that
 			// only one such picture will be loaded at a time.
+			//
+			// But we still match black and white to 0 and 15 to make sure they
+			// mach exactly.
+
+			int black = -1;
+			int white = -1;
+
+			for (int i = 0; i < pict.getPaletteColorCount(); i++) {
+				int r = palette[3 * i];
+				int g = palette[3 * i + 1];
+				int b = palette[3 * i + 2];
+
+				if (r == 0 && g == 0 && b == 0)
+					black = i;
+				else if (r == 255 && g == 255 && b == 255)
+					white = i;
+			}
 
 			if (palette) {
 				_system->getPaletteManager()->setPalette(palette, kCustomColor, pict.getPaletteColorCount());
 
 				for (int y = 0; y < s->h; y++) {
 					for (int x = 0; x < s->w; x++) {
-						s->setPixel(x, y, kCustomColor + s1->getPixel(x, y));
+						int color = s1->getPixel(x, y);
+
+						if (color == black)
+							color = kBlack;
+						else if (color == white)
+							color = kWhite;
+						else
+							color = kCustomColor + color;
+
+						s->setPixel(x, y, color);
 					}
 				}
 			} else
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 0b845422a0e..000528214a8 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -23,7 +23,6 @@
 #define SCUMM_GFX_MAC_H
 
 #include "graphics/font.h"
-#include "graphics/macgui/macwindowmanager.h"
 
 class OSystem;
 
@@ -181,7 +180,7 @@ public:
 		virtual void setValue(int value);
 		int getValue() const { return _value; }
 
-		virtual Graphics::MacGUIConstants::MacCursorType getCursorType() const { return Graphics::MacGUIConstants::kMacCursorArrow; }
+		virtual bool useBeamCursor() { return false; }
 		virtual bool findWidget(int x, int y) const;
 
 		virtual void draw(bool drawFocused = false) = 0;
@@ -257,7 +256,7 @@ public:
 	public:
 		MacEditText(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::String text, bool enabled);
 
-		Graphics::MacGUIConstants::MacCursorType getCursorType() const;
+		bool useBeamCursor() { return true; }
 		bool findWidget(int x, int y) const;
 
 		void draw(bool drawFocused = false);
@@ -317,6 +316,15 @@ public:
 
 		bool _visible = false;
 
+		Graphics::Surface *_beamCursor = nullptr;
+		Common::Point _beamCursorPos;
+		bool _beamCursorVisible = false;
+		int _beamCursorHotspotX = 3;
+		int _beamCursorHotspotY = 4;
+
+		void drawBeamCursor();
+		void undrawBeamCursor();
+
 		PauseToken _pauseToken;
 
 		Graphics::Surface *_from = nullptr;
@@ -333,6 +341,7 @@ public:
 		Common::Point _focusClick;
 		Common::Point _oldMousePos;
 		Common::Point _mousePos;
+		Common::Point _realMousePos;
 
 		Common::StringArray _substitutions;
 		Common::Array<Common::Rect> _dirtyRects;
@@ -401,6 +410,8 @@ public:
 	MacGui(ScummEngine *vm, Common::String resourceFile);
 	virtual ~MacGui();
 
+	Graphics::Surface *surface() { return _surface; }
+
 	virtual const Common::String name() const = 0;
 
 	virtual bool handleEvent(Common::Event &event);


Commit: 92697e882d1c1e8730436a597542af2735135206
    https://github.com/scummvm/scummvm/commit/92697e882d1c1e8730436a597542af2735135206
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Fix Mac "beam" cursor hiding while typing

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 61d5da1b498..47146ab5194 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -1197,9 +1197,13 @@ void MacGui::MacDialogWindow::update(bool fullRedraw) {
 	_dirtyRects.clear();
 
 	if (_beamCursor) {
-		undrawBeamCursor();
+		if (_beamCursorVisible)
+			undrawBeamCursor();
+
 		_beamCursorPos = _realMousePos;
-		drawBeamCursor();
+
+		if (_beamCursorVisible)
+			drawBeamCursor();
 	}
 }
 
@@ -1334,8 +1338,10 @@ int MacGui::MacDialogWindow::runDialog() {
 				// to key presses.
 				for (uint i = 0; i < _widgets.size(); i++) {
 					if (_widgets[i]->handleKeyDown(event)) {
-						if (_beamCursor)
-							_beamCursorVisible = true;
+						if (_beamCursor) {
+							_beamCursorVisible = false;
+							undrawBeamCursor();
+						}
 						_widgets[i]->setRedraw();
 						break;
 					}


Commit: 33d6bdb37816aac26685a7e048fc2532e288d3f5
    https://github.com/scummvm/scummvm/commit/33d6bdb37816aac26685a7e048fc2532e288d3f5
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Fix Mac text drag selection, I hope

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 47146ab5194..b61dbe4927a 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -579,25 +579,22 @@ int MacGui::MacEditText::getTextPosFromMouse(int x, int y) {
 		return 0;
 
 	if (x >= _bounds.right || y >= _bounds.bottom)
-		return _text.size() - 1;
+		return _text.size();
 
 	x -= _bounds.left;
 
-	int textPos = _text.size() - 1;
 	int textX = _textPos;
 
 	for (uint i = 0; i < _text.size(); i++) {
 		int charWidth = _font->getCharWidth(_text[i]);
 
-		if (x >= textX && x < textX + charWidth) {
-			textPos = i;
-			break;
-		}
+		if (x >= textX && x < textX + charWidth)
+			return i;
 
 		textX += charWidth;
 	}
 
-	return textPos;
+	return _text.size();
 }
 
 void MacGui::MacEditText::deleteSelection() {
@@ -694,10 +691,10 @@ void MacGui::MacEditText::draw(bool drawFocused) {
 	}
 
 	if (firstCharSelected)
-		_window->innerSurface()->fillRect(Common::Rect(_bounds.left + 1, _bounds.top, _bounds.left + 2, _bounds.bottom), kGreen);
+		_window->innerSurface()->fillRect(Common::Rect(_bounds.left + 1, _bounds.top, _bounds.left + 2, _bounds.bottom), kBlack);
 
 	if (lastCharSelected)
-		_window->innerSurface()->fillRect(Common::Rect(_bounds.left + x + 1, _bounds.top, _bounds.right, _bounds.bottom), kGreen);
+		_window->innerSurface()->fillRect(Common::Rect(_bounds.left + x + 1, _bounds.top, _bounds.right, _bounds.bottom), kBlack);
 
 	if (_selectLen == 0) {
 		_textSurface.vLine(caretX, 0, _textSurface.h - 1, kRed);


Commit: 6d9c2bb0060e8e2ab2b8babe3a4064792cc606f3
    https://github.com/scummvm/scummvm/commit/6d9c2bb0060e8e2ab2b8babe3a4064792cc606f3
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Fix double-click past end of text in Mac edit text widget

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index b61dbe4927a..87166209eb8 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -720,7 +720,11 @@ void MacGui::MacEditText::handleMouseDown(Common::Event &event) {
 			int startPos = _caretPos;
 			int endPos = _caretPos;
 
-			if (_text[_caretPos] == ' ') {
+			// Check if used clicked past the end of the text
+			if (_caretPos >= (int)_text.size())
+				startPos = endPos = _text.size() - 1;
+
+			if (_text[startPos] == ' ') {
 				while (startPos >= 0) {
 					if (_text[startPos] != ' ') {
 						startPos++;


Commit: fdc5a386959f9a15b0b3942006f2f5cecf535a67
    https://github.com/scummvm/scummvm/commit/fdc5a386959f9a15b0b3942006f2f5cecf535a67
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Restore Mac cursor when releasing mouse button

When drag-selecting text, it would keep the "beam" cursor active. This
should be fixed now.

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 87166209eb8..444ce182f69 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -1268,15 +1268,21 @@ int MacGui::MacDialogWindow::runDialog() {
 
 			case Common::EVENT_LBUTTONUP:
 				buttonPressed = false;
+				updateCursor();
+
+				if (_focusedWidget) {
+					MacWidget *widget = _focusedWidget;
 
-				if (_focusedWidget && _focusedWidget->findWidget(event.mouse.x, event.mouse.y)) {
-					widgetId = _focusedWidget->getId();
-					_focusedWidget->handleMouseUp(event);
 					clearFocusedWidget();
-					return widgetId;
+					updateCursor();
+
+					if (widget->findWidget(event.mouse.x, event.mouse.y)) {
+						widgetId = widget->getId();
+						widget->handleMouseUp(event);
+						return widgetId;
+					}
 				}
 
-				clearFocusedWidget();
 				break;
 
 			case Common::EVENT_MOUSEMOVE:
@@ -1290,25 +1296,9 @@ int MacGui::MacDialogWindow::runDialog() {
 
 					_focusedWidget->handleMouseMove(event);
 				} else {
-					int mouseOverWidget = findWidget(_mousePos.x, _mousePos.y);
-
-					bool useBeamCursor = (mouseOverWidget >= 0 && _widgets[mouseOverWidget]->useBeamCursor());
-
-					if (useBeamCursor && !_beamCursor) {
-						CursorMan.showMouse(false);
-						_beamCursor = new Graphics::Surface();
-						_beamCursor->create(7, 16, Graphics::PixelFormat::createFormatCLUT8());
-						_beamCursorVisible = true;
-						_beamCursorPos = _realMousePos;
-					} else if (!useBeamCursor && _beamCursor) {
-						CursorMan.showMouse(true);
-						undrawBeamCursor();
-						_beamCursor->free();
-						delete _beamCursor;
-						_beamCursor = nullptr;
-						_beamCursorVisible = false;
-					}
+					updateCursor();
 				}
+
 				break;
 
 			case Common::EVENT_KEYDOWN:
@@ -1368,6 +1358,27 @@ int MacGui::MacDialogWindow::runDialog() {
 	return -1;
 }
 
+void MacGui::MacDialogWindow::updateCursor() {
+	int mouseOverWidget = findWidget(_mousePos.x, _mousePos.y);
+	bool useBeamCursor = (mouseOverWidget >= 0 && _widgets[mouseOverWidget]->useBeamCursor());
+
+	if (useBeamCursor && !_beamCursor) {
+		CursorMan.showMouse(false);
+		_beamCursor = new Graphics::Surface();
+		_beamCursor->create(7, 16, Graphics::PixelFormat::createFormatCLUT8());
+		_beamCursorVisible = true;
+		_beamCursorPos = _realMousePos;
+	} else if (!useBeamCursor && _beamCursor) {
+		CursorMan.showMouse(true);
+
+		undrawBeamCursor();
+		_beamCursor->free();
+		delete _beamCursor;
+		_beamCursor = nullptr;
+		_beamCursorVisible = false;
+	}
+}
+
 void MacGui::MacDialogWindow::drawSprite(const Graphics::Surface *sprite, int x, int y) {
 	_innerSurface.copyRectToSurface(*sprite, x, y, Common::Rect(sprite->w, sprite->h));
 	markRectAsDirty(Common::Rect(x, y, x + sprite->w, y + sprite->h));
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 000528214a8..344a66db1d7 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -362,6 +362,7 @@ public:
 
 		void show();
 		int runDialog();
+		void updateCursor();
 
 		void setDefaultWidget(int nr) { _defaultWidget = _widgets[nr]; }
 		MacWidget *getDefaultWidget() const { return _defaultWidget; }


Commit: 95f0097db7cae2a3c37139d082ea8258bc13fc31
    https://github.com/scummvm/scummvm/commit/95f0097db7cae2a3c37139d082ea8258bc13fc31
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Fix Mac edit text widget drag-selection. Again.

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 444ce182f69..dc2d44d6de6 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -575,17 +575,18 @@ bool MacGui::MacEditText::findWidget(int x, int y) const {
 }
 
 int MacGui::MacEditText::getTextPosFromMouse(int x, int y) {
-	if (_text.empty() || y < _bounds.top || x < _bounds.left + 1)
+	if (_text.empty() || y < _bounds.top)
 		return 0;
 
-	if (x >= _bounds.right || y >= _bounds.bottom)
+	if (y >= _bounds.bottom)
 		return _text.size();
 
 	x -= _bounds.left;
 
 	int textX = _textPos;
+	uint i;
 
-	for (uint i = 0; i < _text.size(); i++) {
+	for (i = 0; i < _text.size() && textX <= _bounds.width(); i++) {
 		int charWidth = _font->getCharWidth(_text[i]);
 
 		if (x >= textX && x < textX + charWidth)
@@ -594,7 +595,7 @@ int MacGui::MacEditText::getTextPosFromMouse(int x, int y) {
 		textX += charWidth;
 	}
 
-	return _text.size();
+	return i;
 }
 
 void MacGui::MacEditText::deleteSelection() {
@@ -693,7 +694,7 @@ void MacGui::MacEditText::draw(bool drawFocused) {
 	if (firstCharSelected)
 		_window->innerSurface()->fillRect(Common::Rect(_bounds.left + 1, _bounds.top, _bounds.left + 2, _bounds.bottom), kBlack);
 
-	if (lastCharSelected)
+	if (lastCharSelected && _bounds.left + x + 1 < _bounds.right)
 		_window->innerSurface()->fillRect(Common::Rect(_bounds.left + x + 1, _bounds.top, _bounds.right, _bounds.bottom), kBlack);
 
 	if (_selectLen == 0) {


Commit: bdf92b93d1dae018e19482d59ba2065c876b9764
    https://github.com/scummvm/scummvm/commit/bdf92b93d1dae018e19482d59ba2065c876b9764
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Initial work on scrolling Mac edit text widget on drag-selection.

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index dc2d44d6de6..06e2633b0ab 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -849,6 +849,52 @@ void MacGui::MacEditText::handleMouseMove(Common::Event &event) {
 	}
 }
 
+void MacGui::MacEditText::handleMouseHeld() {
+	if (_text.empty())
+		return;
+
+	Common::Point mousePos = _window->getMousePos();
+	int x = _textPos;
+
+	if (mousePos.x < _bounds.left + 1) {
+		if (_textPos == 0)
+			return;
+
+		// Scroll the text to show any partially displayed character
+		// or, if it's entirely shown, the character before that.
+
+		setRedraw();
+
+		for (uint i = 0; i < _text.size(); i++) {
+			if (x == 0) {
+				_textPos += _font->getCharWidth(_text[i - 1]);
+				return;
+			}
+
+			int charWidth = _font->getCharWidth(_text[i]);
+
+			if (x < 0 && x + charWidth > 0) {
+				_textPos += (charWidth + x);
+				return;
+			}
+
+			x += charWidth;
+		}
+	} else if (mousePos.x > _bounds.right) {
+		for (uint i = 0; i < _text.size(); i++) {
+			int charWidth = _font->getCharWidth(_text[i]);
+
+			if (x > _textSurface.w + 1) {
+				_textPos -= (x - _textSurface.w + 1);
+				setRedraw();
+				return;
+			}
+
+			x += charWidth;
+		}
+	}
+}
+
 // ---------------------------------------------------------------------------
 // Image widget
 // ---------------------------------------------------------------------------
@@ -1261,7 +1307,7 @@ int MacGui::MacDialogWindow::runDialog() {
 			switch (event.type) {
 			case Common::EVENT_LBUTTONDOWN:
 				buttonPressed = true;
-				nextMouseRepeat = _system->getMillis() + 100;
+				nextMouseRepeat = _system->getMillis() + 25;
 				setFocusedWidget(event.mouse.x, event.mouse.y);
 				if (_focusedWidget)
 					_focusedWidget->handleMouseDown(event);
@@ -1347,7 +1393,7 @@ int MacGui::MacDialogWindow::runDialog() {
 		}
 
 		if (_focusedWidget && _system->getMillis() > nextMouseRepeat) {
-			nextMouseRepeat = _system->getMillis() + 100;
+			nextMouseRepeat = _system->getMillis() + 25;
 			_focusedWidget->handleMouseHeld();
 		}
 
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 344a66db1d7..0c8ca830d4b 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -264,6 +264,7 @@ public:
 		void handleMouseDown(Common::Event &event);
 		bool handleKeyDown(Common::Event &event);
 		void handleMouseMove(Common::Event &event);
+		void handleMouseHeld();
 	};
 
 	class MacPicture : public MacWidget {


Commit: 0a47c0aa843f1cf53b43982026766ab1d0497a2b
    https://github.com/scummvm/scummvm/commit/0a47c0aa843f1cf53b43982026766ab1d0497a2b
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Update selection on Mac edit text scrolling

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 06e2633b0ab..825f4ca591e 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -574,6 +574,18 @@ bool MacGui::MacEditText::findWidget(int x, int y) const {
 	return _bounds.contains(x, y);
 }
 
+void MacGui::MacEditText::updateSelection(int x, int y) {
+	int oldSelectLen = _selectLen;
+
+	int pos = getTextPosFromMouse(x, y);
+
+	_selectLen = pos - _caretPos;
+
+	if (_selectLen != oldSelectLen) {
+		setRedraw();
+	}
+}
+
 int MacGui::MacEditText::getTextPosFromMouse(int x, int y) {
 	if (_text.empty() || y < _bounds.top)
 		return 0;
@@ -837,18 +849,6 @@ bool MacGui::MacEditText::handleKeyDown(Common::Event &event) {
 	return false;
 }
 
-void MacGui::MacEditText::handleMouseMove(Common::Event &event) {
-	int oldSelectLen = _selectLen;
-
-	int pos = getTextPosFromMouse(event.mouse.x, event.mouse.y);
-
-	_selectLen = pos - _caretPos;
-
-	if (_selectLen != oldSelectLen) {
-		setRedraw();
-	}
-}
-
 void MacGui::MacEditText::handleMouseHeld() {
 	if (_text.empty())
 		return;
@@ -863,11 +863,10 @@ void MacGui::MacEditText::handleMouseHeld() {
 		// Scroll the text to show any partially displayed character
 		// or, if it's entirely shown, the character before that.
 
-		setRedraw();
-
 		for (uint i = 0; i < _text.size(); i++) {
 			if (x == 0) {
 				_textPos += _font->getCharWidth(_text[i - 1]);
+				updateSelection(mousePos.x, mousePos.y);
 				return;
 			}
 
@@ -875,6 +874,7 @@ void MacGui::MacEditText::handleMouseHeld() {
 
 			if (x < 0 && x + charWidth > 0) {
 				_textPos += (charWidth + x);
+				updateSelection(mousePos.x, mousePos.y);
 				return;
 			}
 
@@ -886,7 +886,7 @@ void MacGui::MacEditText::handleMouseHeld() {
 
 			if (x > _textSurface.w + 1) {
 				_textPos -= (x - _textSurface.w + 1);
-				setRedraw();
+				updateSelection(mousePos.x, mousePos.y);
 				return;
 			}
 
@@ -895,6 +895,10 @@ void MacGui::MacEditText::handleMouseHeld() {
 	}
 }
 
+void MacGui::MacEditText::handleMouseMove(Common::Event &event) {
+	updateSelection(event.mouse.x, event.mouse.y);
+}
+
 // ---------------------------------------------------------------------------
 // Image widget
 // ---------------------------------------------------------------------------
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 0c8ca830d4b..dfd52272f44 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -258,6 +258,7 @@ public:
 
 		bool useBeamCursor() { return true; }
 		bool findWidget(int x, int y) const;
+		void updateSelection(int x, int y);
 
 		void draw(bool drawFocused = false);
 


Commit: 2cda7f071e2a4919c165b74251cd7fa5ec78a3d0
    https://github.com/scummvm/scummvm/commit/2cda7f071e2a4919c165b74251cd7fa5ec78a3d0
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Simplify the still buggy Mac edit text scrolling

Scrolling one character at a time is too much of a hassle. Scroll by a
fixed number of pixels instead.

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 825f4ca591e..941cd93d8d3 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -587,7 +587,7 @@ void MacGui::MacEditText::updateSelection(int x, int y) {
 }
 
 int MacGui::MacEditText::getTextPosFromMouse(int x, int y) {
-	if (_text.empty() || y < _bounds.top)
+	if (_text.empty() || y < _bounds.top || (_textPos == 0 && x < _bounds.left))
 		return 0;
 
 	if (y >= _bounds.bottom)
@@ -854,45 +854,23 @@ void MacGui::MacEditText::handleMouseHeld() {
 		return;
 
 	Common::Point mousePos = _window->getMousePos();
-	int x = _textPos;
-
-	if (mousePos.x < _bounds.left + 1) {
-		if (_textPos == 0)
-			return;
-
-		// Scroll the text to show any partially displayed character
-		// or, if it's entirely shown, the character before that.
-
-		for (uint i = 0; i < _text.size(); i++) {
-			if (x == 0) {
-				_textPos += _font->getCharWidth(_text[i - 1]);
-				updateSelection(mousePos.x, mousePos.y);
-				return;
-			}
 
-			int charWidth = _font->getCharWidth(_text[i]);
+	int oldTextPos = _textPos;
 
-			if (x < 0 && x + charWidth > 0) {
-				_textPos += (charWidth + x);
-				updateSelection(mousePos.x, mousePos.y);
-				return;
-			}
-
-			x += charWidth;
-		}
-	} else if (mousePos.x > _bounds.right) {
-		for (uint i = 0; i < _text.size(); i++) {
-			int charWidth = _font->getCharWidth(_text[i]);
-
-			if (x > _textSurface.w + 1) {
-				_textPos -= (x - _textSurface.w + 1);
-				updateSelection(mousePos.x, mousePos.y);
-				return;
-			}
+	int minTextPos = MIN(_bounds.width() - _font->getStringWidth(_text), 0);
 
-			x += charWidth;
-		}
+	if (mousePos.x < _bounds.left + 1 && mousePos.y < _bounds.bottom && _textPos < 0) {
+		_textPos += 4;
+		if (_textPos > 0)
+			_textPos = 0;
+	} else if (mousePos.x >= _bounds.right) {
+		_textPos -= 4;
+		if (_textPos < minTextPos)
+			_textPos = minTextPos;
 	}
+
+	if (_textPos != oldTextPos)
+		updateSelection(mousePos.x, mousePos.y);
 }
 
 void MacGui::MacEditText::handleMouseMove(Common::Event &event) {
@@ -1311,7 +1289,7 @@ int MacGui::MacDialogWindow::runDialog() {
 			switch (event.type) {
 			case Common::EVENT_LBUTTONDOWN:
 				buttonPressed = true;
-				nextMouseRepeat = _system->getMillis() + 25;
+				nextMouseRepeat = _system->getMillis() + 40;
 				setFocusedWidget(event.mouse.x, event.mouse.y);
 				if (_focusedWidget)
 					_focusedWidget->handleMouseDown(event);
@@ -1397,7 +1375,7 @@ int MacGui::MacDialogWindow::runDialog() {
 		}
 
 		if (_focusedWidget && _system->getMillis() > nextMouseRepeat) {
-			nextMouseRepeat = _system->getMillis() + 25;
+			nextMouseRepeat = _system->getMillis() + 40;
 			_focusedWidget->handleMouseHeld();
 		}
 


Commit: 9b6dc9119dd91e16f2f29322aded6fd664d61468
    https://github.com/scummvm/scummvm/commit/9b6dc9119dd91e16f2f29322aded6fd664d61468
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Reorder some Mac GUI code

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 941cd93d8d3..b250360fb69 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -1545,6 +1545,57 @@ MacGui::~MacGui() {
 	delete _windowManager;
 }
 
+void MacGui::setPalette(const byte *palette, uint size) {
+	_windowManager->passPalette(palette, size);
+}
+
+bool MacGui::handleEvent(Common::Event &event) {
+	return _windowManager->processEvent(event);
+}
+
+int MacGui::delay(uint32 ms) {
+	uint32 to;
+
+	to = _system->getMillis() + ms;
+
+	while (ms == 0 || _system->getMillis() < to) {
+		Common::Event event;
+
+		while (_system->getEventManager()->pollEvent(event)) {
+			switch (event.type) {
+			case Common::EVENT_QUIT:
+				return 2;
+
+			case Common::EVENT_LBUTTONDOWN:
+				return 1;
+
+			default:
+				break;
+			}
+		}
+
+		uint32 delta = to - _system->getMillis();
+
+		if (delta > 0) {
+			_system->delayMillis(MIN<uint32>(delta, 10));
+			_system->updateScreen();
+		}
+	}
+
+	return 0;
+}
+
+// --------------------------------------------------------------------------
+// Menu handling
+//
+// In the original, the menu was activated by pressing the "Command key".
+// This does not seem very friendly to touch devices, so we instead use the
+// "mouse over" method in the Mac Window Manager class.
+//
+// TODO: Ideally we should handle both, but I don't know if there's a way for
+//       them to coexist.
+// --------------------------------------------------------------------------
+
 void MacGui::menuCallback(int id, Common::String &name, void *data) {
 	((MacGui *)data)->handleMenu(id, name);
 }
@@ -1630,6 +1681,83 @@ void MacGui::initialize() {
 	}
 }
 
+bool MacGui::handleMenu(int id, Common::String &name) {
+	// This menu item (e.g. a menu separator) has no action, so it's
+	// handled trivially.
+	if (id == 0)
+		return true;
+
+	// Originally, the menu bar would still be visible. I don't know how
+	// to replicate that effect.
+
+	_windowManager->getMenu()->closeMenu();
+
+	switch (id) {
+	case 100:	// About
+		runAboutDialog();
+		return true;
+
+	case 200:	// Open
+		if (runOpenDialog())
+			debug("Open a saved game");
+		return true;
+
+	case 201:	// Save
+		if (runSaveDialog())
+			debug("Save a game");
+		return true;
+
+	case 202:	// Restart
+		if (runRestartDialog())
+			debug("Game should restart now");
+		return true;
+
+	case 203:	// Pause
+		return true;
+
+	// In the original, the Edit menu is active during save dialogs, though
+	// only Cut, Copy and Paste.
+
+	case 300:	// Undo
+	case 301:	// Cut
+	case 302:	// Copy
+	case 303:	// Paste
+	case 304:	// Clear
+		return true;
+	}
+
+	return false;
+}
+
+void MacGui::updateWindowManager() {
+	// We want the arrow cursor for menus. Note that the menu triggers even
+	// when the mouse is invisible, which may or may not be a bug. But the
+	// original did allow you to open the menu with Alt even when the
+	// cursor was visible, so for now it's a feature.
+
+	bool isActive = _windowManager->isMenuActive();
+
+	if (isActive) {
+		if (!_menuIsActive) {
+			_cursorWasVisible = CursorMan.showMouse(true);
+			_windowManager->pushCursor(Graphics::MacGUIConstants::kMacCursorArrow);
+		}
+	} else {
+		if (_menuIsActive) {
+			if (_windowManager->getCursorType() == Graphics::MacGUIConstants::kMacCursorArrow)
+				_windowManager->popCursor();
+			CursorMan.showMouse(_cursorWasVisible);
+		}
+	}
+
+	_menuIsActive = isActive;
+	_windowManager->draw();
+}
+
+// ---------------------------------------------------------------------------
+// Font handling
+// ---------------------------------------------------------------------------
+
 const Graphics::Font *MacGui::getFont(FontId fontId) {
 	const Graphics::Font *font = _fonts.getValOrDefault((int)fontId);
 
@@ -1696,6 +1824,16 @@ bool MacGui::getFontParams(FontId fontId, int &id, int &size, int &slant) const
 	}
 }
 
+// ---------------------------------------------------------------------------
+// PICT loader
+//
+// ScummVM already has a PICT v2 loader, and we use that when necessary. But
+// for PICT v1 we have our own for now.
+//
+// TODO: Investigate if PICT v1 and v2 can be handled by the standard PICT
+//       loader.
+// ---------------------------------------------------------------------------
+
 Graphics::Surface *MacGui::loadPict(int id) {
 	Common::MacResManager resource;
 	Graphics::Surface *s = nullptr;
@@ -1896,91 +2034,17 @@ Graphics::Surface *MacGui::decodePictV1(Common::SeekableReadStream *res) {
 	return s;
 }
 
-bool MacGui::handleMenu(int id, Common::String &name) {
-	// This menu item (e.g. a menu separator) has no action, so it's
-	// handled trivially.
-	if (id == 0)
-		return true;
-
-	// Originally, the menu bar would still be visible. I don't know how
-	// to replicate that effect.
-
-	_windowManager->getMenu()->closeMenu();
-
-	switch (id) {
-	case 100:	// About
-		runAboutDialog();
-		return true;
-
-	case 200:	// Open
-		if (runOpenDialog())
-			debug("Open a saved game");
-		return true;
-
-	case 201:	// Save
-		if (runSaveDialog())
-			debug("Save a game");
-		return true;
-
-	case 202:	// Restart
-		if (runRestartDialog())
-			debug("Game should restart now");
-		return true;
-
-	case 203:	// Pause
-		return true;
-
-	// In the original, the Edit menu is active during save dialogs, though
-	// only Cut, Copy and Paste.
-
-	case 300:	// Undo
-	case 301:	// Cut
-	case 302:	// Copy
-	case 303:	// Paste
-	case 304:	// Clear
-		return true;
-	}
-
-	return false;
-}
-
-bool MacGui::runOkCancelDialog(Common::String text) {
-	// Widgets:
-	//
-	// 0 - Okay button
-	// 1 - Cancel button
-	// 2 - "^0" text
-
-	MacDialogWindow *window = createDialog(502);
-
-	window->setDefaultWidget(0);
-	window->addSubstitution(text);
-
-	// When quitting, the default action is to quit
-	bool ret = true;
-
-	while (!_vm->shouldQuit()) {
-		int clicked = window->runDialog();
-
-		if (clicked == 0)
-			break;
+// ---------------------------------------------------------------------------
+// Window handling
+// ---------------------------------------------------------------------------
 
-		if (clicked == 1) {
-			ret = false;
-			break;
-		}
+MacGui::MacDialogWindow *MacGui::createWindow(Common::Rect bounds, MacDialogWindowStyle style) {
+	if (bounds.left < 0 || bounds.top < 0 || bounds.right >= 640 || bounds.bottom >= 400) {
+		// This happens with the Last Crusade file dialogs.
+		bounds.moveTo((640 - bounds.width()) / 2, 27);
 	}
 
-	delete window;
-	return ret;
-}
-
-bool MacGui::runQuitDialog() {
-	return runOkCancelDialog("Are you sure you want to quit?");
-}
-
-bool MacGui::runRestartDialog() {
-	return runOkCancelDialog("Are you sure you want to restart this game from the beginning?");
+	return new MacDialogWindow(this, _system, _surface, bounds, style);
 }
 
 Common::String MacGui::getDialogString(Common::SeekableReadStream *res, int len) {
@@ -1992,96 +2056,6 @@ Common::String MacGui::getDialogString(Common::SeekableReadStream *res, int len)
 	return str;
 }
 
-bool MacGui::handleEvent(Common::Event &event) {
-	return _windowManager->processEvent(event);
-}
-
-void MacGui::setPalette(const byte *palette, uint size) {
-	_windowManager->passPalette(palette, size);
-}
-
-void MacGui::updateWindowManager() {
-	// TODO: Originally, the left Alt button opens the menu. In ScummVM,
-	//       it triggers on mouse-over. Preferrably both should work (the
-	//       latter should work better for touch screens), but it's hard
-	//       to get them to coexist.
-
-	// We want the arrow cursor for menus. Note that the menu triggers even
-	// when the mouse is invisible, which may or may not be a bug. But the
-	// original did allow you to open the menu with Alt even when the
-	// cursor was visible, so for now it's a feature.
-
-	bool isActive = _windowManager->isMenuActive();
-
-	if (isActive) {
-		if (!_menuIsActive) {
-			_cursorWasVisible = CursorMan.showMouse(true);
-			_windowManager->pushCursor(Graphics::MacGUIConstants::kMacCursorArrow);
-		}
-	} else {
-		if (_menuIsActive) {
-			if (_windowManager->getCursorType() == Graphics::MacGUIConstants::kMacCursorArrow)
-				_windowManager->popCursor();
-			CursorMan.showMouse(_cursorWasVisible);
-		}
-	}
-
-	_menuIsActive = isActive;
-	_windowManager->draw();
-}
-
-MacGui::MacDialogWindow *MacGui::drawBanner(char *message) {
-	MacGui::MacDialogWindow *window = createWindow(Common::Rect(70, 189, 570, 211), kStyleRounded);
-	const Graphics::Font *font = getFont(_vm->_game.id == GID_INDY3 ? kIndy3FontMedium : kLoomFontMedium);
-
-	Graphics::Surface *s = window->innerSurface();
-	font->drawString(s, (char *)message, 0, 0, s->w, kBlack, Graphics::kTextAlignCenter);
-
-	window->show();
-	return window;
-}
-
-int MacGui::delay(uint32 ms) {
-	uint32 to;
-
-	to = _system->getMillis() + ms;
-
-	while (ms == 0 || _system->getMillis() < to) {
-		Common::Event event;
-
-		while (_system->getEventManager()->pollEvent(event)) {
-			switch (event.type) {
-			case Common::EVENT_QUIT:
-				return 2;
-
-			case Common::EVENT_LBUTTONDOWN:
-				return 1;
-
-			default:
-				break;
-			}
-		}
-
-		uint32 delta = to - _system->getMillis();
-
-		if (delta > 0) {
-			_system->delayMillis(MIN<uint32>(delta, 10));
-			_system->updateScreen();
-		}
-	}
-
-	return 0;
-}
-
-MacGui::MacDialogWindow *MacGui::createWindow(Common::Rect bounds, MacDialogWindowStyle style) {
-	if (bounds.left < 0 || bounds.top < 0 || bounds.right >= 640 || bounds.bottom >= 400) {
-		// This happens with the Last Crusade file dialogs.
-		bounds.moveTo((640 - bounds.width()) / 2, 27);
-	}
-
-	return new MacDialogWindow(this, _system, _surface, bounds, style);
-}
-
 MacGui::MacDialogWindow *MacGui::createDialog(int dialogId) {
 	Common::MacResManager resource;
 	Common::SeekableReadStream *res;
@@ -2193,6 +2167,60 @@ MacGui::MacDialogWindow *MacGui::createDialog(int dialogId) {
 	return window;
 }
 
+// ---------------------------------------------------------------------------
+// Standard dialogs
+// ---------------------------------------------------------------------------
+
+bool MacGui::runOkCancelDialog(Common::String text) {
+	// Widgets:
+	//
+	// 0 - Okay button
+	// 1 - Cancel button
+	// 2 - "^0" text
+
+	MacDialogWindow *window = createDialog(502);
+
+	window->setDefaultWidget(0);
+	window->addSubstitution(text);
+
+	// When quitting, the default action is to quit
+	bool ret = true;
+
+	while (!_vm->shouldQuit()) {
+		int clicked = window->runDialog();
+
+		if (clicked == 0)
+			break;
+
+		if (clicked == 1) {
+			ret = false;
+			break;
+		}
+	}
+
+	delete window;
+	return ret;
+}
+
+bool MacGui::runQuitDialog() {
+	return runOkCancelDialog("Are you sure you want to quit?");
+}
+
+bool MacGui::runRestartDialog() {
+	return runOkCancelDialog("Are you sure you want to restart this game from the beginning?");
+}
+
+MacGui::MacDialogWindow *MacGui::drawBanner(char *message) {
+	MacGui::MacDialogWindow *window = createWindow(Common::Rect(70, 189, 570, 211), kStyleRounded);
+	const Graphics::Font *font = getFont(_vm->_game.id == GID_INDY3 ? kIndy3FontMedium : kLoomFontMedium);
+
+	Graphics::Surface *s = window->innerSurface();
+	font->drawString(s, (char *)message, 0, 0, s->w, kBlack, Graphics::kTextAlignCenter);
+
+	window->show();
+	return window;
+}
+
 // ===========================================================================
 // The Mac Loom GUI. This one is pretty simple.
 // ===========================================================================
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index dfd52272f44..813eb3a2456 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -116,6 +116,8 @@ protected:
 		kStyleRounded
 	};
 
+	int delay(uint32 ms = 0);
+
 	virtual bool getFontParams(FontId fontId, int &id, int &size, int &slant) const;
 
 	Common::String getDialogString(Common::SeekableReadStream *res, int len);
@@ -417,10 +419,12 @@ public:
 
 	virtual const Common::String name() const = 0;
 
+	void setPalette(const byte *palette, uint size);
 	virtual bool handleEvent(Common::Event &event);
+  
 	static void menuCallback(int id, Common::String &name, void *data);
-
 	virtual void initialize();
+	void updateWindowManager();
 
 	const Graphics::Font *getFont(FontId fontId);
 	virtual const Graphics::Font *getFontByScummId(int32 id) = 0;
@@ -433,8 +437,6 @@ public:
 	virtual void resetAfterLoad() = 0;
 	virtual void update(int delta) = 0;
 
-	void updateWindowManager();
-
 	virtual void setupCursor(int &width, int &height, int &hotspotX, int &hotspotY, int &animate) = 0;
 
 	virtual Graphics::Surface *textArea() { return nullptr; }
@@ -442,14 +444,9 @@ public:
 	virtual void initTextAreaForActor(Actor *a, byte color) {}
 	virtual void printCharToTextArea(int chr, int x, int y, int color) {}
 
-	void setPalette(const byte *palette, uint size);
-
-	MacDialogWindow *drawBanner(char *message);
-
-	int delay(uint32 ms = 0);
-
 	MacDialogWindow *createWindow(Common::Rect bounds, MacDialogWindowStyle style = kStyleNormal);
 	MacDialogWindow *createDialog(int dialogId);
+	MacDialogWindow *drawBanner(char *message);
 };
 
 class MacLoomGui : public MacGui {


Commit: de0b8679e55732d097e25df9923ba7a4c9e45d26
    https://github.com/scummvm/scummvm/commit/de0b8679e55732d097e25df9923ba7a4c9e45d26
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Tweak Mac edit text selection

While ignoring the larger issues that are still not fixed.

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index b250360fb69..10caa89fe8d 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -587,7 +587,10 @@ void MacGui::MacEditText::updateSelection(int x, int y) {
 }
 
 int MacGui::MacEditText::getTextPosFromMouse(int x, int y) {
-	if (_text.empty() || y < _bounds.top || (_textPos == 0 && x < _bounds.left))
+	if (_text.empty())
+		return 0;
+
+	if (y < _bounds.top)
 		return 0;
 
 	if (y >= _bounds.bottom)
@@ -601,12 +604,18 @@ int MacGui::MacEditText::getTextPosFromMouse(int x, int y) {
 	for (i = 0; i < _text.size() && textX <= _bounds.width(); i++) {
 		int charWidth = _font->getCharWidth(_text[i]);
 
-		if (x >= textX && x < textX + charWidth)
+		if (x >= textX && x < textX + charWidth) {
+			if (x > textX + charWidth / 2)
+				return i + 1;
 			return i;
+		}
 
 		textX += charWidth;
 	}
 
+	if (x <= _bounds.left)
+		return 0;
+
 	return i;
 }
 
@@ -860,11 +869,11 @@ void MacGui::MacEditText::handleMouseHeld() {
 	int minTextPos = MIN(_bounds.width() - _font->getStringWidth(_text), 0);
 
 	if (mousePos.x < _bounds.left + 1 && mousePos.y < _bounds.bottom && _textPos < 0) {
-		_textPos += 4;
+		_textPos += 5;
 		if (_textPos > 0)
 			_textPos = 0;
 	} else if (mousePos.x >= _bounds.right) {
-		_textPos -= 4;
+		_textPos -= 5;
 		if (_textPos < minTextPos)
 			_textPos = minTextPos;
 	}


Commit: 67d7933785d9e25b3a7c6d34a2bd8a1394a12737
    https://github.com/scummvm/scummvm/commit/67d7933785d9e25b3a7c6d34a2bd8a1394a12737
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Fix jankiness in Mac edit text scrolling

Also increased the scroll speed a bit

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 10caa89fe8d..338825feded 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -581,9 +581,8 @@ void MacGui::MacEditText::updateSelection(int x, int y) {
 
 	_selectLen = pos - _caretPos;
 
-	if (_selectLen != oldSelectLen) {
+	if (_selectLen != oldSelectLen)
 		setRedraw();
-	}
 }
 
 int MacGui::MacEditText::getTextPosFromMouse(int x, int y) {
@@ -869,17 +868,19 @@ void MacGui::MacEditText::handleMouseHeld() {
 	int minTextPos = MIN(_bounds.width() - _font->getStringWidth(_text), 0);
 
 	if (mousePos.x < _bounds.left + 1 && mousePos.y < _bounds.bottom && _textPos < 0) {
-		_textPos += 5;
+		_textPos += 8;
 		if (_textPos > 0)
 			_textPos = 0;
 	} else if (mousePos.x >= _bounds.right) {
-		_textPos -= 5;
+		_textPos -= 8;
 		if (_textPos < minTextPos)
 			_textPos = minTextPos;
 	}
 
-	if (_textPos != oldTextPos)
+	if (_textPos != oldTextPos) {
 		updateSelection(mousePos.x, mousePos.y);
+		setRedraw();
+	}
 }
 
 void MacGui::MacEditText::handleMouseMove(Common::Event &event) {


Commit: 99c8f345b9a3321276b231ddd5d37fa6ccbcc18f
    https://github.com/scummvm/scummvm/commit/99c8f345b9a3321276b231ddd5d37fa6ccbcc18f
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Fix Map "beam" cursor inverted colors

I don't know for sure if this is how the Mac does it, but it seems to
match the few colors I checked, and it looks good enough to me. It's not
really something that's supposed to happen outside of black and white
anyway. But it can.

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 338825feded..2790523271d 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -1159,26 +1159,28 @@ void MacGui::MacDialogWindow::drawBeamCursor() {
 
 	_beamCursor->copyRectToSurface(*(_gui->surface()), 0, 0, Common::Rect(x0, y0, x1, y1));
 
-	const Common::Point beam[] = {
-		Common::Point(0, 0), Common::Point(1, 0), Common::Point(5, 0),
-		Common::Point(6, 0), Common::Point(2, 1), Common::Point(4, 1),
-		Common::Point(3, 2), Common::Point(3, 3), Common::Point(3, 4),
-		Common::Point(3, 5), Common::Point(3, 6), Common::Point(3, 7),
-		Common::Point(3, 8), Common::Point(3, 9), Common::Point(3, 10),
-		Common::Point(3, 11), Common::Point(3, 12), Common::Point(3, 13),
-		Common::Point(2, 14), Common::Point(4, 14), Common::Point(0, 15),
-		Common::Point(1, 15), Common::Point(5, 15), Common::Point(6, 15)
+	const byte beam[] = {
+		0,  0,  1,  0,  5,  0,  6,  0,  2,  1,  4,  1,  3,  2,  3,  3,
+		3,  4,  3,  5,  3,  6,  3,  7,  3,  8,  3,  9,  3, 10,  3, 11,
+		3, 12,  3, 13,  2, 14,  4, 14,  0, 15,  1, 15,  5, 15,  6, 15
 	};
 
-	for (int i = 0; i < ARRAYSIZE(beam); i++) {
-		uint32 color = _beamCursor->getPixel(beam[i].x, beam[i].y);
+	for (int i = 0; i < ARRAYSIZE(beam); i += 2) {
+		uint32 color = _beamCursor->getPixel(beam[i], beam[i + 1]);
 
-		if (color == kBlack)
-			color = kWhite;
+		// From looking at a couple of colors, it seems this is how
+		// the colors are inverted for 0-15. I'm just going to assume
+		// that the same method will work reasonably well for the
+		// custom colors. If there's anything else, just make it black.
+
+		if (color <= 15)
+			color = 15 - color;
+		else if (color > kCustomColor && color <= kCustomColor + 15)
+			color = kCustomColor + 15 - color;
 		else
 			color = kBlack;
 
-		_beamCursor->setPixel(beam[i].x, beam[i].y, color);
+		_beamCursor->setPixel(beam[i], beam[i + 1], color);
 	}
 
 	int srcX = 0;


Commit: d1e0e637dc2e32d5e565db957ee4b3c9da4d779c
    https://github.com/scummvm/scummvm/commit/d1e0e637dc2e32d5e565db957ee4b3c9da4d779c
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Fix off-by-one errors in Mac edit text scrolling

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 2790523271d..4fd04df006a 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -865,12 +865,12 @@ void MacGui::MacEditText::handleMouseHeld() {
 
 	int oldTextPos = _textPos;
 
-	int minTextPos = MIN(_bounds.width() - _font->getStringWidth(_text), 0);
+	int minTextPos = MIN(_bounds.width() - _font->getStringWidth(_text) - 1, 1);
 
-	if (mousePos.x < _bounds.left + 1 && mousePos.y < _bounds.bottom && _textPos < 0) {
+	if (mousePos.x < _bounds.left + 1 && mousePos.y < _bounds.bottom && _textPos < 1) {
 		_textPos += 8;
-		if (_textPos > 0)
-			_textPos = 0;
+		if (_textPos > 1)
+			_textPos = 1;
 	} else if (mousePos.x >= _bounds.right) {
 		_textPos -= 8;
 		if (_textPos < minTextPos)


Commit: a6778d61f35178d0f8b8759ae1782c0b925ddbb7
    https://github.com/scummvm/scummvm/commit/a6778d61f35178d0f8b8759ae1782c0b925ddbb7
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Add cursor blinking for Mac edit text widget

Simply moving (or blinking) the cursor will not trigger a full redraw of
the widget. I think that's enough drawing optimization to make it
acceptable. I've also added a selectAll() method to it, and made several
minor bugfixes. The rest is just cleanups and renamings.

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 4fd04df006a..a74e025a723 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -518,14 +518,14 @@ void MacGui::MacCheckbox::handleMouseUp(Common::Event &event) {
 // Static text widget
 // ---------------------------------------------------------------------------
 
-void MacGui::MacText::draw(bool drawFocused) {
+void MacGui::MacStaticText::draw(bool drawFocused) {
 	if (!_visible)
 		return;
 
 	if (!_redraw && !_fullRedraw)
 		return;
 
-	debug(1, "MacGui::MacText: Drawing text %d (_fullRedraw = %d, drawFocused = %d, _value = %d)", _id, _fullRedraw, drawFocused, _value);
+	debug(1, "MacGui::MacStaticText: Drawing text %d (_fullRedraw = %d, drawFocused = %d, _value = %d)", _id, _fullRedraw, drawFocused, _value);
 
 	_window->innerSurface()->fillRect(_bounds, kWhite);
 	drawText(_text, _bounds.left, _bounds.top, _bounds.width(), kBlack, Graphics::kTextAlignLeft, 1);
@@ -574,17 +574,6 @@ bool MacGui::MacEditText::findWidget(int x, int y) const {
 	return _bounds.contains(x, y);
 }
 
-void MacGui::MacEditText::updateSelection(int x, int y) {
-	int oldSelectLen = _selectLen;
-
-	int pos = getTextPosFromMouse(x, y);
-
-	_selectLen = pos - _caretPos;
-
-	if (_selectLen != oldSelectLen)
-		setRedraw();
-}
-
 int MacGui::MacEditText::getTextPosFromMouse(int x, int y) {
 	if (_text.empty())
 		return 0;
@@ -618,6 +607,17 @@ int MacGui::MacEditText::getTextPosFromMouse(int x, int y) {
 	return i;
 }
 
+void MacGui::MacEditText::updateSelection(int x, int y) {
+	int oldSelectLen = _selectLen;
+
+	int pos = getTextPosFromMouse(x, y);
+
+	_selectLen = pos - _caretPos;
+
+	if (_selectLen != oldSelectLen)
+		setRedraw();
+}
+
 void MacGui::MacEditText::deleteSelection() {
 	int startPos;
 	int len;
@@ -633,24 +633,26 @@ void MacGui::MacEditText::deleteSelection() {
 	_text.erase(startPos, len);
 	_caretPos = startPos;
 	_selectLen = 0;
+	setRedraw();
+}
+
+void MacGui::MacEditText::selectAll() {
+	_caretPos = 0;
+	_selectLen = _text.size();
+	setRedraw();
 }
 
 void MacGui::MacEditText::draw(bool drawFocused) {
 	if (!_visible)
 		return;
 
-	if (!_redraw && !_fullRedraw)
-		return;
-
-	Graphics::Surface *s = _window->innerSurface();
-
-	s->fillRect(_bounds, kWhite);
-
 	int caretX = 0;
 
-	if (_selectLen == 0) {
-		// Make sure the caret is visible
+	// Calculate the caret position, and make sure that it will be placed
+	// inside the visible area of the widget. This may require scrolling
+	// the text, which will trigger a redraw.
 
+	if (_selectLen == 0) {
 		caretX = _textPos - 1;
 
 		for (int i = 0; i < _caretPos; i++)
@@ -665,69 +667,111 @@ void MacGui::MacEditText::draw(bool drawFocused) {
 
 		if (delta) {
 			_textPos -= delta;
+			_caretX -= delta;
 			caretX -= delta;
+			setRedraw();
 		}
 	}
 
-	int selectStart = -1;
-	int selectEnd = -1;
+	// Redraw the contents of the widget. This redraws the entire text,
+	// which is a bit wasteful.
 
-	if (_selectLen != 0) {
-		if (_selectLen < 0) {
-			selectStart = _caretPos + _selectLen;
-			selectEnd = _caretPos - 1;
-		} else {
-			selectStart = _caretPos;
-			selectEnd = _caretPos + _selectLen - 1;
+	if (_redraw || _fullRedraw) {
+		Graphics::Surface *s = _window->innerSurface();
+
+		s->fillRect(_bounds, kWhite);
+
+		int selectStart = -1;
+		int selectEnd = -1;
+
+		if (_selectLen != 0) {
+			if (_selectLen < 0) {
+				selectStart = _caretPos + _selectLen;
+				selectEnd = _caretPos - 1;
+			} else {
+				selectStart = _caretPos;
+				selectEnd = _caretPos + _selectLen - 1;
+			}
 		}
-	}
 
-	int x = _textPos;
-	int y = 0;
+		int x = _textPos;
+		int y = 0;
 
-	bool firstChar = true;
-	bool firstCharSelected = false;
-	bool lastCharSelected = false;
+		bool firstChar = true;
+		bool firstCharSelected = false;
+		bool lastCharSelected = false;
 
-	for (int i = 0; i < (int)_text.size() && x < _textSurface.w; i++) {
-		Color color = kBlack;
-		int charWidth = _font->getCharWidth(_text[i]);
+		for (int i = 0; i < (int)_text.size() && x < _textSurface.w; i++) {
+			Color color = kBlack;
+			int charWidth = _font->getCharWidth(_text[i]);
 
-		if (x + charWidth >= 0) {
-			if (_selectLen != 0 && i >= selectStart && i <= selectEnd) {
-				if (firstChar)
-					firstCharSelected = true;
-				lastCharSelected = true;
+			if (x + charWidth >= 0) {
+				if (_selectLen != 0 && i >= selectStart && i <= selectEnd) {
+					if (firstChar)
+						firstCharSelected = true;
+					lastCharSelected = true;
 
-				_textSurface.fillRect(Common::Rect(x, 0, x + charWidth, _textSurface.h), kBlack);
-				color = kWhite;
-			} else
-				lastCharSelected = false;
+					_textSurface.fillRect(Common::Rect(x, 0, x + charWidth, _textSurface.h), kBlack);
+					color = kWhite;
+				} else
+					lastCharSelected = false;
 
-			_font->drawChar(&_textSurface, _text[i], x, y, color);
-			firstChar = false;
+				_font->drawChar(&_textSurface, _text[i], x, y, color);
+				firstChar = false;
+			}
+
+			x += charWidth;
 		}
 
-		x += charWidth;
-	}
+		if (firstCharSelected)
+			_window->innerSurface()->fillRect(Common::Rect(_bounds.left + 1, _bounds.top, _bounds.left + 2, _bounds.bottom), kBlack);
 
-	if (firstCharSelected)
-		_window->innerSurface()->fillRect(Common::Rect(_bounds.left + 1, _bounds.top, _bounds.left + 2, _bounds.bottom), kBlack);
+		if (lastCharSelected && _bounds.left + x + 1 < _bounds.right)
+			_window->innerSurface()->fillRect(Common::Rect(_bounds.left + x + 1, _bounds.top, _bounds.right, _bounds.bottom), kBlack);
 
-	if (lastCharSelected && _bounds.left + x + 1 < _bounds.right)
-		_window->innerSurface()->fillRect(Common::Rect(_bounds.left + x + 1, _bounds.top, _bounds.right, _bounds.bottom), kBlack);
+		_window->markRectAsDirty(_bounds);
+	}
+
+	// Redraw the caret, if it has changed since the last time.
 
 	if (_selectLen == 0) {
-		_textSurface.vLine(caretX, 0, _textSurface.h - 1, kRed);
-	}
+		uint32 now = _window->_system->getMillis();
+		bool caretVisible = _caretVisible;
 
-	_window->markRectAsDirty(_bounds);
+		if (now >= _nextCaretBlink) {
+			_caretVisible = !_caretVisible;
+			_nextCaretBlink = now + 500;
+		}
+
+		if (caretX != _caretX || caretVisible != _caretVisible) {
+			if (caretX != _caretX && !_redraw && !_fullRedraw) {
+				// Erase the old caret. Not needed if the whole
+				// widget was just redrawn.
+
+				_textSurface.vLine(_caretX, 0, _bounds.bottom - 1, kWhite);
+				if (!_redraw && !_fullRedraw)
+					_window->markRectAsDirty(Common::Rect(_bounds.left + _caretX + 1, _bounds.top, _bounds.left + _caretX + 2, _bounds.bottom));
+			}
+
+			// Draw the new caret
+
+			_textSurface.vLine(caretX, 0, _bounds.bottom - 1, _caretVisible ? kBlack : kWhite);
+
+			if (!_redraw && !_fullRedraw)
+				_window->markRectAsDirty(Common::Rect(_bounds.left + caretX + 1, _bounds.top, _bounds.left + caretX + 2, _bounds.bottom));
+
+			_caretX = caretX;
+		}
+	}
 
 	_redraw = false;
 	_fullRedraw = false;
 }
 
 void MacGui::MacEditText::handleMouseDown(Common::Event &event) {
+	int oldSelectLen = _selectLen;
+	int oldCaretPos = _caretPos;
+
 	uint32 now = _window->_system->getMillis();
 	int x = event.mouse.x - _bounds.left;
 
@@ -779,6 +823,12 @@ void MacGui::MacEditText::handleMouseDown(Common::Event &event) {
 				}
 			}
 
+			if (startPos < 0)
+				startPos = 0;
+
+			if (endPos >= (int)_text.size())
+				endPos = _text.size() - 1;
+
 			_caretPos = startPos;
 			_selectLen = endPos - startPos + 1;
 		}
@@ -791,36 +841,50 @@ void MacGui::MacEditText::handleMouseDown(Common::Event &event) {
 		_selectLen = 0;
 	}
 
-	_redraw = true;
+	if (_selectLen != oldSelectLen || _caretPos != oldCaretPos)
+		setRedraw();
 }
 
 bool MacGui::MacEditText::handleKeyDown(Common::Event &event) {
 	if (event.kbd.flags & (Common::KBD_CTRL | Common::KBD_ALT | Common::KBD_META))
 		return false;
 
+	// Stop caret blinking while typing. We do this by making the caret
+	// visible, but force the next blink to happen immediately. Otherwise,
+	// it may not register that the blink state has changed.
+
+	_caretVisible = false;
+	_nextCaretBlink = _window->_system->getMillis();
+
 	switch (event.kbd.keycode) {
 	case Common::KEYCODE_LEFT:
-		if (_selectLen < 0) {
+		if (_selectLen < 0)
 			_caretPos = _caretPos + _selectLen;
-			_selectLen = -_selectLen;
-		}
 
-		if (_caretPos > 0)
-			_caretPos--;
+		_caretPos--;
 
-		_selectLen = 0;
+		if (_caretPos < 0)
+			_caretPos = 0;
+
+		if (_selectLen != 0) {
+			_selectLen = 0;
+			setRedraw();
+		}
 		return true;
 
 	case Common::KEYCODE_RIGHT:
-		if (_selectLen > 0) {
+		if (_selectLen > 0)
 			_caretPos += _selectLen;
-			_selectLen = -_selectLen;
-		}
 
-		if (_caretPos < (int)_text.size())
-			_caretPos++;
+		_caretPos++;
+
+		if (_caretPos > (int)_text.size())
+			_caretPos = _text.size();
 
-		_selectLen = 0;
+		if (_selectLen != 0) {
+			_selectLen = 0;
+			setRedraw();
+		}
 		return true;
 
 	case Common::KEYCODE_BACKSPACE:
@@ -829,6 +893,7 @@ bool MacGui::MacEditText::handleKeyDown(Common::Event &event) {
 		} else if (_caretPos > 0) {
 			_caretPos--;
 			_text.deleteChar(_caretPos);
+			setRedraw();
 		}
 		return true;
 
@@ -837,6 +902,7 @@ bool MacGui::MacEditText::handleKeyDown(Common::Event &event) {
 			deleteSelection();
 		} else if (_caretPos < (int)_text.size()) {
 			_text.deleteChar(_caretPos);
+			setRedraw();
 		}
 		return true;
 
@@ -851,6 +917,7 @@ bool MacGui::MacEditText::handleKeyDown(Common::Event &event) {
 			deleteSelection();
 		_text.insertChar(event.kbd.ascii, _caretPos);
 		_caretPos++;
+		setRedraw();
 		return true;
 	}
 
@@ -888,7 +955,7 @@ void MacGui::MacEditText::handleMouseMove(Common::Event &event) {
 }
 
 // ---------------------------------------------------------------------------
-// Image widget
+// Picture widget
 // ---------------------------------------------------------------------------
 
 MacGui::MacPicture::MacPicture(MacGui::MacDialogWindow *window, Common::Rect bounds, int id, bool enabled) : MacWidget(window, bounds, "Picture", enabled) {
@@ -918,10 +985,12 @@ void MacGui::MacPicture::draw(bool drawFocused) {
 }
 
 // ---------------------------------------------------------------------------
-// Slider widget
+// Picture slider widget. This is the custom slider widget used for the Loom
+// and Indy 3 options dialogs. It consists of a background image and a slider
+// drag handle.
 // ---------------------------------------------------------------------------
 
-bool MacGui::MacSlider::findWidget(int x, int y) const {
+bool MacGui::MacPictureSlider::findWidget(int x, int y) const {
 	if (!_visible || !_enabled)
 		return false;
 
@@ -934,7 +1003,7 @@ bool MacGui::MacSlider::findWidget(int x, int y) const {
 	return _bounds.contains(x, y);
 }
 
-void MacGui::MacSlider::setValue(int value) {
+void MacGui::MacPictureSlider::setValue(int value) {
 	MacWidget::setValue(CLIP(value, _minValue, _maxValue));
 
 	int valueRange = _maxValue - _minValue;
@@ -947,7 +1016,7 @@ void MacGui::MacSlider::setValue(int value) {
 	setRedraw();
 }
 
-void MacGui::MacSlider::draw(bool drawFocused) {
+void MacGui::MacPictureSlider::draw(bool drawFocused) {
 	if (!_redraw && !_fullRedraw)
 		return;
 
@@ -974,7 +1043,7 @@ void MacGui::MacSlider::draw(bool drawFocused) {
 	_fullRedraw = false;
 }
 
-void MacGui::MacSlider::handleMouseDown(Common::Event &event) {
+void MacGui::MacPictureSlider::handleMouseDown(Common::Event &event) {
 	int mouseX = event.mouse.x;
 	int handleWidth = _handle->getBounds().width();
 
@@ -986,7 +1055,7 @@ void MacGui::MacSlider::handleMouseDown(Common::Event &event) {
 	handleMouseMove(event);
 }
 
-void MacGui::MacSlider::handleMouseUp(Common::Event &event) {
+void MacGui::MacPictureSlider::handleMouseUp(Common::Event &event) {
 	int posRange = (_maxX - _rightMargin) - (_minX + _rightMargin);
 	int posOffset = _handleX - (_minX + _leftMargin);
 
@@ -996,7 +1065,7 @@ void MacGui::MacSlider::handleMouseUp(Common::Event &event) {
 	setValue(_minValue + valueOffset);
 }
 
-void MacGui::MacSlider::handleMouseMove(Common::Event &event) {
+void MacGui::MacPictureSlider::handleMouseMove(Common::Event &event) {
 	_handleX = CLIP<int>(event.mouse.x - _bounds.left - _grabOffset, _minX, _maxX);
 	setRedraw();
 }
@@ -1094,14 +1163,14 @@ void MacGui::MacDialogWindow::setFocusedWidget(int x, int y) {
 		_focusedWidget = _widgets[nr];
 		_focusClick.x = x;
 		_focusClick.y = y;
-		_focusedWidget->setRedraw();
+		_focusedWidget->getFocus();
 	} else
 		clearFocusedWidget();
 }
 
 void MacGui::MacDialogWindow::clearFocusedWidget() {
 	if (_focusedWidget) {
-		_focusedWidget->setRedraw();
+		_focusedWidget->loseFocus();
 		_focusedWidget = nullptr;
 		_focusClick.x = -1;
 		_focusClick.y = -1;
@@ -1117,34 +1186,46 @@ int MacGui::MacDialogWindow::findWidget(int x, int y) const {
 	return -1;
 }
 
-void MacGui::MacDialogWindow::addButton(Common::Rect bounds, Common::String text, bool enabled) {
-	_widgets.push_back(new MacButton(this, bounds, text, enabled));
+MacGui::MacButton *MacGui::MacDialogWindow::addButton(Common::Rect bounds, Common::String text, bool enabled) {
+	MacGui::MacButton *button = new MacButton(this, bounds, text, enabled);
+	_widgets.push_back(button);
+	return button;
 }
 
-void MacGui::MacDialogWindow::addCheckbox(Common::Rect bounds, Common::String text, bool enabled) {
-	_widgets.push_back(new MacCheckbox(this, bounds, text, enabled));
+MacGui::MacCheckbox *MacGui::MacDialogWindow::addCheckbox(Common::Rect bounds, Common::String text, bool enabled) {
+	MacGui::MacCheckbox *checkbox = new MacCheckbox(this, bounds, text, enabled);
+	_widgets.push_back(checkbox);
+	return checkbox;
 }
 
-void MacGui::MacDialogWindow::addText(Common::Rect bounds, Common::String text, bool enabled) {
-	_widgets.push_back(new MacText(this, bounds, text, enabled));
+MacGui::MacStaticText *MacGui::MacDialogWindow::addStaticText(Common::Rect bounds, Common::String text, bool enabled) {
+	MacGui::MacStaticText *staticText = new MacStaticText(this, bounds, text, enabled);
+	_widgets.push_back(staticText);
+	return staticText;
 }
 
-void MacGui::MacDialogWindow::addEditText(Common::Rect bounds, Common::String text, bool enabled) {
-	_widgets.push_back(new MacEditText(this, bounds, text, enabled));
+MacGui::MacEditText *MacGui::MacDialogWindow::addEditText(Common::Rect bounds, Common::String text, bool enabled) {
+	MacGui::MacEditText *editText = new MacEditText(this, bounds, text, enabled);
+	_widgets.push_back(editText);
+	return editText;
 }
 
-void MacGui::MacDialogWindow::addPicture(Common::Rect bounds, int id, bool enabled) {
-	_widgets.push_back(new MacPicture(this, bounds, id, false));
+MacGui::MacPicture *MacGui::MacDialogWindow::addPicture(Common::Rect bounds, int id, bool enabled) {
+	MacGui::MacPicture *picture = new MacPicture(this, bounds, id, false);
+	_widgets.push_back(picture);
+	return picture;
 }
 
-void MacGui::MacDialogWindow::addSlider(int backgroundId, int handleId, bool enabled, int minX, int maxX, int minValue, int maxValue, int leftMargin, int rightMargin) {
+MacGui::MacPictureSlider *MacGui::MacDialogWindow::addPictureSlider(int backgroundId, int handleId, bool enabled, int minX, int maxX, int minValue, int maxValue, int leftMargin, int rightMargin) {
 	MacPicture *background = (MacPicture *)_widgets[backgroundId];
 	MacPicture *handle = (MacPicture *)_widgets[handleId];
 
 	background->setVisible(false);
 	handle->setVisible(false);
 
-	_widgets.push_back(new MacSlider(this, background, handle, enabled, minX, maxX, minValue, maxValue, leftMargin, rightMargin));
+	MacGui::MacPictureSlider *slider = new MacPictureSlider(this, background, handle, enabled, minX, maxX, minValue, maxValue, leftMargin, rightMargin);
+	_widgets.push_back(slider);
+	return slider;
 }
 
 void MacGui::MacDialogWindow::markRectAsDirty(Common::Rect r) {
@@ -1374,7 +1455,6 @@ int MacGui::MacDialogWindow::runDialog() {
 							_beamCursorVisible = false;
 							undrawBeamCursor();
 						}
-						_widgets[i]->setRedraw();
 						break;
 					}
 				}
@@ -2148,7 +2228,7 @@ MacGui::MacDialogWindow *MacGui::createDialog(int dialogId) {
 			case 8:
 				// Static text
 				str = getDialogString(res, len);
-				window->addText(r, str, enabled);
+				window->addStaticText(r, str, enabled);
 				break;
 
 			case 16:
@@ -2656,7 +2736,7 @@ bool MacLoomGui::runSaveDialog() {
 	window->addButton(Common::Rect(254, 128, 334, 148), "Cancel", true);
 	window->addButton(Common::Rect(254, 83, 334, 103), "Delete", true);
 
-	window->addEditText(Common::Rect(16, 164, 229, 180), "Game file", true);
+	MacGui::MacEditText *editText = window->addEditText(Common::Rect(16, 164, 229, 180), "Game file", true);
 
 	Graphics::Surface *s = window->innerSurface();
 	const Graphics::Font *font = getFont(kSystemFont);
@@ -2670,6 +2750,7 @@ bool MacLoomGui::runSaveDialog() {
 	font->drawString(s, "Save Game File as...", 14, 143, 218, kBlack, Graphics::kTextAlignLeft, 4);
 
 	window->setDefaultWidget(0);
+	editText->selectAll();
 
 	// When quitting, the default action is to not open a saved game
 	bool ret = false;
@@ -2729,10 +2810,10 @@ bool MacLoomGui::runOptionsDialog() {
 	if (!sound)
 		window->setWidgetEnabled(3, false);
 
-	window->addSlider(4, 5, true, 5, 105, 0, 9);
+	window->addPictureSlider(4, 5, true, 5, 105, 0, 9);
 	window->setWidgetValue(11, textSpeed);
 
-	window->addSlider(8, 9, true, 5, 69, 0, 2, 6, 4);
+	window->addPictureSlider(8, 9, true, 5, 69, 0, 2, 6, 4);
 	window->setWidgetValue(12, musicQuality);
 
 	// TODO: I don't know where it gets the "Machine Speed" from. It doesn't
@@ -4389,7 +4470,7 @@ bool MacIndy3Gui::runOptionsDialog() {
 	if (!sound)
 		window->setWidgetEnabled(3, false);
 
-	window->addSlider(4, 5, true, 5, 105, 0, 9);
+	window->addPictureSlider(4, 5, true, 5, 105, 0, 9);
 	window->setWidgetValue(9, textSpeed);
 
 	window->addSubstitution(Common::String::format("%d", _vm->VAR(_vm->VAR_MACHINE_SPEED)));
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 813eb3a2456..f29bd9f7569 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -174,6 +174,9 @@ public:
 		void setVisible(bool visible) { _visible = visible; }
 		bool isVisible() const { return _visible; }
 
+		virtual void getFocus() { setRedraw(); }
+		virtual void loseFocus() { setRedraw(); }
+
 		void setRedraw(bool fullRedraw = false);
 
 		bool isEnabled() const { return _enabled; }
@@ -226,9 +229,9 @@ public:
 	// as disabled so we enable it and make it "disabled" by giving it a
 	// custom findWidget().
 
-	class MacText : public MacWidget {
+	class MacStaticText : public MacWidget {
 	public:
-		MacText(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::String text, bool enabled) : MacWidget(window, bounds, text, true) {}
+		MacStaticText(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::String text, bool enabled) : MacWidget(window, bounds, text, true) {}
 
 		bool findWidget(int x, int y) const { return false; }
 
@@ -237,30 +240,37 @@ public:
 
 	class MacEditText : public MacWidget {
 	private:
-		int _textPos = 0;
+		int _textPos = 1;
 		int _selectLen = 0;
 		int _caretPos = 0;
+		int _caretX = -1;
 
 		uint32 _lastClickTime = 0;
 		uint32 _lastScrollTime = 0;
 
 		int _lastClickX = 0;
 
-		uint32 _lastBlinkTime = 0;
-		bool _caretVisible = false;
+		uint32 _nextCaretBlink = 0;
+		bool _caretVisible = true;
 
 		const Graphics::Font *_font;
 		Graphics::Surface _textSurface;
 
 		int getTextPosFromMouse(int x, int y);
+
+		void updateSelection(int x, int y);
 		void deleteSelection();
 
 	public:
 		MacEditText(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::String text, bool enabled);
 
+		void getFocus() {}
+		void loseFocus() {}
+
+		void selectAll();
+
 		bool useBeamCursor() { return true; }
 		bool findWidget(int x, int y) const;
-		void updateSelection(int x, int y);
 
 		void draw(bool drawFocused = false);
 
@@ -283,7 +293,7 @@ public:
 		void draw(bool drawFocused = false);
 	};
 
-	class MacSlider : public MacWidget {
+	class MacPictureSlider : public MacWidget {
 	private:
 		MacPicture *_background;
 		MacPicture *_handle;
@@ -298,7 +308,7 @@ public:
 		int _rightMargin;
 
 	public:
-		 MacSlider(MacGui::MacDialogWindow *window, MacPicture *background, MacPicture *handle, bool enabled, int minX, int maxX, int minValue, int maxValue, int leftMargin, int rightMargin)
+		 MacPictureSlider(MacGui::MacDialogWindow *window, MacPicture *background, MacPicture *handle, bool enabled, int minX, int maxX, int minValue, int maxValue, int leftMargin, int rightMargin)
 			: MacWidget(window, background->getBounds(), "Slider", enabled),
 			_background(background), _handle(handle), _minX(minX),
 			_maxX(maxX), _minValue(minValue), _maxValue(maxValue),
@@ -385,12 +395,12 @@ public:
 		int findWidget(int x, int y) const;
 		void redrawWidget(int nr) { _widgets[nr]->setRedraw(true); }
 
-		void addButton(Common::Rect bounds, Common::String text, bool enabled);
-		void addCheckbox(Common::Rect bounds, Common::String text, bool enabled);
-		void addText(Common::Rect bounds, Common::String text, bool enabled);
-		void addEditText(Common::Rect bounds, Common::String text, bool enabled);
-		void addPicture(Common::Rect bounds, int id, bool enabled);
-		void addSlider(int backgroundId, int handleId, bool enabled, int minX, int maxX, int minValue, int maxValue, int leftMargin = 0, int rightMargin = 0);
+		MacGui::MacButton *addButton(Common::Rect bounds, Common::String text, bool enabled);
+		MacGui::MacCheckbox *addCheckbox(Common::Rect bounds, Common::String text, bool enabled);
+		MacGui::MacStaticText *addStaticText(Common::Rect bounds, Common::String text, bool enabled);
+		MacGui::MacEditText *addEditText(Common::Rect bounds, Common::String text, bool enabled);
+		MacGui::MacPicture *addPicture(Common::Rect bounds, int id, bool enabled);
+		MacGui::MacPictureSlider *addPictureSlider(int backgroundId, int handleId, bool enabled, int minX, int maxX, int minValue, int maxValue, int leftMargin = 0, int rightMargin = 0);
 
 		void addSubstitution(Common::String text) { _substitutions.push_back(text); }
 		void replaceSubstitution(int nr, Common::String text) { _substitutions[nr] = text; }


Commit: 7556308aa49df6952b67ca34dea6c90c9fb2e098
    https://github.com/scummvm/scummvm/commit/7556308aa49df6952b67ca34dea6c90c9fb2e098
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Preliminary work on Mac slider widget

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index a74e025a723..bae10b41665 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -984,6 +984,35 @@ void MacGui::MacPicture::draw(bool drawFocused) {
 	_fullRedraw = false;
 }
 
+// ---------------------------------------------------------------------------
+// Standard slider widget
+// ---------------------------------------------------------------------------
+
+void MacGui::MacSlider::draw(bool drawFocused) {
+	if (!_visible)
+		return;
+
+	if (!_redraw && !_fullRedraw)
+		return;
+
+	debug(1, "MacGui::MacSlider: Drawing slider (_fullRedraw = %d, drawFocused = %d, _value = %d)", _fullRedraw, drawFocused, _value);
+
+	Graphics::Surface *s = _window->innerSurface();
+
+	s->frameRect(_bounds, kBlack);
+
+	_redraw = false;
+	_fullRedraw = false;
+
+	_window->markRectAsDirty(_bounds);
+}
+
+void MacGui::MacSlider::handleMouseDown(Common::Event &event) {
+}
+
+void MacGui::MacSlider::handleMouseMove(Common::Event &event) {
+}
+
 // ---------------------------------------------------------------------------
 // Picture slider widget. This is the custom slider widget used for the Loom
 // and Indy 3 options dialogs. It consists of a background image and a slider
@@ -1020,7 +1049,7 @@ void MacGui::MacPictureSlider::draw(bool drawFocused) {
 	if (!_redraw && !_fullRedraw)
 		return;
 
-	debug(1, "MacGui::MacPicture: Drawing slider %d (_fullRedraw = %d, drawFocused = %d, _value = %d)", _id, _fullRedraw, drawFocused, _value);
+	debug(1, "MacGui::MacPictureSlider: Drawing slider %d (_fullRedraw = %d, drawFocused = %d, _value = %d)", _id, _fullRedraw, drawFocused, _value);
 
 	Graphics::Surface *bgSprite = _background->getPicture();
 	Graphics::Surface *hSprite = _handle->getPicture();
@@ -1216,6 +1245,12 @@ MacGui::MacPicture *MacGui::MacDialogWindow::addPicture(Common::Rect bounds, int
 	return picture;
 }
 
+MacGui::MacSlider *MacGui::MacDialogWindow::addSlider(Common::Rect bounds, int minValue, int maxValue, int pageSize, bool enabled) {
+	MacGui::MacSlider *slider = new MacSlider(this, bounds, minValue, maxValue, pageSize, enabled);
+	_widgets.push_back(slider);
+	return slider;
+}
+
 MacGui::MacPictureSlider *MacGui::MacDialogWindow::addPictureSlider(int backgroundId, int handleId, bool enabled, int minX, int maxX, int minValue, int maxValue, int leftMargin, int rightMargin) {
 	MacPicture *background = (MacPicture *)_widgets[backgroundId];
 	MacPicture *handle = (MacPicture *)_widgets[handleId];
@@ -2699,11 +2734,11 @@ bool MacLoomGui::runOpenDialog() {
 	window->addButton(Common::Rect(254, 135, 334, 155), "Open", true);
 	window->addButton(Common::Rect(254, 104, 334, 124), "Cancel", true);
 	window->addButton(Common::Rect(254, 59, 334, 79), "Delete", true);
+	window->addSlider(Common::Rect(216, 13, 232, 159), 0, 50, 7, true);
 
 	Graphics::Surface *s = window->innerSurface();
 
 	s->frameRect(Common::Rect(14, 13, 217, 159), kBlack);
-	s->frameRect(Common::Rect(216, 13, 232, 159), kBlack);
 	s->hLine(253, 91, 334, kBlack);
 
 	window->setDefaultWidget(0);
@@ -2735,6 +2770,7 @@ bool MacLoomGui::runSaveDialog() {
 	window->addButton(Common::Rect(254, 159, 334, 179), "Save", true);
 	window->addButton(Common::Rect(254, 128, 334, 148), "Cancel", true);
 	window->addButton(Common::Rect(254, 83, 334, 103), "Delete", true);
+	window->addSlider(Common::Rect(216, 9, 232, 137), 0, 50, 7, true);
 
 	MacGui::MacEditText *editText = window->addEditText(Common::Rect(16, 164, 229, 180), "Game file", true);
 
@@ -2743,7 +2779,6 @@ bool MacLoomGui::runSaveDialog() {
 
 	s->frameRect(Common::Rect(14, 161, 232, 183), kBlack);
 	s->frameRect(Common::Rect(14, 9, 217, 137), kBlack);
-	s->frameRect(Common::Rect(216, 9, 232, 137), kBlack);
 
 	s->hLine(253, 115, 334, kBlack);
 
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index f29bd9f7569..5f6227bcfd6 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -293,6 +293,23 @@ public:
 		void draw(bool drawFocused = false);
 	};
 
+	class MacSlider : public MacWidget {
+	private:
+		int _minValue;
+		int _maxValue;
+		int _pageSize;
+
+	public:
+		MacSlider(MacGui::MacDialogWindow *window, Common::Rect bounds, int minValue, int maxValue, int pageSize, bool enabled)
+			: MacWidget(window, bounds, "Slider", enabled),
+			_minValue(minValue), _maxValue(maxValue), _pageSize(pageSize) {}
+
+		void draw(bool drawFocued = false);
+
+		void handleMouseDown(Common::Event &event);
+		void handleMouseMove(Common::Event &event);
+	};
+
 	class MacPictureSlider : public MacWidget {
 	private:
 		MacPicture *_background;
@@ -400,6 +417,7 @@ public:
 		MacGui::MacStaticText *addStaticText(Common::Rect bounds, Common::String text, bool enabled);
 		MacGui::MacEditText *addEditText(Common::Rect bounds, Common::String text, bool enabled);
 		MacGui::MacPicture *addPicture(Common::Rect bounds, int id, bool enabled);
+		MacGui::MacSlider *addSlider(Common::Rect bounds, int minValue, int maxValue, int pageSize, bool enabled);
 		MacGui::MacPictureSlider *addPictureSlider(int backgroundId, int handleId, bool enabled, int minX, int maxX, int minValue, int maxValue, int leftMargin = 0, int rightMargin = 0);
 
 		void addSubstitution(Common::String text) { _substitutions.push_back(text); }


Commit: fb6d85f64fd6653324dcd57778f89d076d4f1094
    https://github.com/scummvm/scummvm/commit/fb6d85f64fd6653324dcd57778f89d076d4f1094
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Hard-code the width of the Mac slider widget

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index bae10b41665..c133337b126 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -1245,8 +1245,8 @@ MacGui::MacPicture *MacGui::MacDialogWindow::addPicture(Common::Rect bounds, int
 	return picture;
 }
 
-MacGui::MacSlider *MacGui::MacDialogWindow::addSlider(Common::Rect bounds, int minValue, int maxValue, int pageSize, bool enabled) {
-	MacGui::MacSlider *slider = new MacSlider(this, bounds, minValue, maxValue, pageSize, enabled);
+MacGui::MacSlider *MacGui::MacDialogWindow::addSlider(int x, int y, int h, int minValue, int maxValue, int pageSize, bool enabled) {
+	MacGui::MacSlider *slider = new MacSlider(this, Common::Rect(x, y, x + 16, y + h), minValue, maxValue, pageSize, enabled);
 	_widgets.push_back(slider);
 	return slider;
 }
@@ -2734,7 +2734,7 @@ bool MacLoomGui::runOpenDialog() {
 	window->addButton(Common::Rect(254, 135, 334, 155), "Open", true);
 	window->addButton(Common::Rect(254, 104, 334, 124), "Cancel", true);
 	window->addButton(Common::Rect(254, 59, 334, 79), "Delete", true);
-	window->addSlider(Common::Rect(216, 13, 232, 159), 0, 50, 7, true);
+	window->addSlider(216, 13, 146, 0, 50, 7, true);
 
 	Graphics::Surface *s = window->innerSurface();
 
@@ -2770,7 +2770,7 @@ bool MacLoomGui::runSaveDialog() {
 	window->addButton(Common::Rect(254, 159, 334, 179), "Save", true);
 	window->addButton(Common::Rect(254, 128, 334, 148), "Cancel", true);
 	window->addButton(Common::Rect(254, 83, 334, 103), "Delete", true);
-	window->addSlider(Common::Rect(216, 9, 232, 137), 0, 50, 7, true);
+	window->addSlider(216, 9, 128, 0, 50, 7, true);
 
 	MacGui::MacEditText *editText = window->addEditText(Common::Rect(16, 164, 229, 180), "Game file", true);
 
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 5f6227bcfd6..d72ec0a6ab4 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -417,7 +417,7 @@ public:
 		MacGui::MacStaticText *addStaticText(Common::Rect bounds, Common::String text, bool enabled);
 		MacGui::MacEditText *addEditText(Common::Rect bounds, Common::String text, bool enabled);
 		MacGui::MacPicture *addPicture(Common::Rect bounds, int id, bool enabled);
-		MacGui::MacSlider *addSlider(Common::Rect bounds, int minValue, int maxValue, int pageSize, bool enabled);
+		MacGui::MacSlider *addSlider(int x, int y, int h, int minValue, int maxValue, int pageSize, bool enabled);
 		MacGui::MacPictureSlider *addPictureSlider(int backgroundId, int handleId, bool enabled, int minX, int maxX, int minValue, int maxValue, int leftMargin = 0, int rightMargin = 0);
 
 		void addSubstitution(Common::String text) { _substitutions.push_back(text); }


Commit: c4c605502bd2ed89a37af0952d4191eeb4739579
    https://github.com/scummvm/scummvm/commit/c4c605502bd2ed89a37af0952d4191eeb4739579
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Further work on Mac slider widget

There are lots of bugs, and dragging is not implemented yet. But it's
starting to look pretty good, I think.

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index c133337b126..03d7b442c7c 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -234,7 +234,7 @@ MacGui::MacWidget::MacWidget(MacGui::MacDialogWindow *window, Common::Rect bound
 }
 
 bool MacGui::MacWidget::findWidget(int x, int y) const {
-	return _visible && _enabled && _bounds.contains(x, y);
+	return _bounds.contains(x, y);
 }
 
 void MacGui::MacWidget::setRedraw(bool fullRedraw) {
@@ -254,7 +254,11 @@ void MacGui::MacWidget::setValue(int value) {
 	setRedraw();
 }
 
-int MacGui::MacWidget::drawText(Common::String text, int x, int y, int w, Color color, Graphics::TextAlign align, int deltax) {
+void MacGui::MacWidget::drawBitmap(Common::Rect r, const uint16 *bitmap, Color color) const {
+	_window->_gui->drawBitmap(_window->innerSurface(), r, bitmap, color);
+}
+
+int MacGui::MacWidget::drawText(Common::String text, int x, int y, int w, Color color, Graphics::TextAlign align, int deltax) const {
 	if (text.empty())
 		return 0;
 
@@ -338,9 +342,6 @@ int MacGui::MacWidget::drawText(Common::String text, int x, int y, int w, Color
 // ---------------------------------------------------------------------------
 
 void MacGui::MacButton::draw(bool drawFocused) {
-	if (!_visible)
-		return;
-
 	if (!_redraw && !_fullRedraw)
 		return;
 
@@ -466,13 +467,10 @@ MacGui::MacCheckbox::MacCheckbox(MacGui::MacDialogWindow *window, Common::Rect b
 }
 
 bool MacGui::MacCheckbox::findWidget(int x, int y) const {
-	return _visible && _enabled && _hitBounds.contains(x, y);
+	return _hitBounds.contains(x, y);
 }
 
 void MacGui::MacCheckbox::draw(bool drawFocused) {
-	if (!_visible)
-		return;
-
 	if (!_redraw && !_fullRedraw)
 		return;
 
@@ -519,9 +517,6 @@ void MacGui::MacCheckbox::handleMouseUp(Common::Event &event) {
 // ---------------------------------------------------------------------------
 
 void MacGui::MacStaticText::draw(bool drawFocused) {
-	if (!_visible)
-		return;
-
 	if (!_redraw && !_fullRedraw)
 		return;
 
@@ -562,9 +557,6 @@ MacGui::MacEditText::MacEditText(MacGui::MacDialogWindow *window, Common::Rect b
 }
 
 bool MacGui::MacEditText::findWidget(int x, int y) const {
-	if (!_visible || !_enabled)
-		return false;
-
 	// Once we start dragging the handle, any mouse position is considered
 	// within the widget.
 
@@ -643,9 +635,6 @@ void MacGui::MacEditText::selectAll() {
 }
 
 void MacGui::MacEditText::draw(bool drawFocused) {
-	if (!_visible)
-		return;
-
 	int caretX = 0;
 
 	// Calculate the caret position, and make sure that it will be placed
@@ -970,9 +959,6 @@ MacGui::MacPicture::~MacPicture() {
 }
 
 void MacGui::MacPicture::draw(bool drawFocused) {
-	if (!_visible)
-		return;
-
 	if (!_redraw && !_fullRedraw)
 		return;
 
@@ -988,10 +974,68 @@ void MacGui::MacPicture::draw(bool drawFocused) {
 // Standard slider widget
 // ---------------------------------------------------------------------------
 
-void MacGui::MacSlider::draw(bool drawFocused) {
-	if (!_visible)
-		return;
+MacGui::MacSlider::MacSlider(MacGui::MacDialogWindow *window, Common::Rect bounds, int minValue, int maxValue, int pageSize, bool enabled)
+	: MacWidget(window, bounds, "Slider", enabled),
+	_minValue(minValue), _maxValue(maxValue), _pageSize(pageSize) {
+	_boundsButtonUp = Common::Rect(_bounds.left, _bounds.top, _bounds.right, _bounds.top + 16);
+	_boundsButtonDown = Common::Rect(_bounds.left, _bounds.bottom - 16, _bounds.right, _bounds.bottom);
+	_boundsBody = Common::Rect(_bounds.left, _bounds.top + 16, _bounds.right, _bounds.bottom - 16);
+
+	_clickPos.x = -1;
+	_clickPos.y = -1;
+}
+
+Common::Rect MacGui::MacSlider::getHandleRect(int value) {
+	int handlePos = value * (_boundsBody.bottom - _boundsBody.top - 16) / (_maxValue - _minValue);
+
+	Common::Rect handleRect;
+
+	handleRect.left = _boundsBody.left + 1;
+	handleRect.top = _boundsBody.top + handlePos;
+	handleRect.right = _boundsBody.right - 1;
+	handleRect.bottom = handleRect.top + 16;
+
+	return handleRect;
+}
+
+void MacGui::MacSlider::fill(Common::Rect r) {
+	Color pattern[2][4] = {
+		{ kBlack, kWhite, kWhite, kWhite },
+		{ kWhite, kWhite, kBlack, kWhite }
+	};
+
+	Graphics::Surface *s = _window->innerSurface();
+
+	for (int y = r.top; y < r.bottom; y++) {
+		for (int x = r.left; x < r.right; x++) {
+			s->setPixel(x, y, pattern[y % 2][x % 4]);
+		}
+	}
+}
+
+void MacGui::MacSlider::drawHandle(Common::Rect r) {
+	Graphics::Surface *s = _window->innerSurface();
+
+	s->frameRect(r, kBlack);
+	r.grow(-1);
+	s->fillRect(r, kWhite);
+}
+
+bool MacGui::MacSlider::findWidget(int x, int y) const {
+	if (_maxValue - _minValue <= _pageSize)
+		return false;
+
+	Common::Rect bounds = _bounds;
 
+	if (_dragOffset) {
+		bounds.left -= 25;
+		bounds.right += 25;
+	}
+
+	return bounds.contains(x, y);
+}
+
+void MacGui::MacSlider::draw(bool drawFocused) {
 	if (!_redraw && !_fullRedraw)
 		return;
 
@@ -999,18 +1043,233 @@ void MacGui::MacSlider::draw(bool drawFocused) {
 
 	Graphics::Surface *s = _window->innerSurface();
 
-	s->frameRect(_bounds, kBlack);
+	if (_fullRedraw) {
+		s->frameRect(_bounds, kBlack);
+		s->hLine(_bounds.left + 1, _bounds.top + 15, _bounds.right - 2, kBlack);
+		s->hLine(_bounds.left + 1, _bounds.bottom - 16, _bounds.right - 2, kBlack);
+	}
+
+	if (_fullRedraw || _redrawBody) {
+		Common::Rect fillRect(_boundsBody.left + 1, _boundsBody.top, _boundsBody.right - 1, _boundsBody.bottom);
+
+		if (_maxValue - _minValue > _pageSize)
+			fill(fillRect);
+		else
+			s->fillRect(fillRect, kWhite);
+
+		Common::Rect handleRect = getHandleRect(_value);
+		drawHandle(handleRect);
+
+		if (!_fullRedraw)
+			_window->markRectAsDirty(_boundsBody);
+	}
+
+	// There is a narrower version of these arrows, but I'm speculating
+	// that they're intended for a 9" Mac screen.
+
+	if (_fullRedraw || _redrawUpArrow) {
+		const uint16 upArrow[] = {
+			0x0600, 0x0900, 0x1080, 0x2040, 0x4020,
+			0xF0F0, 0x1080, 0x1080, 0x1080, 0x1F80
+		};
+
+		const uint16 upArrowFilled[] = {
+			0x0600, 0x0F00, 0x1F80, 0x3FC0, 0x7FE0,
+			0xFFF0, 0x1F80, 0x1F80, 0x1F80, 0x1F80
+		};
+
+		Common::Rect r = _boundsButtonUp;
+		r.grow(-1);
+
+		s->fillRect(r, kWhite);
+		drawBitmap(Common::Rect(r.left + 1, r.top + 2, r.right - 1, r.top + 12), _upArrowPressed ? upArrowFilled : upArrow, kBlack);
+		_redrawUpArrow = false;
+
+		if (!_fullRedraw)
+			_window->markRectAsDirty(r);
+	}
+
+	if (_fullRedraw || _redrawDownArrow) {
+		const uint16 downArrow[] = {
+			0x1F80,	0x1080,	0x1080,	0x1080,	0xF0F0,
+			0x4020,	0x2040,	0x1080,	0x0900,	0x0600
+		};
+
+		const uint16 downArrowFilled[] = {
+			0x1F80, 0x1F80, 0x1F80, 0x1F80, 0xFFF0,
+			0x7FE0, 0x3FC0, 0x1F80, 0x0F00, 0x0600
+		};
+
+		Common::Rect r = _boundsButtonDown;
+		r.grow(-1);
+
+		s->fillRect(r, kWhite);
+		drawBitmap(Common::Rect(r.left + 1, r.top + 2, r.right - 1, r.top + 12), _downArrowPressed ? downArrowFilled : downArrow, kBlack);
+		_redrawDownArrow = false;
+
+		if (!_fullRedraw)
+			_window->markRectAsDirty(r);
+	}
+
+	if (_fullRedraw)
+		_window->markRectAsDirty(_bounds);
 
 	_redraw = false;
 	_fullRedraw = false;
-
-	_window->markRectAsDirty(_bounds);
 }
 
 void MacGui::MacSlider::handleMouseDown(Common::Event &event) {
+	int x = event.mouse.x;
+	int y = event.mouse.y;
+
+	_clickPos.x = x;
+	_clickPos.y = y;
+	_paging = 0;
+	_dragOffset = -1;
+
+	int oldValue = _value;
+
+	if (_boundsButtonUp.contains(x, y)) {
+		_nextRepeat = _window->_system->getMillis() + 200;
+		_upArrowPressed = true;
+		_redrawUpArrow = true;
+		_value = MAX(_minValue, _value - 1);
+		setRedraw();
+	} else if (_boundsButtonDown.contains(x, y)) {
+		_nextRepeat = _window->_system->getMillis() + 200;
+		_downArrowPressed = true;
+		_redrawDownArrow = true;
+		_value = MIN(_maxValue, _value + 1);
+		setRedraw();
+	} else {
+		Common::Rect handleRect = getHandleRect(_value);
+
+		if (y < handleRect.top) {
+			_nextRepeat = _window->_system->getMillis() + 200;
+			_paging = -1;
+			_value = MAX(_minValue, _value - (_pageSize - 1));
+		} else if (y >= handleRect.bottom) {
+			_nextRepeat = _window->_system->getMillis() + 200;
+			_paging = 1;
+			_value = MIN(_maxValue, _value + (_pageSize - 1));
+		} else {
+			_dragOffset = y - handleRect.top;
+		}
+	}
+
+	if (_value != oldValue) {
+		_redrawBody = true;
+		setRedraw();
+	}
+}
+
+void MacGui::MacSlider::handleMouseUp(Common::Event &event) {
+	if (_upArrowPressed) {
+		_upArrowPressed = false;
+		_redrawUpArrow = true;
+		setRedraw();
+	} else if (_downArrowPressed) {
+		_downArrowPressed = false;
+		_redrawDownArrow = true;
+		setRedraw();
+	} else if (_dragOffset >= 0) {
+		_redrawBody = true;
+		setRedraw();
+	}
+
+	_paging = 0;
+	_dragOffset = -1;
+	_clickPos.x = -1;
+	_clickPos.y = -1;
 }
 
 void MacGui::MacSlider::handleMouseMove(Common::Event &event) {
+	int x = event.mouse.x;
+	int y = event.mouse.y;
+
+	if (!findWidget(x, y))
+		return;
+
+	if (_dragOffset) {
+		_redrawBody = true;
+		setRedraw();
+	} else {
+		if (!_boundsButtonUp.contains(x, y)) {
+			if (_upArrowPressed) {
+				_upArrowPressed = false;
+				_redrawUpArrow = true;
+				setRedraw();
+			}
+		} else {
+			if (_boundsButtonUp.contains(_clickPos) && !_upArrowPressed) {
+				_nextRepeat = _window->_system->getMillis() + 200;
+				_upArrowPressed = true;
+				_redrawUpArrow = true;
+				setRedraw();
+			}
+		}
+
+		if (!_boundsButtonDown.contains(x, y)) {
+			if (_downArrowPressed) {
+				_downArrowPressed = false;
+				_redrawDownArrow = true;
+				setRedraw();
+			}
+		} else {
+			if (_boundsButtonDown.contains(_clickPos) && !_downArrowPressed) {
+				_nextRepeat = _window->_system->getMillis() + 200;
+				_downArrowPressed = true;
+				_redrawDownArrow = true;
+				setRedraw();
+			}
+		}
+	}
+}
+
+void MacGui::MacSlider::handleMouseHeld() {
+	uint32 now = _window->_system->getMillis();
+	Common::Point p = _window->getMousePos();
+
+	if (now < _nextRepeat || !findWidget(p.x, p.y))
+		return;
+
+	int oldValue = _value;
+
+	if (_upArrowPressed) {
+		_value = MAX(_minValue, _value - 1);
+		_nextRepeat = now + 80;
+	}
+
+	if (_downArrowPressed) {
+		_value = MIN(_maxValue, _value + 1);
+		_nextRepeat = now + 80;
+	}
+
+	if (_paging) {
+		Common::Rect r = getHandleRect(_value);
+
+		if (_paging == -1) {
+			if (p.y < r.top) {
+				_nextRepeat = now + 100;
+				_value = MAX(_minValue, _value - (_pageSize - 1));
+			}
+		} else if (_paging == 1) {
+			if (p.y >= r.bottom) {
+				_nextRepeat = now + 100;
+				_value = MIN(_maxValue, _value + (_pageSize - 1));
+			}
+		}
+	}
+
+	if (_value != oldValue) {
+		Common::Rect r = getHandleRect(oldValue);
+		fill(r);
+		_window->markRectAsDirty(r);
+
+		r = getHandleRect(_value);
+		drawHandle(r);
+		_window->markRectAsDirty(r);
+	}
 }
 
 // ---------------------------------------------------------------------------
@@ -1020,9 +1279,6 @@ void MacGui::MacSlider::handleMouseMove(Common::Event &event) {
 // ---------------------------------------------------------------------------
 
 bool MacGui::MacPictureSlider::findWidget(int x, int y) const {
-	if (!_visible || !_enabled)
-		return false;
-
 	// Once we start dragging the handle, any mouse position is considered
 	// within the widget.
 
@@ -1208,7 +1464,7 @@ void MacGui::MacDialogWindow::clearFocusedWidget() {
 
 int MacGui::MacDialogWindow::findWidget(int x, int y) const {
 	for (uint i = 0; i < _widgets.size(); i++) {
-		if (_widgets[i]->findWidget(x, y))
+		if (_widgets[i]->isEnabled() && _widgets[i]->isVisible() && _widgets[i]->findWidget(x, y))
 			return i;
 	}
 
@@ -1336,8 +1592,10 @@ void MacGui::MacDialogWindow::undrawBeamCursor() {
 }
 
 void MacGui::MacDialogWindow::update(bool fullRedraw) {
-	for (uint i = 0; i < _widgets.size(); i++)
-		_widgets[i]->draw();
+	for (uint i = 0; i < _widgets.size(); i++) {
+		if (_widgets[i]->isVisible())
+			_widgets[i]->draw();
+	}
 
 	if (fullRedraw) {
 		_dirtyRects.clear();
@@ -1384,8 +1642,11 @@ int MacGui::MacDialogWindow::runDialog() {
 
 		for (uint i = 0; i < _widgets.size(); i++) {
 			_widgets[i]->setId(i);
-			_widgets[i]->setRedraw(true);
-			_widgets[i]->draw();
+
+			if (_widgets[i]->isVisible()) {
+				_widgets[i]->setRedraw(true);
+				_widgets[i]->draw();
+			}
 		}
 	}
 
@@ -1466,7 +1727,7 @@ int MacGui::MacDialogWindow::runDialog() {
 				// Handle default button
 				if (event.kbd.keycode == Common::KEYCODE_RETURN) {
 					MacWidget *widget = getDefaultWidget();
-					if (widget && widget->isEnabled()) {
+					if (widget && widget->isEnabled() && widget->isVisible()) {
 						for (int i = 0; i < 2; i++) {
 							widget->setRedraw();
 							widget->draw(i == 0);
@@ -2348,6 +2609,23 @@ MacGui::MacDialogWindow *MacGui::drawBanner(char *message) {
 	return window;
 }
 
+void MacGui::drawBitmap(Common::Rect r, const uint16 *bitmap, Color color) const {
+	drawBitmap(_surface, r, bitmap, color);
+}
+
+void MacGui::drawBitmap(Graphics::Surface *s, Common::Rect r, const uint16 *bitmap, Color color) const {
+	assert(r.width() <= 16);
+
+	for (int y = 0; y < r.height(); y++) {
+		uint16 bit = 0x8000;
+		for (int x = 0; x < r.width(); x++) {
+			if (bitmap[y] & bit)
+				s->setPixel(r.left + x, r.top + y, color);
+			bit >>= 1;
+		}
+	}
+}
+
 // ===========================================================================
 // The Mac Loom GUI. This one is pretty simple.
 // ===========================================================================
@@ -4853,21 +5131,4 @@ void MacIndy3Gui::fill(Common::Rect r) const {
 		_surface->fillRect(r, kLightGray);
 }
 
-void MacIndy3Gui::drawBitmap(Common::Rect r, const uint16 *bitmap, Color color) const {
-	byte *ptr = (byte *)_surface->getBasePtr(r.left, r.top);
-	int pitch = _surface->pitch;
-
-	assert(r.width() <= 16);
-
-	for (int y = 0; y < r.height(); y++) {
-		uint16 bit = 0x8000;
-		for (int x = 0; x < r.width(); x++) {
-			if (bitmap[y] & bit)
-				ptr[x] = color;
-			bit >>= 1;
-		}
-		ptr += pitch;
-	}
-}
-
 } // End of namespace Scumm
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index d72ec0a6ab4..f641361df29 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -140,6 +140,7 @@ public:
 	protected:
 		bool _redraw = false;
 		bool _enabled = false;
+		bool _visible = true;
 		Common::Rect _bounds;
 
 	public:
@@ -147,6 +148,8 @@ public:
 		virtual ~MacGuiObject() {}
 
 		Common::Rect getBounds() const { return _bounds; }
+		bool isEnabled() const { return _enabled; }
+		bool isVisible() const { return _visible; }
 	};
 
 	class MacWidget : public MacGuiObject {
@@ -154,13 +157,13 @@ public:
 		MacGui::MacDialogWindow *_window;
 		int _id = -1;
 
-		bool _visible = true;
 		bool _fullRedraw = false;
 
 		Common::String _text;
 		int _value = 0;
 
-		int drawText(Common::String text, int x, int y, int w, Color color, Graphics::TextAlign align = Graphics::kTextAlignLeft, int deltax = 0);
+		int drawText(Common::String text, int x, int y, int w, Color color, Graphics::TextAlign align = Graphics::kTextAlignLeft, int deltax = 0) const;
+		void drawBitmap(Common::Rect r, const uint16 *bitmap, Color color) const;
 
 	public:
 		MacWidget(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::String text, bool enabled);
@@ -172,14 +175,12 @@ public:
 		// Visibility never changes after initialization, so it does
 		// not trigger a redraw.
 		void setVisible(bool visible) { _visible = visible; }
-		bool isVisible() const { return _visible; }
 
 		virtual void getFocus() { setRedraw(); }
 		virtual void loseFocus() { setRedraw(); }
 
 		void setRedraw(bool fullRedraw = false);
 
-		bool isEnabled() const { return _enabled; }
 		void setEnabled(bool enabled);
 
 		virtual void setValue(int value);
@@ -295,19 +296,41 @@ public:
 
 	class MacSlider : public MacWidget {
 	private:
+		Common::Point _clickPos;
+		uint32 _nextRepeat;
+
 		int _minValue;
 		int _maxValue;
 		int _pageSize;
+		int _paging;
+
+		bool _upArrowPressed = false;
+		bool _downArrowPressed = false;
+		int _dragOffset = -1;
+
+		bool _redrawUpArrow = false;
+		bool _redrawDownArrow = false;
+		bool _redrawBody = false;
+
+		Common::Rect _boundsButtonUp;
+		Common::Rect _boundsButtonDown;
+		Common::Rect _boundsBody;
+
+		Common::Rect getHandleRect(int value);
+
+		void fill(Common::Rect r);
+		void drawHandle(Common::Rect r);
 
 	public:
-		MacSlider(MacGui::MacDialogWindow *window, Common::Rect bounds, int minValue, int maxValue, int pageSize, bool enabled)
-			: MacWidget(window, bounds, "Slider", enabled),
-			_minValue(minValue), _maxValue(maxValue), _pageSize(pageSize) {}
+		MacSlider(MacGui::MacDialogWindow *window, Common::Rect bounds, int minValue, int maxValue, int pageSize, bool enabled);
 
+		bool findWidget(int x, int y) const;
 		void draw(bool drawFocued = false);
 
 		void handleMouseDown(Common::Event &event);
+		void handleMouseUp(Common::Event &event);
 		void handleMouseMove(Common::Event &event);
+		void handleMouseHeld();
 	};
 
 	class MacPictureSlider : public MacWidget {
@@ -475,6 +498,9 @@ public:
 	MacDialogWindow *createWindow(Common::Rect bounds, MacDialogWindowStyle style = kStyleNormal);
 	MacDialogWindow *createDialog(int dialogId);
 	MacDialogWindow *drawBanner(char *message);
+
+	void drawBitmap(Common::Rect r, const uint16 *bitmap, Color color) const;
+	void drawBitmap(Graphics::Surface *s, Common::Rect r, const uint16 *bitmap, Color color) const;
 };
 
 class MacLoomGui : public MacGui {
@@ -628,7 +654,6 @@ private:
 
 	class VerbWidget : public Widget {
 	protected:
-		bool _visible = false;
 		int _verbid = 0;
 		int _verbslot = -1;
 		bool _kill = false;
@@ -638,7 +663,6 @@ private:
 
 		void setVerbid(int n) { _verbid = n; }
 		bool hasVerb() const { return _verbslot != -1; }
-		bool isVisible() const { return _visible; }
 		void threaten() { _kill = true; }
 		bool isDying() const { return _kill; }
 
@@ -757,7 +781,6 @@ private:
 	void hide();
 
 	void fill(Common::Rect r) const;
-	void drawBitmap(Common::Rect r, const uint16 *bitmap, Color color) const;
 
 	void markScreenAsDirty(Common::Rect r);
 	void copyDirtyRectsToScreen();


Commit: 5606ed8ef99bdede70cc7e7766111665df30b133
    https://github.com/scummvm/scummvm/commit/5606ed8ef99bdede70cc7e7766111665df30b133
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Clarify some rules regarding the Mac dialog window event loop

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 03d7b442c7c..62cfcd9f922 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -1635,6 +1635,9 @@ void MacGui::MacDialogWindow::fillPattern(Common::Rect r, uint16 pattern) {
 }
 
 int MacGui::MacDialogWindow::runDialog() {
+	// The first time the function is called, show the dialog and redraw
+	// all widgets completely.
+
 	if (!_visible) {
 		show();
 
@@ -1662,6 +1665,8 @@ int MacGui::MacDialogWindow::runDialog() {
 		int widgetId = -1;
 
 		while (_system->getEventManager()->pollEvent(event)) {
+			// Adjust mouse coordinates to the dialog window
+
 			if (Common::isMouseEvent(event)) {
 				_realMousePos.x = event.mouse.x;
 				_realMousePos.y = event.mouse.y;
@@ -1677,6 +1682,17 @@ int MacGui::MacDialogWindow::runDialog() {
 
 			switch (event.type) {
 			case Common::EVENT_LBUTTONDOWN:
+				// When a widget is clicked, it becomes the
+				// focused widget. Focused widgets are often
+				// indicated by some sort of highlight, e.g.
+				// buttons become inverted.
+				//
+				// This highlight is usually only shown while
+				// the mouse is within the widget bounds, but
+				// as long as it remains focused it can regain
+				// the highlight by moving the cursor back into
+				// the widget bounds again.
+
 				buttonPressed = true;
 				nextMouseRepeat = _system->getMillis() + 40;
 				setFocusedWidget(event.mouse.x, event.mouse.y);
@@ -1688,31 +1704,53 @@ int MacGui::MacDialogWindow::runDialog() {
 				buttonPressed = false;
 				updateCursor();
 
+				// Only the focused widget receives the button
+				// up event. If the widget handles the event,
+				// control is passed back to the caller of
+				// runDialog() so that it can react, e.g. to
+				// the user clicking the "Okay" button.
+
 				if (_focusedWidget) {
 					MacWidget *widget = _focusedWidget;
 
-					clearFocusedWidget();
 					updateCursor();
 
 					if (widget->findWidget(event.mouse.x, event.mouse.y)) {
+						clearFocusedWidget();
 						widgetId = widget->getId();
 						widget->handleMouseUp(event);
 						return widgetId;
 					}
+
+					clearFocusedWidget();
 				}
 
 				break;
 
 			case Common::EVENT_MOUSEMOVE:
-				if (_beamCursor && !_beamCursorVisible)
+				// The "beam" cursor can be hidden, but will
+				// become visible again when the user moves
+				// the mouse.
+
+				if (_beamCursor)
 					_beamCursorVisible = true;
 
+				// Only the focused widget receives mouse move
+				// events, and then only if the mouse is within
+				// the widget's area of control. This are of
+				// control is usually the widget bounds, but
+				// widgets can redefine findWidget() to change
+				// this, e.g. for slider widgets.
+
 				if (_focusedWidget) {
-					if (_focusedWidget->findWidget(_oldMousePos.x, _oldMousePos.y) != _focusedWidget->findWidget(_mousePos.x, _mousePos.y)) {
+					bool wasActive = _focusedWidget->findWidget(_oldMousePos.x, _oldMousePos.y);
+					bool isActive = _focusedWidget->findWidget(_mousePos.x, _mousePos.y);
+
+					if (wasActive != isActive)
 						_focusedWidget->setRedraw();
-					}
 
-					_focusedWidget->handleMouseMove(event);
+					if (isActive)
+						_focusedWidget->handleMouseMove(event);
 				} else {
 					updateCursor();
 				}
@@ -1744,9 +1782,15 @@ int MacGui::MacDialogWindow::runDialog() {
 				}
 
 				// Otherwise, give widgets a chance to react
-				// to key presses.
+				// to key presses. All widgets get a chance,
+				// whether or not they are focused. This may
+				// be a bad idea, if there is ever more than
+				// one edit text widget in the window.
+				//
+				// Typing hides the "beam" cursor.
+
 				for (uint i = 0; i < _widgets.size(); i++) {
-					if (_widgets[i]->handleKeyDown(event)) {
+					if (_widgets[i]->isVisible() && _widgets[i]->isEnabled() && _widgets[i]->handleKeyDown(event)) {
 						if (_beamCursor) {
 							_beamCursorVisible = false;
 							undrawBeamCursor();
@@ -1762,6 +1806,11 @@ int MacGui::MacDialogWindow::runDialog() {
 			}
 		}
 
+		// A focused widget implies that the mouse button is being
+		// held down. It must be active and visible, so it can receive
+		// mouse repeat events, e.g. for holding down scroll buttons
+		// on a slider widget.
+
 		if (_focusedWidget && _system->getMillis() > nextMouseRepeat) {
 			nextMouseRepeat = _system->getMillis() + 40;
 			_focusedWidget->handleMouseHeld();


Commit: d7b05633ad6f1d0d5d63900fa8f146df56e060b0
    https://github.com/scummvm/scummvm/commit/d7b05633ad6f1d0d5d63900fa8f146df56e060b0
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Fix Mac slider widget scroll button highlighting

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 62cfcd9f922..d5a6a435535 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -1027,7 +1027,7 @@ bool MacGui::MacSlider::findWidget(int x, int y) const {
 
 	Common::Rect bounds = _bounds;
 
-	if (_dragOffset) {
+	if (_dragOffset >= 0) {
 		bounds.left -= 25;
 		bounds.right += 25;
 	}
@@ -1187,10 +1187,23 @@ void MacGui::MacSlider::handleMouseMove(Common::Event &event) {
 	int x = event.mouse.x;
 	int y = event.mouse.y;
 
-	if (!findWidget(x, y))
+	if (!findWidget(x, y)) {
+		if (_upArrowPressed) {
+			_upArrowPressed = false;
+			_redrawUpArrow = true;
+			setRedraw();
+		}
+
+		if (_downArrowPressed) {
+			_downArrowPressed = false;
+			_redrawDownArrow = true;
+			setRedraw();
+		}
+
 		return;
+	}
 
-	if (_dragOffset) {
+	if (_dragOffset >= 0) {
 		_redrawBody = true;
 		setRedraw();
 	} else {
@@ -1749,7 +1762,10 @@ int MacGui::MacDialogWindow::runDialog() {
 					if (wasActive != isActive)
 						_focusedWidget->setRedraw();
 
-					if (isActive)
+					// The widget gets mouse events while it's active, but also
+					// one last one when it becomes inactive.
+
+					if (isActive || wasActive)
 						_focusedWidget->handleMouseMove(event);
 				} else {
 					updateCursor();


Commit: 9625471842d86a9ac0acf4e9b3c5943bcbdfff5f
    https://github.com/scummvm/scummvm/commit/9625471842d86a9ac0acf4e9b3c5943bcbdfff5f
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Clarify Mac scroll widget "paging" a bit

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index d5a6a435535..c8f0d964ca8 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -1261,13 +1261,16 @@ void MacGui::MacSlider::handleMouseHeld() {
 	if (_paging) {
 		Common::Rect r = getHandleRect(_value);
 
+		// Keep paging until at least half the scroll handle has gone past the
+		// mouse cursor. This may have to be tuned.
+
 		if (_paging == -1) {
-			if (p.y < r.top) {
+			if (p.y < r.top + r.height() / 2 && _value > _minValue) {
 				_nextRepeat = now + 100;
 				_value = MAX(_minValue, _value - (_pageSize - 1));
 			}
 		} else if (_paging == 1) {
-			if (p.y >= r.bottom) {
+			if (p.y >= r.bottom - r.height() / 2 && _value < _maxValue) {
 				_nextRepeat = now + 100;
 				_value = MIN(_maxValue, _value + (_pageSize - 1));
 			}


Commit: 16056a889f6f70d1796144bc8df0c312bf023375
    https://github.com/scummvm/scummvm/commit/16056a889f6f70d1796144bc8df0c312bf023375
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Implement Mac scroll widget handle dragging

I still have to implement letting go of the handle, but there's no time
for that right now.

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index c8f0d964ca8..aba407b45c8 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -998,7 +998,7 @@ Common::Rect MacGui::MacSlider::getHandleRect(int value) {
 	return handleRect;
 }
 
-void MacGui::MacSlider::fill(Common::Rect r) {
+void MacGui::MacSlider::fill(Common::Rect r, bool inverted) {
 	Color pattern[2][4] = {
 		{ kBlack, kWhite, kWhite, kWhite },
 		{ kWhite, kWhite, kBlack, kWhite }
@@ -1008,7 +1008,19 @@ void MacGui::MacSlider::fill(Common::Rect r) {
 
 	for (int y = r.top; y < r.bottom; y++) {
 		for (int x = r.left; x < r.right; x++) {
-			s->setPixel(x, y, pattern[y % 2][x % 4]);
+			if (inverted) {
+				// The inverted style is used for drawing the "ghost" of the
+				// slider handle while dragging. I think this matches the
+				// original behavior, though I'm not quite sure.
+
+				bool srcPixel = s->getPixel(x, y) == kBlack;
+				bool dstPixel = pattern[y % 2][x % 4] == kWhite;
+
+				Color color = (srcPixel ^ dstPixel) ? kBlack : kWhite;
+
+				s->setPixel(x, y, color);
+			} else
+				s->setPixel(x, y, pattern[y % 2][x % 4]);
 		}
 	}
 }
@@ -1126,6 +1138,7 @@ void MacGui::MacSlider::handleMouseDown(Common::Event &event) {
 	_clickPos.y = y;
 	_paging = 0;
 	_dragOffset = -1;
+	_dragPos = -1;
 
 	int oldValue = _value;
 
@@ -1154,6 +1167,7 @@ void MacGui::MacSlider::handleMouseDown(Common::Event &event) {
 			_value = MIN(_maxValue, _value + (_pageSize - 1));
 		} else {
 			_dragOffset = y - handleRect.top;
+			_dragPos = handleRect.top;
 		}
 	}
 
@@ -1179,6 +1193,7 @@ void MacGui::MacSlider::handleMouseUp(Common::Event &event) {
 
 	_paging = 0;
 	_dragOffset = -1;
+	_dragPos = -1;
 	_clickPos.x = -1;
 	_clickPos.y = -1;
 }
@@ -1204,8 +1219,39 @@ void MacGui::MacSlider::handleMouseMove(Common::Event &event) {
 	}
 
 	if (_dragOffset >= 0) {
-		_redrawBody = true;
-		setRedraw();
+		Common::Rect r;
+
+		r.left = _boundsBody.left + 1;
+		r.top = _dragPos;
+		r.right = _boundsBody.right - 1;
+		r.bottom = _dragPos + 16;
+
+		fill(r);
+		_window->markRectAsDirty(r);
+
+		Common::Rect handleRect = getHandleRect(_value);
+
+		if (r.intersects(handleRect)) {
+			drawHandle(handleRect);
+			_window->markRectAsDirty(handleRect);
+		}
+
+		int dragPos = CLIP<int>(y - _dragOffset, _boundsBody.top, _boundsBody.bottom - 16);
+		_dragPos = dragPos;
+
+		r.moveTo(_boundsBody.left + 1, dragPos);
+
+		// Drawing a solid rectangle would be easier, and probably look better.
+		// But it seems the orginal Mac widget would draw the frame as an
+		// inverted slider background, even when drawing it on top of the
+		// slider handle.
+
+		fill(Common::Rect(r.left, r.top, r.right, r.top + 1), true);
+		fill(Common::Rect(r.left, r.bottom - 1, r.right, r.bottom), true);
+		fill(Common::Rect(r.left, r.top + 1, r.left + 1, r.bottom - 1), true);
+		fill(Common::Rect(r.right - 1, r.top + 1, r.right, r.bottom - 1), true);
+
+		_window->markRectAsDirty(r);
 	} else {
 		if (!_boundsButtonUp.contains(x, y)) {
 			if (_upArrowPressed) {
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index f641361df29..a1e0266459b 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -307,6 +307,7 @@ public:
 		bool _upArrowPressed = false;
 		bool _downArrowPressed = false;
 		int _dragOffset = -1;
+		int _dragPos = -1;
 
 		bool _redrawUpArrow = false;
 		bool _redrawDownArrow = false;
@@ -318,7 +319,7 @@ public:
 
 		Common::Rect getHandleRect(int value);
 
-		void fill(Common::Rect r);
+		void fill(Common::Rect r, bool inverted = false);
 		void drawHandle(Common::Rect r);
 
 	public:


Commit: 8c10e5160d17679cec02865d69d9161a34a89084
    https://github.com/scummvm/scummvm/commit/8c10e5160d17679cec02865d69d9161a34a89084
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Implement Mac scrollbar dragging

This also does some unification of MacSlider and MacPictureSlider.

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index aba407b45c8..b5f034a7571 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -970,21 +970,67 @@ void MacGui::MacPicture::draw(bool drawFocused) {
 	_fullRedraw = false;
 }
 
+// ---------------------------------------------------------------------------
+// Slider base class
+// ---------------------------------------------------------------------------
+
+void MacGui::MacSliderBase::setValue(int value) {
+	_value = CLIP(value, _minValue, _maxValue);
+	_handlePos = calculatePosFromValue();
+}
+
+int MacGui::MacSliderBase::calculateValueFromPos() const {
+	int posRange = _maxPos - _minPos;
+	int posOffset = _handlePos - _minPos;
+
+	int valueRange = _maxValue - _minValue;
+	int valueOffset = (posRange / 2 + valueRange * posOffset) / posRange;
+
+	return _minValue + valueOffset;
+}
+
+int MacGui::MacSliderBase::calculatePosFromValue() const {
+	int valueRange = _maxValue - _minValue;
+	int valueOffset = _value - _minValue;
+
+	int posRange = _maxPos - _minPos;
+	int posOffset = (valueRange / 2 + posRange * valueOffset) / valueRange;
+
+	return _minPos + posOffset;
+}
+
 // ---------------------------------------------------------------------------
 // Standard slider widget
 // ---------------------------------------------------------------------------
 
 MacGui::MacSlider::MacSlider(MacGui::MacDialogWindow *window, Common::Rect bounds, int minValue, int maxValue, int pageSize, bool enabled)
-	: MacWidget(window, bounds, "Slider", enabled),
-	_minValue(minValue), _maxValue(maxValue), _pageSize(pageSize) {
+	: MacSliderBase(window, bounds, minValue, maxValue, 0, 0, enabled),
+	_pageSize(pageSize) {
 	_boundsButtonUp = Common::Rect(_bounds.left, _bounds.top, _bounds.right, _bounds.top + 16);
 	_boundsButtonDown = Common::Rect(_bounds.left, _bounds.bottom - 16, _bounds.right, _bounds.bottom);
 	_boundsBody = Common::Rect(_bounds.left, _bounds.top + 16, _bounds.right, _bounds.bottom - 16);
 
+	_minPos = _boundsBody.top;
+	_maxPos = _boundsBody.bottom - 16;
+
 	_clickPos.x = -1;
 	_clickPos.y = -1;
 }
 
+bool MacGui::MacSlider::findWidget(int x, int y) const {
+	if (_maxValue - _minValue <= _pageSize)
+		return false;
+
+	Common::Rect bounds = _bounds;
+
+	if (_grabOffset >= 0) {
+		bounds.left -= 25;
+		bounds.right += 25;
+	}
+
+	return bounds.contains(x, y);
+}
+
 Common::Rect MacGui::MacSlider::getHandleRect(int value) {
 	int handlePos = value * (_boundsBody.bottom - _boundsBody.top - 16) / (_maxValue - _minValue);
 
@@ -1025,109 +1071,110 @@ void MacGui::MacSlider::fill(Common::Rect r, bool inverted) {
 	}
 }
 
-void MacGui::MacSlider::drawHandle(Common::Rect r) {
-	Graphics::Surface *s = _window->innerSurface();
-
-	s->frameRect(r, kBlack);
-	r.grow(-1);
-	s->fillRect(r, kWhite);
-}
-
-bool MacGui::MacSlider::findWidget(int x, int y) const {
-	if (_maxValue - _minValue <= _pageSize)
-		return false;
-
-	Common::Rect bounds = _bounds;
-
-	if (_dragOffset >= 0) {
-		bounds.left -= 25;
-		bounds.right += 25;
-	}
-
-	return bounds.contains(x, y);
-}
-
 void MacGui::MacSlider::draw(bool drawFocused) {
 	if (!_redraw && !_fullRedraw)
 		return;
 
-	debug(1, "MacGui::MacSlider: Drawing slider (_fullRedraw = %d, drawFocused = %d, _value = %d)", _fullRedraw, drawFocused, _value);
-
-	Graphics::Surface *s = _window->innerSurface();
+	// There are several things that will trigger a redraw, but unlike
+	// other widgets this one only handles full redraws. Everything else
+	// is handled outside of draw().
 
 	if (_fullRedraw) {
+		debug(1, "MacGui::MacSlider: Drawing slider (_fullRedraw = %d, drawFocused = %d, _value = %d)", _fullRedraw, drawFocused, _value);
+
+		Graphics::Surface *s = _window->innerSurface();
+
 		s->frameRect(_bounds, kBlack);
 		s->hLine(_bounds.left + 1, _bounds.top + 15, _bounds.right - 2, kBlack);
 		s->hLine(_bounds.left + 1, _bounds.bottom - 16, _bounds.right - 2, kBlack);
-	}
 
-	if (_fullRedraw || _redrawBody) {
+		drawUpArrow(false);
+		drawDownArrow(false);
+
 		Common::Rect fillRect(_boundsBody.left + 1, _boundsBody.top, _boundsBody.right - 1, _boundsBody.bottom);
 
-		if (_maxValue - _minValue > _pageSize)
+		if (_maxValue - _minValue > _pageSize) {
 			fill(fillRect);
-		else
-			s->fillRect(fillRect, kWhite);
 
-		Common::Rect handleRect = getHandleRect(_value);
-		drawHandle(handleRect);
+			Common::Rect handleRect = getHandleRect(_value);
+			drawHandle(handleRect);
+		} else
+			s->fillRect(fillRect, kWhite);
 
-		if (!_fullRedraw)
-			_window->markRectAsDirty(_boundsBody);
+		_window->markRectAsDirty(_bounds);
 	}
 
-	// There is a narrower version of these arrows, but I'm speculating
-	// that they're intended for a 9" Mac screen.
+	_redraw = false;
+	_fullRedraw = false;
+}
 
-	if (_fullRedraw || _redrawUpArrow) {
-		const uint16 upArrow[] = {
-			0x0600, 0x0900, 0x1080, 0x2040, 0x4020,
-			0xF0F0, 0x1080, 0x1080, 0x1080, 0x1F80
-		};
+// There is a narrower version of these arrows, but I'm speculating that
+// they're intended for a 9" Mac screen. We go with the slightly wider version
+// here to make them easier to hit.
 
-		const uint16 upArrowFilled[] = {
-			0x0600, 0x0F00, 0x1F80, 0x3FC0, 0x7FE0,
-			0xFFF0, 0x1F80, 0x1F80, 0x1F80, 0x1F80
-		};
+void MacGui::MacSlider::drawUpArrow(bool markAsDirty) {
+	debug(1, "MacGui::MacSlider: Drawing up arrow (_upArrowPressed = %d, markAsDirty = %d)", _upArrowPressed, markAsDirty);
 
-		Common::Rect r = _boundsButtonUp;
-		r.grow(-1);
+	const uint16 upArrow[] = {
+		0x0600, 0x0900, 0x1080, 0x2040, 0x4020,
+		0xF0F0, 0x1080, 0x1080, 0x1080, 0x1F80
+	};
 
-		s->fillRect(r, kWhite);
-		drawBitmap(Common::Rect(r.left + 1, r.top + 2, r.right - 1, r.top + 12), _upArrowPressed ? upArrowFilled : upArrow, kBlack);
-		_redrawUpArrow = false;
+	const uint16 upArrowFilled[] = {
+		0x0600, 0x0F00, 0x1F80, 0x3FC0, 0x7FE0,
+		0xFFF0, 0x1F80, 0x1F80, 0x1F80, 0x1F80
+	};
 
-		if (!_fullRedraw)
-			_window->markRectAsDirty(r);
-	}
+	drawArrow(_boundsButtonUp, (_upArrowPressed ? upArrowFilled : upArrow), markAsDirty);
+}
 
-	if (_fullRedraw || _redrawDownArrow) {
-		const uint16 downArrow[] = {
-			0x1F80,	0x1080,	0x1080,	0x1080,	0xF0F0,
-			0x4020,	0x2040,	0x1080,	0x0900,	0x0600
-		};
+void MacGui::MacSlider::drawDownArrow(bool markAsDirty) {
+	debug(1, "MacGui::MacSlider: Drawing down arrow (_downArrowPressed = %d, markAsDirty = %d)", _downArrowPressed, markAsDirty);
 
-		const uint16 downArrowFilled[] = {
-			0x1F80, 0x1F80, 0x1F80, 0x1F80, 0xFFF0,
-			0x7FE0, 0x3FC0, 0x1F80, 0x0F00, 0x0600
-		};
+	const uint16 downArrow[] = {
+		0x1F80,	0x1080,	0x1080,	0x1080,	0xF0F0,
+		0x4020,	0x2040,	0x1080,	0x0900,	0x0600
+	};
 
-		Common::Rect r = _boundsButtonDown;
-		r.grow(-1);
+	const uint16 downArrowFilled[] = {
+		0x1F80, 0x1F80, 0x1F80, 0x1F80, 0xFFF0,
+		0x7FE0, 0x3FC0, 0x1F80, 0x0F00, 0x0600
+	};
 
-		s->fillRect(r, kWhite);
-		drawBitmap(Common::Rect(r.left + 1, r.top + 2, r.right - 1, r.top + 12), _downArrowPressed ? downArrowFilled : downArrow, kBlack);
-		_redrawDownArrow = false;
+	drawArrow(_boundsButtonDown, (_downArrowPressed ? downArrowFilled : downArrow), markAsDirty);
+}
 
-		if (!_fullRedraw)
-			_window->markRectAsDirty(r);
-	}
+void MacGui::MacSlider::drawArrow(Common::Rect r, const uint16 *bitmap, bool markAsDirty) {
+	Graphics::Surface *s = _window->innerSurface();
 
-	if (_fullRedraw)
-		_window->markRectAsDirty(_bounds);
+	r.grow(-1);
 
-	_redraw = false;
-	_fullRedraw = false;
+	s->fillRect(r, kWhite);
+	drawBitmap(Common::Rect(r.left + 1, r.top + 2, r.right - 1, r.top + 12), bitmap, kBlack);
+
+	if (markAsDirty)
+		_window->markRectAsDirty(r);
+}
+
+void MacGui::MacSlider::drawHandle(Common::Rect r) {
+	debug(2, "MacGui::MacSlider::drawHandle(%d)", r.top);
+
+	Graphics::Surface *s = _window->innerSurface();
+
+	s->frameRect(r, kBlack);
+	r.grow(-1);
+	s->fillRect(r, kWhite);
+}
+
+void MacGui::MacSlider::redrawHandle(int oldValue, int newValue) {
+	Common::Rect r = getHandleRect(oldValue);
+
+	fill(r);
+	_window->markRectAsDirty(r);
+
+	r = getHandleRect(newValue);
+	drawHandle(r);
+	_window->markRectAsDirty(r);
 }
 
 void MacGui::MacSlider::handleMouseDown(Common::Event &event) {
@@ -1137,23 +1184,21 @@ void MacGui::MacSlider::handleMouseDown(Common::Event &event) {
 	_clickPos.x = x;
 	_clickPos.y = y;
 	_paging = 0;
-	_dragOffset = -1;
-	_dragPos = -1;
+	_grabOffset = -1;
+	_handlePos = -1;
 
 	int oldValue = _value;
 
 	if (_boundsButtonUp.contains(x, y)) {
 		_nextRepeat = _window->_system->getMillis() + 200;
 		_upArrowPressed = true;
-		_redrawUpArrow = true;
 		_value = MAX(_minValue, _value - 1);
-		setRedraw();
+		drawUpArrow(true);
 	} else if (_boundsButtonDown.contains(x, y)) {
 		_nextRepeat = _window->_system->getMillis() + 200;
 		_downArrowPressed = true;
-		_redrawDownArrow = true;
 		_value = MIN(_maxValue, _value + 1);
-		setRedraw();
+		drawDownArrow(true);
 	} else {
 		Common::Rect handleRect = getHandleRect(_value);
 
@@ -1166,34 +1211,39 @@ void MacGui::MacSlider::handleMouseDown(Common::Event &event) {
 			_paging = 1;
 			_value = MIN(_maxValue, _value + (_pageSize - 1));
 		} else {
-			_dragOffset = y - handleRect.top;
-			_dragPos = handleRect.top;
+			_grabOffset = y - handleRect.top;
+			_handlePos = handleRect.top;
 		}
 	}
 
-	if (_value != oldValue) {
-		_redrawBody = true;
-		setRedraw();
-	}
+	if (_value != oldValue)
+		redrawHandle(oldValue, _value);
 }
 
 void MacGui::MacSlider::handleMouseUp(Common::Event &event) {
 	if (_upArrowPressed) {
 		_upArrowPressed = false;
-		_redrawUpArrow = true;
-		setRedraw();
+		drawUpArrow(true);
 	} else if (_downArrowPressed) {
 		_downArrowPressed = false;
-		_redrawDownArrow = true;
-		setRedraw();
-	} else if (_dragOffset >= 0) {
-		_redrawBody = true;
-		setRedraw();
+		drawDownArrow(true);
+	} else if (_grabOffset >= 0) {
+		// Erase the drag rect, since the handle might not end up in
+		// the exact same spot.
+		Common::Rect r(_boundsBody.left + 1, _handlePos, _boundsBody.right - 1, _handlePos + 16);
+		fill(r);
+		_window->markRectAsDirty(r);
+
+		// Calculate new value and move the handle there
+		int newValue = calculateValueFromPos();
+
+		redrawHandle(_value, newValue);
+		_value = newValue;
 	}
 
 	_paging = 0;
-	_dragOffset = -1;
-	_dragPos = -1;
+	_grabOffset = -1;
+	_handlePos = -1;
 	_clickPos.x = -1;
 	_clickPos.y = -1;
 }
@@ -1205,26 +1255,24 @@ void MacGui::MacSlider::handleMouseMove(Common::Event &event) {
 	if (!findWidget(x, y)) {
 		if (_upArrowPressed) {
 			_upArrowPressed = false;
-			_redrawUpArrow = true;
-			setRedraw();
+			drawUpArrow(true);
 		}
 
 		if (_downArrowPressed) {
 			_downArrowPressed = false;
-			_redrawDownArrow = true;
-			setRedraw();
+			drawDownArrow(true);
 		}
 
 		return;
 	}
 
-	if (_dragOffset >= 0) {
+	if (_grabOffset >= 0) {
 		Common::Rect r;
 
 		r.left = _boundsBody.left + 1;
-		r.top = _dragPos;
+		r.top = _handlePos;
 		r.right = _boundsBody.right - 1;
-		r.bottom = _dragPos + 16;
+		r.bottom = _handlePos + 16;
 
 		fill(r);
 		_window->markRectAsDirty(r);
@@ -1236,15 +1284,14 @@ void MacGui::MacSlider::handleMouseMove(Common::Event &event) {
 			_window->markRectAsDirty(handleRect);
 		}
 
-		int dragPos = CLIP<int>(y - _dragOffset, _boundsBody.top, _boundsBody.bottom - 16);
-		_dragPos = dragPos;
+		_handlePos = CLIP<int>(y - _grabOffset, _boundsBody.top, _boundsBody.bottom - 16);
 
-		r.moveTo(_boundsBody.left + 1, dragPos);
+		r.moveTo(_boundsBody.left + 1, _handlePos);
 
-		// Drawing a solid rectangle would be easier, and probably look better.
-		// But it seems the orginal Mac widget would draw the frame as an
-		// inverted slider background, even when drawing it on top of the
-		// slider handle.
+		// Drawing a solid rectangle would be easier, and probably look
+		// better. But it seems the orginal Mac widget would draw the
+		// frame as an inverted slider background, even when drawing it
+		// on top of the slider handle.
 
 		fill(Common::Rect(r.left, r.top, r.right, r.top + 1), true);
 		fill(Common::Rect(r.left, r.bottom - 1, r.right, r.bottom), true);
@@ -1256,30 +1303,26 @@ void MacGui::MacSlider::handleMouseMove(Common::Event &event) {
 		if (!_boundsButtonUp.contains(x, y)) {
 			if (_upArrowPressed) {
 				_upArrowPressed = false;
-				_redrawUpArrow = true;
-				setRedraw();
+				drawUpArrow(true);
 			}
 		} else {
 			if (_boundsButtonUp.contains(_clickPos) && !_upArrowPressed) {
 				_nextRepeat = _window->_system->getMillis() + 200;
 				_upArrowPressed = true;
-				_redrawUpArrow = true;
-				setRedraw();
+				drawUpArrow(true);
 			}
 		}
 
 		if (!_boundsButtonDown.contains(x, y)) {
 			if (_downArrowPressed) {
 				_downArrowPressed = false;
-				_redrawDownArrow = true;
-				setRedraw();
+				drawDownArrow(true);
 			}
 		} else {
 			if (_boundsButtonDown.contains(_clickPos) && !_downArrowPressed) {
 				_nextRepeat = _window->_system->getMillis() + 200;
 				_downArrowPressed = true;
-				_redrawDownArrow = true;
-				setRedraw();
+				drawDownArrow(true);
 			}
 		}
 	}
@@ -1323,15 +1366,26 @@ void MacGui::MacSlider::handleMouseHeld() {
 		}
 	}
 
-	if (_value != oldValue) {
-		Common::Rect r = getHandleRect(oldValue);
-		fill(r);
-		_window->markRectAsDirty(r);
+	if (_value != oldValue)
+		redrawHandle(oldValue, _value);
+}
 
-		r = getHandleRect(_value);
-		drawHandle(r);
-		_window->markRectAsDirty(r);
-	}
+void MacGui::MacSlider::handleWheelUp() {
+	int oldValue = _value;
+
+	_value = MAX(_minValue, _value - (_pageSize - 1));
+
+	if (_value != oldValue)
+		redrawHandle(oldValue, _value);
+}
+
+void MacGui::MacSlider::handleWheelDown() {
+	int oldValue = _value;
+
+	_value = MIN(_maxValue, _value + (_pageSize - 1));
+
+	if (_value != oldValue)
+		redrawHandle(oldValue, _value);
 }
 
 // ---------------------------------------------------------------------------
@@ -1350,17 +1404,22 @@ bool MacGui::MacPictureSlider::findWidget(int x, int y) const {
 	return _bounds.contains(x, y);
 }
 
-void MacGui::MacPictureSlider::setValue(int value) {
-	MacWidget::setValue(CLIP(value, _minValue, _maxValue));
+void MacGui::MacPictureSlider::eraseHandle() {
+	Common::Rect r = _handle->getBounds();
+	int y = r.top - _bounds.top;
+	int w = r.width();
+	int h = r.height();
 
-	int valueRange = _maxValue - _minValue;
-	int valueOffset = _value - _minValue;
+	Graphics::Surface *background = _background->getPicture();
+	Graphics::Surface sprite = background->getSubArea(Common::Rect(_handlePos, y, _handlePos + w, y + h));
+	_window->drawSprite(&sprite, _bounds.left + _handlePos, r.top);
+}
 
-	int posRange = (_maxX - _rightMargin) - (_minX + _leftMargin);
-	int posOffset = (valueRange / 2 + posRange * valueOffset) / valueRange;
+void MacGui::MacPictureSlider::drawHandle() {
+	Graphics::Surface *sprite = _handle->getPicture();
+	Common::Rect r = _handle->getBounds();
 
-	_handleX = _minX + _leftMargin + posOffset;
-	setRedraw();
+	_window->drawSprite(sprite, _bounds.left + _handlePos, r.top);
 }
 
 void MacGui::MacPictureSlider::draw(bool drawFocused) {
@@ -1369,23 +1428,11 @@ void MacGui::MacPictureSlider::draw(bool drawFocused) {
 
 	debug(1, "MacGui::MacPictureSlider: Drawing slider %d (_fullRedraw = %d, drawFocused = %d, _value = %d)", _id, _fullRedraw, drawFocused, _value);
 
-	Graphics::Surface *bgSprite = _background->getPicture();
-	Graphics::Surface *hSprite = _handle->getPicture();
-
-	if (_fullRedraw)
-		_window->drawSprite(bgSprite, _bounds.left, _bounds.top);
-
-	int handleY = _handle->getBounds().top - _bounds.top;
-
-	if (_lastHandleX != -1 && !_fullRedraw) {
-		Graphics::Surface bg = bgSprite->getSubArea(Common::Rect(_lastHandleX, handleY, _lastHandleX + hSprite->w, handleY + hSprite->h));
-
-		_window->drawSprite(&bg, _bounds.left + _lastHandleX, _bounds.top + handleY);
+	if (_fullRedraw) {
+		_window->drawSprite(_background->getPicture(), _bounds.left, _bounds.top);
+		drawHandle();
 	}
 
-	_window->drawSprite(hSprite, _bounds.left + _handleX, _bounds.top + handleY);
-	_lastHandleX = _handleX;
-
 	_redraw = false;
 	_fullRedraw = false;
 }
@@ -1394,8 +1441,8 @@ void MacGui::MacPictureSlider::handleMouseDown(Common::Event &event) {
 	int mouseX = event.mouse.x;
 	int handleWidth = _handle->getBounds().width();
 
-	if (mouseX >= _handleX && mouseX < _handleX + handleWidth)
-		_grabOffset = event.mouse.x - _bounds.left - _handleX;
+	if (mouseX >= _handlePos && mouseX < _handlePos + handleWidth)
+		_grabOffset = event.mouse.x - _bounds.left - _handlePos;
 	else
 		_grabOffset = handleWidth / 2;
 
@@ -1403,18 +1450,45 @@ void MacGui::MacPictureSlider::handleMouseDown(Common::Event &event) {
 }
 
 void MacGui::MacPictureSlider::handleMouseUp(Common::Event &event) {
-	int posRange = (_maxX - _rightMargin) - (_minX + _rightMargin);
-	int posOffset = _handleX - (_minX + _leftMargin);
+	// Erase the drag rect, since the handle might not end up in
+	// the exact same spot.
+	int newValue = calculateValueFromPos();
 
-	int valueRange = _maxValue - _minValue;
-	int valueOffset = (posRange / 2 + valueRange * posOffset) / posRange;
-
-	setValue(_minValue + valueOffset);
+	if (newValue != _value) {
+		eraseHandle();
+		setValue(newValue);
+		drawHandle();
+	}
 }
 
 void MacGui::MacPictureSlider::handleMouseMove(Common::Event &event) {
-	_handleX = CLIP<int>(event.mouse.x - _bounds.left - _grabOffset, _minX, _maxX);
-	setRedraw();
+	int newPos = CLIP<int>(event.mouse.x - _bounds.left - _grabOffset, _minX, _maxX);
+
+	if (newPos != _handlePos) {
+		eraseHandle();
+		_handlePos = newPos;
+		drawHandle();
+	}
+}
+
+void MacGui::MacPictureSlider::handleWheelUp() {
+	int newValue = MAX(_minValue, _value + 1);
+
+	if (_value != newValue) {
+		eraseHandle();
+		setValue(newValue);
+		drawHandle();
+	}
+}
+
+void MacGui::MacPictureSlider::handleWheelDown() {
+	int newValue = MIN(_maxValue, _value - 1);
+
+	if (_value != newValue) {
+		eraseHandle();
+		setValue(newValue);
+		drawHandle();
+	}
 }
 
 // ---------------------------------------------------------------------------
@@ -1742,6 +1816,8 @@ int MacGui::MacDialogWindow::runDialog() {
 				_mousePos.y = event.mouse.y;
 			}
 
+			int w;
+
 			switch (event.type) {
 			case Common::EVENT_LBUTTONDOWN:
 				// When a widget is clicked, it becomes the
@@ -1811,8 +1887,9 @@ int MacGui::MacDialogWindow::runDialog() {
 					if (wasActive != isActive)
 						_focusedWidget->setRedraw();
 
-					// The widget gets mouse events while it's active, but also
-					// one last one when it becomes inactive.
+					// The widget gets mouse events while
+					// it's active, but also one last one
+					// when it becomes inactive.
 
 					if (isActive || wasActive)
 						_focusedWidget->handleMouseMove(event);
@@ -1822,6 +1899,26 @@ int MacGui::MacDialogWindow::runDialog() {
 
 				break;
 
+			case Common::EVENT_WHEELUP:
+				if (!_gui->_vm->_enableEnhancements || _focusedWidget)
+					break;
+
+				w = findWidget(event.mouse.x, event.mouse.y);
+				if (w >= 0)
+					_widgets[w]->handleWheelUp();
+
+				break;
+
+			case Common::EVENT_WHEELDOWN:
+				if (!_gui->_vm->_enableEnhancements || _focusedWidget)
+					break;
+
+				w = findWidget(event.mouse.x, event.mouse.y);
+				if (w >= 0)
+					_widgets[w]->handleWheelDown();
+
+				break;
+
 			case Common::EVENT_KEYDOWN:
 				// Ignore keyboard while mouse is pressed
 				if (buttonPressed)
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index a1e0266459b..2051d01b552 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -195,6 +195,8 @@ public:
 		virtual void handleMouseUp(Common::Event &event) {}
 		virtual void handleMouseMove(Common::Event &event) {}
 		virtual void handleMouseHeld() {}
+		virtual void handleWheelUp() {}
+		virtual void handleWheelDown() {}
 		virtual bool handleKeyDown(Common::Event &event) { return false; }
 	};
 
@@ -294,24 +296,37 @@ public:
 		void draw(bool drawFocused = false);
 	};
 
-	class MacSlider : public MacWidget {
+	class MacSliderBase : public MacWidget {
+	protected:
+		int _minValue;
+		int _maxValue;
+		int _minPos;
+		int _maxPos;
+		int _handlePos = -1;
+		int _grabOffset = -1;
+
+		int calculateValueFromPos() const;
+		int calculatePosFromValue() const;
+
+	public:
+		MacSliderBase(MacGui::MacDialogWindow *window, Common::Rect bounds, int minValue, int maxValue, int minPos, int maxPos, bool enabled)
+			: MacWidget(window, bounds, "SliderBase", enabled),
+			_minValue(minValue), _maxValue(maxValue),
+			_minPos(minPos), _maxPos(maxPos) {}
+
+		void setValue(int value);
+	};
+
+	class MacSlider : public MacSliderBase {
 	private:
 		Common::Point _clickPos;
 		uint32 _nextRepeat;
 
-		int _minValue;
-		int _maxValue;
 		int _pageSize;
 		int _paging;
 
 		bool _upArrowPressed = false;
 		bool _downArrowPressed = false;
-		int _dragOffset = -1;
-		int _dragPos = -1;
-
-		bool _redrawUpArrow = false;
-		bool _redrawDownArrow = false;
-		bool _redrawBody = false;
 
 		Common::Rect _boundsButtonUp;
 		Common::Rect _boundsButtonDown;
@@ -320,7 +335,13 @@ public:
 		Common::Rect getHandleRect(int value);
 
 		void fill(Common::Rect r, bool inverted = false);
+
+		void drawUpArrow(bool markAsDirty);
+		void drawDownArrow(bool markAsDirty);
+		void drawArrow(Common::Rect r, const uint16 *bitmap, bool markAsDirty);
+
 		void drawHandle(Common::Rect r);
+		void redrawHandle(int oldValue, int newValue);
 
 	public:
 		MacSlider(MacGui::MacDialogWindow *window, Common::Rect bounds, int minValue, int maxValue, int pageSize, bool enabled);
@@ -332,36 +353,36 @@ public:
 		void handleMouseUp(Common::Event &event);
 		void handleMouseMove(Common::Event &event);
 		void handleMouseHeld();
+		void handleWheelUp();
+		void handleWheelDown();
 	};
 
-	class MacPictureSlider : public MacWidget {
+	class MacPictureSlider : public MacSliderBase {
 	private:
 		MacPicture *_background;
 		MacPicture *_handle;
 		int _minX;
 		int _maxX;
-		int _handleX;
-		int _grabOffset;
-		int _lastHandleX = -1;
-		int _minValue;
-		int _maxValue;
 		int _leftMargin;
 		int _rightMargin;
 
+		void eraseHandle();
+		void drawHandle();
+
 	public:
 		 MacPictureSlider(MacGui::MacDialogWindow *window, MacPicture *background, MacPicture *handle, bool enabled, int minX, int maxX, int minValue, int maxValue, int leftMargin, int rightMargin)
-			: MacWidget(window, background->getBounds(), "Slider", enabled),
+			: MacSliderBase(window, background->getBounds(), minValue, maxValue, minX + leftMargin, maxX - rightMargin, enabled),
 			_background(background), _handle(handle), _minX(minX),
-			_maxX(maxX), _minValue(minValue), _maxValue(maxValue),
-			_leftMargin(leftMargin), _rightMargin(rightMargin) {}
+			_maxX(maxX), _leftMargin(leftMargin), _rightMargin(rightMargin) {}
 
 		bool findWidget(int x, int y) const;
-		void setValue(int value);
 		void draw(bool drawFocused = false);
 
 		void handleMouseDown(Common::Event &event);
 		void handleMouseUp(Common::Event &event);
 		void handleMouseMove(Common::Event &event);
+		void handleWheelUp();
+		void handleWheelDown();
 	};
 
 	class MacDialogWindow {


Commit: e44874b7468a64e6bf9687a71104e464d0359999
    https://github.com/scummvm/scummvm/commit/e44874b7468a64e6bf9687a71104e464d0359999
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Cleanup Mac slider widget

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index b5f034a7571..e6cba3c10d6 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -1023,6 +1023,8 @@ bool MacGui::MacSlider::findWidget(int x, int y) const {
 
 	Common::Rect bounds = _bounds;
 
+	// While dragging the handle, you'r allowed to go outside the slider.
+
 	if (_grabOffset >= 0) {
 		bounds.left -= 25;
 		bounds.right += 25;
@@ -1252,20 +1254,6 @@ void MacGui::MacSlider::handleMouseMove(Common::Event &event) {
 	int x = event.mouse.x;
 	int y = event.mouse.y;
 
-	if (!findWidget(x, y)) {
-		if (_upArrowPressed) {
-			_upArrowPressed = false;
-			drawUpArrow(true);
-		}
-
-		if (_downArrowPressed) {
-			_downArrowPressed = false;
-			drawDownArrow(true);
-		}
-
-		return;
-	}
-
 	if (_grabOffset >= 0) {
 		Common::Rect r;
 
@@ -1404,6 +1392,21 @@ bool MacGui::MacPictureSlider::findWidget(int x, int y) const {
 	return _bounds.contains(x, y);
 }
 
+void MacGui::MacPictureSlider::draw(bool drawFocused) {
+	if (!_redraw && !_fullRedraw)
+		return;
+
+	debug(1, "MacGui::MacPictureSlider: Drawing slider %d (_fullRedraw = %d, drawFocused = %d, _value = %d)", _id, _fullRedraw, drawFocused, _value);
+
+	if (_fullRedraw) {
+		_window->drawSprite(_background->getPicture(), _bounds.left, _bounds.top);
+		drawHandle();
+	}
+
+	_redraw = false;
+	_fullRedraw = false;
+}
+
 void MacGui::MacPictureSlider::eraseHandle() {
 	Common::Rect r = _handle->getBounds();
 	int y = r.top - _bounds.top;
@@ -1422,21 +1425,6 @@ void MacGui::MacPictureSlider::drawHandle() {
 	_window->drawSprite(sprite, _bounds.left + _handlePos, r.top);
 }
 
-void MacGui::MacPictureSlider::draw(bool drawFocused) {
-	if (!_redraw && !_fullRedraw)
-		return;
-
-	debug(1, "MacGui::MacPictureSlider: Drawing slider %d (_fullRedraw = %d, drawFocused = %d, _value = %d)", _id, _fullRedraw, drawFocused, _value);
-
-	if (_fullRedraw) {
-		_window->drawSprite(_background->getPicture(), _bounds.left, _bounds.top);
-		drawHandle();
-	}
-
-	_redraw = false;
-	_fullRedraw = false;
-}
-
 void MacGui::MacPictureSlider::handleMouseDown(Common::Event &event) {
 	int mouseX = event.mouse.x;
 	int handleWidth = _handle->getBounds().width();


Commit: eef41c8d0e573355cbfcc3de83795dd42080b9fc
    https://github.com/scummvm/scummvm/commit/eef41c8d0e573355cbfcc3de83795dd42080b9fc
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Fix dragging the mouse out of the Mac slider's control

The ghost of the drag handle wasn't erased. Now it is.

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index e6cba3c10d6..dc47ba402cf 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -1023,11 +1023,14 @@ bool MacGui::MacSlider::findWidget(int x, int y) const {
 
 	Common::Rect bounds = _bounds;
 
-	// While dragging the handle, you'r allowed to go outside the slider.
+	// While dragging the handle, you're allowed to go outside the slider.
+	// I don't know by how much, though.
 
 	if (_grabOffset >= 0) {
 		bounds.left -= 25;
 		bounds.right += 25;
+		bounds.top -= 50;
+		bounds.bottom += 50;
 	}
 
 	return bounds.contains(x, y);
@@ -1158,6 +1161,12 @@ void MacGui::MacSlider::drawArrow(Common::Rect r, const uint16 *bitmap, bool mar
 		_window->markRectAsDirty(r);
 }
 
+void MacGui::MacSlider::eraseDragHandle() {
+	Common::Rect r(_boundsBody.left + 1, _handlePos, _boundsBody.right - 1, _handlePos + 16);
+	fill(r);
+	_window->markRectAsDirty(r);
+}
+
 void MacGui::MacSlider::drawHandle(Common::Rect r) {
 	debug(2, "MacGui::MacSlider::drawHandle(%d)", r.top);
 
@@ -1230,11 +1239,9 @@ void MacGui::MacSlider::handleMouseUp(Common::Event &event) {
 		_downArrowPressed = false;
 		drawDownArrow(true);
 	} else if (_grabOffset >= 0) {
-		// Erase the drag rect, since the handle might not end up in
+		// Erase the drag handle, since the handle might not end up in
 		// the exact same spot.
-		Common::Rect r(_boundsBody.left + 1, _handlePos, _boundsBody.right - 1, _handlePos + 16);
-		fill(r);
-		_window->markRectAsDirty(r);
+		eraseDragHandle();
 
 		// Calculate new value and move the handle there
 		int newValue = calculateValueFromPos();
@@ -1255,38 +1262,46 @@ void MacGui::MacSlider::handleMouseMove(Common::Event &event) {
 	int y = event.mouse.y;
 
 	if (_grabOffset >= 0) {
-		Common::Rect r;
+		if (!findWidget(x, y)) {
+			eraseDragHandle();
+			drawHandle(getHandleRect(_value));
+			return;
+		}
 
-		r.left = _boundsBody.left + 1;
-		r.top = _handlePos;
-		r.right = _boundsBody.right - 1;
-		r.bottom = _handlePos + 16;
+		int newHandlePos = CLIP<int>(y - _grabOffset, _boundsBody.top, _boundsBody.bottom - 16);
 
-		fill(r);
-		_window->markRectAsDirty(r);
+		// Theoretically, we could end here if the handle position has
+		// not changed. However, we currently don't keep track of if
+		// the handle is hidden because the mouse has moved out of the
+		// widget's control.
+
+		eraseDragHandle();
+
+		_handlePos = newHandlePos;
 
 		Common::Rect handleRect = getHandleRect(_value);
 
-		if (r.intersects(handleRect)) {
+		if (ABS(_handlePos - handleRect.top) <= handleRect.height()) {
 			drawHandle(handleRect);
 			_window->markRectAsDirty(handleRect);
 		}
 
-		_handlePos = CLIP<int>(y - _grabOffset, _boundsBody.top, _boundsBody.bottom - 16);
-
-		r.moveTo(_boundsBody.left + 1, _handlePos);
+		int x0 = _boundsBody.left + 1;
+		int x1 = _boundsBody.right - 1;
+		int y0 = _handlePos;
+		int y1 = _handlePos + 16;
 
 		// Drawing a solid rectangle would be easier, and probably look
 		// better. But it seems the orginal Mac widget would draw the
 		// frame as an inverted slider background, even when drawing it
 		// on top of the slider handle.
 
-		fill(Common::Rect(r.left, r.top, r.right, r.top + 1), true);
-		fill(Common::Rect(r.left, r.bottom - 1, r.right, r.bottom), true);
-		fill(Common::Rect(r.left, r.top + 1, r.left + 1, r.bottom - 1), true);
-		fill(Common::Rect(r.right - 1, r.top + 1, r.right, r.bottom - 1), true);
+		fill(Common::Rect(x0, y0, x1, y0 + 1), true);
+		fill(Common::Rect(x0, y1 - 1, x1, y1), true);
+		fill(Common::Rect(x0, y0 + 1, x0 + 1, y1 - 1), true);
+		fill(Common::Rect(x1 - 1, y0 + 1, x1, y1 - 1), true);
 
-		_window->markRectAsDirty(r);
+		_window->markRectAsDirty(Common::Rect(x0, y0, x1, y1));
 	} else {
 		if (!_boundsButtonUp.contains(x, y)) {
 			if (_upArrowPressed) {
@@ -1338,8 +1353,8 @@ void MacGui::MacSlider::handleMouseHeld() {
 	if (_paging) {
 		Common::Rect r = getHandleRect(_value);
 
-		// Keep paging until at least half the scroll handle has gone past the
-		// mouse cursor. This may have to be tuned.
+		// Keep paging until at least half the scroll handle has gone
+		// past the mouse cursor. This may have to be tuned.
 
 		if (_paging == -1) {
 			if (p.y < r.top + r.height() / 2 && _value > _minValue) {
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 2051d01b552..71965318dc7 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -211,7 +211,6 @@ public:
 		MacButton(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::String text, bool enabled) : MacWidget(window, bounds, text, enabled) {}
 
 		void draw(bool drawFocused = false);
-
 		void drawCorners(Common::Rect r, CornerLine *corner);
 	};
 
@@ -237,7 +236,6 @@ public:
 		MacStaticText(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::String text, bool enabled) : MacWidget(window, bounds, text, true) {}
 
 		bool findWidget(int x, int y) const { return false; }
-
 		void draw(bool drawFocused = false);
 	};
 
@@ -314,6 +312,9 @@ public:
 			_minValue(minValue), _maxValue(maxValue),
 			_minPos(minPos), _maxPos(maxPos) {}
 
+		void getFocus() {}
+		void loseFocus() {}
+
 		void setValue(int value);
 	};
 
@@ -340,6 +341,7 @@ public:
 		void drawDownArrow(bool markAsDirty);
 		void drawArrow(Common::Rect r, const uint16 *bitmap, bool markAsDirty);
 
+		void eraseDragHandle();
 		void drawHandle(Common::Rect r);
 		void redrawHandle(int oldValue, int newValue);
 


Commit: a39a08c4beb4daee06eca2bcdb47e5e13e92290a
    https://github.com/scummvm/scummvm/commit/a39a08c4beb4daee06eca2bcdb47e5e13e92290a
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Hopefully fix Mac slider drag glitch

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index dc47ba402cf..41210b2d324 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -1264,7 +1264,14 @@ void MacGui::MacSlider::handleMouseMove(Common::Event &event) {
 	if (_grabOffset >= 0) {
 		if (!findWidget(x, y)) {
 			eraseDragHandle();
-			drawHandle(getHandleRect(_value));
+
+			Common::Rect handleRect = getHandleRect(_value);
+
+			if (ABS(_handlePos - handleRect.top) <= handleRect.height()) {
+				drawHandle(handleRect);
+				_window->markRectAsDirty(handleRect);
+			}
+
 			return;
 		}
 
@@ -1277,8 +1284,6 @@ void MacGui::MacSlider::handleMouseMove(Common::Event &event) {
 
 		eraseDragHandle();
 
-		_handlePos = newHandlePos;
-
 		Common::Rect handleRect = getHandleRect(_value);
 
 		if (ABS(_handlePos - handleRect.top) <= handleRect.height()) {
@@ -1286,6 +1291,8 @@ void MacGui::MacSlider::handleMouseMove(Common::Event &event) {
 			_window->markRectAsDirty(handleRect);
 		}
 
+		_handlePos = newHandlePos;
+
 		int x0 = _boundsBody.left + 1;
 		int x1 = _boundsBody.right - 1;
 		int y0 = _handlePos;


Commit: bf14d55d8ff3e1149573a560b07370aa70910c48
    https://github.com/scummvm/scummvm/commit/bf14d55d8ff3e1149573a560b07370aa70910c48
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Fix compile after enhancements update

I forgot to commit this before finishing the rebase/force-push.

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 41210b2d324..12f6b464ba4 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -1910,7 +1910,7 @@ int MacGui::MacDialogWindow::runDialog() {
 				break;
 
 			case Common::EVENT_WHEELUP:
-				if (!_gui->_vm->_enableEnhancements || _focusedWidget)
+				if (!_gui->_vm->enhancementEnabled(kEnhUIUX) || _focusedWidget)
 					break;
 
 				w = findWidget(event.mouse.x, event.mouse.y);
@@ -1920,7 +1920,7 @@ int MacGui::MacDialogWindow::runDialog() {
 				break;
 
 			case Common::EVENT_WHEELDOWN:
-				if (!_gui->_vm->_enableEnhancements || _focusedWidget)
+				if (!_gui->_vm->enhancementEnabled(kEnhUIUX) || _focusedWidget)
 					break;
 
 				w = findWidget(event.mouse.x, event.mouse.y);
@@ -3566,7 +3566,7 @@ bool MacLoomGui::handleEvent(Common::Event &event) {
 			// increments of 16 pixels. As an enhancement, we allow
 			// any X coordinate.
 
-			if (!_vm->_enableEnhancements)
+			if (!_vm->enhancementEnabled(kEnhUIUX))
 				newX &= ~0xF;
 
 			if (newX != _practiceBoxPos.x || newY != _practiceBoxPos.y) {
@@ -3957,7 +3957,7 @@ bool MacIndy3Gui::Inventory::handleEvent(Common::Event &event) {
 	if (!_enabled || !_verbid)
 		return false;
 
-	if (_vm->_enableEnhancements) {
+	if (_vm->enhancementEnabled(kEnhUIUX)) {
 		if ((event.type == Common::EVENT_WHEELUP || event.type == Common::EVENT_WHEELDOWN) && _bounds.contains(event.mouse.x, event.mouse.y)) {
 			if (event.type == Common::EVENT_WHEELUP) {
 				_scrollBar->scroll(kScrollUp);


Commit: 0dc5b501e9ae34d7c05a799b4a46522f702e5e36
    https://github.com/scummvm/scummvm/commit/0dc5b501e9ae34d7c05a799b4a46522f702e5e36
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Fix cursor after closing Mac dialog window

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 12f6b464ba4..dc37225ea71 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -1561,8 +1561,8 @@ MacGui::MacDialogWindow::~MacDialogWindow() {
 	if (!CursorMan.isVisible())
 		CursorMan.showMouse(true);
 
-	CursorMan.showMouse(true);
-	_gui->_windowManager->replaceCursor(Graphics::MacGUIConstants::kMacCursorArrow);
+	CursorMan.showMouse(_cursorWasVisible);
+	_gui->_windowManager->popCursor();
 
 	copyToScreen(_backup);
 	_backup->free();
@@ -1586,6 +1586,8 @@ void MacGui::MacDialogWindow::show() {
 	_visible = true;
 	copyToScreen();
 	_dirtyRects.clear();
+	_gui->_windowManager->pushCursor(Graphics::MacGUIConstants::kMacCursorArrow);
+	_cursorWasVisible = CursorMan.showMouse(true);
 }
 
 void MacGui::MacDialogWindow::setFocusedWidget(int x, int y) {
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 71965318dc7..c4709301ec7 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -396,6 +396,7 @@ public:
 
 		Graphics::Surface *_beamCursor = nullptr;
 		Common::Point _beamCursorPos;
+		bool _cursorWasVisible = false;
 		bool _beamCursorVisible = false;
 		int _beamCursorHotspotX = 3;
 		int _beamCursorHotspotY = 4;


Commit: f34b4f78913353662d55e18a9b724878bff04bc7
    https://github.com/scummvm/scummvm/commit/f34b4f78913353662d55e18a9b724878bff04bc7
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Make beam cursor hotspot const

Changed paths:
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index c4709301ec7..704ce0cf9c6 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -398,8 +398,8 @@ public:
 		Common::Point _beamCursorPos;
 		bool _cursorWasVisible = false;
 		bool _beamCursorVisible = false;
-		int _beamCursorHotspotX = 3;
-		int _beamCursorHotspotY = 4;
+		const int _beamCursorHotspotX = 3;
+		const int _beamCursorHotspotY = 4;
 
 		void drawBeamCursor();
 		void undrawBeamCursor();


Commit: 9c2548d6e70a408d725523739e9f08d04ce9decc
    https://github.com/scummvm/scummvm/commit/9c2548d6e70a408d725523739e9f08d04ce9decc
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Fix Mac Loom practice box redrawing

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index dc37225ea71..6a5fcf8d1a1 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -3443,7 +3443,7 @@ void MacLoomGui::update(int delta) {
 				_practiceBox->create(w, h, Graphics::PixelFormat
 ::createFormatCLUT8());
 
-				_practiceBox->fillRect(Common::Rect(62, 22), kBlack);
+				_practiceBox->fillRect(Common::Rect(w, h), kBlack);
 
 				Color color = bw ? kWhite : kLightGray;
 
@@ -3459,6 +3459,8 @@ void MacLoomGui::update(int delta) {
 
 				_practiceBoxNotes = notes;
 
+				_practiceBox->fillRect(Common::Rect(2, 2, w - 2, h - 2), kBlack);
+
 				const Graphics::Font *font = getFont(kLoomFontLarge);
 				Color colors[] = { kRed, kBrightRed, kBrightYellow, kBrightGreen, kBrightCyan, kCyan, kBrightBlue, kWhite };
 


Commit: 48d5aa655ce5f074c5621d6469db34c1e194720e
    https://github.com/scummvm/scummvm/commit/48d5aa655ce5f074c5621d6469db34c1e194720e
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Remove temporary Loom Mac GUI variables that were only used once

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 6a5fcf8d1a1..7e87dccd23e 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -3478,14 +3478,11 @@ void MacLoomGui::update(int delta) {
 			if (_practiceBox) {
 				debug(1, "MacLoomGui: Deleting practice mode box");
 
-				int w = _practiceBox->w;
-				int h = _practiceBox->h;
-
 				_practiceBox->free();
 				delete _practiceBox;
 				_practiceBox = nullptr;
 
-				_system->copyRectToScreen(_surface->getBasePtr(_practiceBoxPos.x, _practiceBoxPos.y), _surface->pitch, _practiceBoxPos.x, _practiceBoxPos.y, w, h);
+				_system->copyRectToScreen(_surface->getBasePtr(_practiceBoxPos.x, _practiceBoxPos.y), _surface->pitch, _practiceBoxPos.x, _practiceBoxPos.y, _practiceBox->w, _practiceBox->h);
 			}
 		}
 	}


Commit: fb3613ef6253b5cffdfdff4bef57fa7c491d915d
    https://github.com/scummvm/scummvm/commit/fb3613ef6253b5cffdfdff4bef57fa7c491d915d
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Fix use-after-free crash with Mac Loom practice box

Strange that it took this far into the game for me to see it.

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 7e87dccd23e..e480d1129df 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -3478,11 +3478,11 @@ void MacLoomGui::update(int delta) {
 			if (_practiceBox) {
 				debug(1, "MacLoomGui: Deleting practice mode box");
 
+				_system->copyRectToScreen(_surface->getBasePtr(_practiceBoxPos.x, _practiceBoxPos.y), _surface->pitch, _practiceBoxPos.x, _practiceBoxPos.y, _practiceBox->w, _practiceBox->h);
+
 				_practiceBox->free();
 				delete _practiceBox;
 				_practiceBox = nullptr;
-
-				_system->copyRectToScreen(_surface->getBasePtr(_practiceBoxPos.x, _practiceBoxPos.y), _surface->pitch, _practiceBoxPos.x, _practiceBoxPos.y, _practiceBox->w, _practiceBox->h);
 			}
 		}
 	}


Commit: fc3d662a8dd1f02fedecfe75447bf9bfd962db0f
    https://github.com/scummvm/scummvm/commit/fc3d662a8dd1f02fedecfe75447bf9bfd962db0f
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Work around Indy 3 Mac GUI being displayed during a fight

This happened when offering the wrong item to the guard manning the
security system at Castle Brunwald. I haven't seen it happen anywhere
else, but I generally try to avoid fights.

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index e480d1129df..85e192954d0 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -5093,6 +5093,13 @@ bool MacIndy3Gui::isVerbGuiAllowed() const {
 	if (vs->topline != 144 || vs->h != 56)
 		return false;
 
+	// HACK: Don't allow the GUI during fist fights. Usually this is not a
+	//       problem, in my experience, but I've had it happening when
+	//       offering an item to a guard led directly to one.
+
+	if (_vm->VAR(_vm->VAR_VERB_SCRIPT) == 19)
+		return false;
+
 	return true;
 }
 


Commit: 93480c331ab0e98528c67aa0869a2efb5433a325
    https://github.com/scummvm/scummvm/commit/93480c331ab0e98528c67aa0869a2efb5433a325
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: MACGUI: Wire up options for Loom

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 85e192954d0..fdb16310098 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -21,6 +21,7 @@
 
 #include "common/system.h"
 #include "common/macresman.h"
+#include "common/config-manager.h"
 
 #include "graphics/cursorman.h"
 #include "graphics/maccursor.h"
@@ -35,6 +36,8 @@
 #include "scumm/actor.h"
 #include "scumm/charset.h"
 #include "scumm/gfx_mac.h"
+#include "scumm/scumm.h"
+#include "scumm/sound.h"
 #include "scumm/usage_bits.h"
 #include "scumm/verbs.h"
 
@@ -3328,13 +3331,18 @@ bool MacLoomGui::runOptionsDialog() {
 	// 11 - Text speed slider (manually created)
 	// 12 - Music quality slider (manually created)
 
-	// TODO: Get these from the SCUMM engine
-	int sound = 0;
-	int music = 0;
-	int scrolling = 0;
-	int fullAnimation = 0;
-	int textSpeed = 5;
-	int musicQuality = 2;
+	int sound = 1;
+	int music = 1;
+	if (_vm->VAR(167) == 2) {
+		sound = music = 0;
+	} else if (_vm->VAR(167) == 1) {
+		music = 0;
+	}
+
+	int scrolling = _vm->_snapScroll == 0;
+	int fullAnimation = _vm->VAR(_vm->VAR_MACHINE_SPEED) == 1 ? 0 : 1;
+	int textSpeed = _vm->_defaultTextSpeed;
+	int musicQuality = _vm->VAR(_vm->VAR_SOUNDCARD) == 10 ? 0 : 2;
 
 	MacDialogWindow *window = createDialog(1000);
 
@@ -3352,11 +3360,8 @@ bool MacLoomGui::runOptionsDialog() {
 	window->addPictureSlider(8, 9, true, 5, 69, 0, 2, 6, 4);
 	window->setWidgetValue(12, musicQuality);
 
-	// TODO: I don't know where it gets the "Machine Speed" from. It doesn't
-	// appear to be VAR_MACHINE_SPEED, because I think that's only set to 1
-	// or 0, and may be the "Full Animation" setting.
-
-	window->addSubstitution(Common::String::format("%d", _vm->VAR(_vm->VAR_MACHINE_SPEED)));
+	// Machine rating
+	window->addSubstitution(Common::String::format("%d", _vm->VAR(53)));
 
 	// When quitting, the default action is not to not apply options
 	bool ret = false;
@@ -3377,12 +3382,58 @@ bool MacLoomGui::runOptionsDialog() {
 	}
 
 	if (ret) {
-		debug("sound: %d", window->getWidgetValue(2));
-		debug("music: %d", window->getWidgetValue(3));
-		debug("scrolling: %d", window->getWidgetValue(6));
-		debug("full animation: %d", window->getWidgetValue(7));
-		debug("text speed: %d", window->getWidgetValue(11));
-		debug("music quality: %d", window->getWidgetValue(12));
+		// Update settings
+
+		// TEXT SPEED
+		_vm->_defaultTextSpeed = CLIP<int>(window->getWidgetValue(11), 0, 9);
+		ConfMan.setInt("original_gui_text_speed", _vm->_defaultTextSpeed);
+		_vm->setTalkSpeed(_vm->_defaultTextSpeed);
+
+		// SOUND&MUSIC ACTIVATION
+		// 0 - Sound&Music on
+		// 1 - Sound on, music off
+		// 2 - Sound&Music off
+		int musicVariableValue = 0;
+
+		if (window->getWidgetValue(2) == 0) {
+			musicVariableValue = 2;
+		} else if (window->getWidgetValue(2) == 1 && window->getWidgetValue(3) == 0) {
+			musicVariableValue = 1;
+		}
+
+		_vm->VAR(167) = musicVariableValue;
+
+		if (musicVariableValue != 0) {
+			if (_vm->VAR(169) != 0) {
+				_vm->_sound->stopSound(_vm->VAR(169));
+				_vm->VAR(169) = 0;
+			}
+		}
+
+		// SCROLLING ACTIVATION
+		_vm->_snapScroll = window->getWidgetValue(6) == 0;
+
+		if (_vm->VAR_CAMERA_FAST_X != 0xFF)
+			_vm->VAR(_vm->VAR_CAMERA_FAST_X) = _vm->_snapScroll;
+
+		// FULL ANIMATION ACTIVATION
+		_vm->VAR(_vm->VAR_MACHINE_SPEED) = window->getWidgetValue(7) == 1 ? 0 : 1;
+
+		// MUSIC QUALITY SELECTOR
+		//
+		// (selections 1 and 2 appear to be the same music
+		// files but rendered at a different bitrate, while
+		// selection 0 activates a completely different set
+		// of files)
+		//
+		// This is currently unimplemented. Let's just set the proper
+		// value for VAR_SOUNDCARD...
+		_vm->VAR(_vm->VAR_SOUNDCARD) = window->getWidgetValue(12) == 0 ? 10 : 11;
+		debug(6, "MacLoomGui::runOptionsDialog(): music quality: %d - unimplemented!", window->getWidgetValue(12));
+
+
+		_vm->syncSoundSettings();
+		ConfMan.flushToDisk();
 	}
 
 	delete window;


Commit: 885f698b2963bf033210c3b4f12afabeb0519526
    https://github.com/scummvm/scummvm/commit/885f698b2963bf033210c3b4f12afabeb0519526
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: MACGUI: Implement partial quality override for Loom

The lowest setting actually does something now :P

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/players/player_mac.cpp
    engines/scumm/players/player_mac.h
    engines/scumm/players/player_v3m.cpp
    engines/scumm/players/player_v3m.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index fdb16310098..50eb2798a96 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -36,6 +36,7 @@
 #include "scumm/actor.h"
 #include "scumm/charset.h"
 #include "scumm/gfx_mac.h"
+#include "scumm/players/player_v3m.h"
 #include "scumm/scumm.h"
 #include "scumm/sound.h"
 #include "scumm/usage_bits.h"
@@ -3426,9 +3427,11 @@ bool MacLoomGui::runOptionsDialog() {
 		// selection 0 activates a completely different set
 		// of files)
 		//
-		// This is currently unimplemented. Let's just set the proper
+		// This is currently incomplete. Let's just set the proper
 		// value for VAR_SOUNDCARD...
 		_vm->VAR(_vm->VAR_SOUNDCARD) = window->getWidgetValue(12) == 0 ? 10 : 11;
+		((Player_V3M *)_vm->_musicEngine)->overrideQuality(_vm->VAR(_vm->VAR_SOUNDCARD) == 10);
+
 		debug(6, "MacLoomGui::runOptionsDialog(): music quality: %d - unimplemented!", window->getWidgetValue(12));
 
 
diff --git a/engines/scumm/players/player_mac.cpp b/engines/scumm/players/player_mac.cpp
index ed8be915010..bd3524a10f6 100644
--- a/engines/scumm/players/player_mac.cpp
+++ b/engines/scumm/players/player_mac.cpp
@@ -314,6 +314,10 @@ int Player_Mac::noteToPitchModifier(byte note, Instrument *instrument) {
 	}
 }
 
+void Player_Mac::overrideChannelMask(int newMask) {
+	_channelMask = newMask;
+}
+
 int Player_Mac::readBuffer(int16 *data, const int numSamples) {
 	Common::StackLock lock(_mutex);
 
diff --git a/engines/scumm/players/player_mac.h b/engines/scumm/players/player_mac.h
index 301826f27f5..0e34ef850fc 100644
--- a/engines/scumm/players/player_mac.h
+++ b/engines/scumm/players/player_mac.h
@@ -126,6 +126,7 @@ protected:
 
 	uint32 durationToSamples(uint16 duration);
 	int noteToPitchModifier(byte note, Instrument *instrument);
+	void overrideChannelMask(int newMask);
 };
 
 } // End of namespace Scumm
diff --git a/engines/scumm/players/player_v3m.cpp b/engines/scumm/players/player_v3m.cpp
index a3aa293236a..ea617a10430 100644
--- a/engines/scumm/players/player_v3m.cpp
+++ b/engines/scumm/players/player_v3m.cpp
@@ -189,4 +189,8 @@ bool Player_V3M::getNextNote(int ch, uint32 &samples, int &pitchModifier, byte &
 	return true;
 }
 
+void Player_V3M::overrideQuality(bool lowQuality) {
+	overrideChannelMask(lowQuality ? 01 : 0x1E);
+}
+
 } // End of namespace Scumm
diff --git a/engines/scumm/players/player_v3m.h b/engines/scumm/players/player_v3m.h
index fc8a15f8412..313251600e9 100644
--- a/engines/scumm/players/player_v3m.h
+++ b/engines/scumm/players/player_v3m.h
@@ -45,6 +45,7 @@ public:
 
 	bool loadMusic(const byte *ptr) override;
 	bool getNextNote(int ch, uint32 &samples, int &pitchModifier, byte &velocity) override;
+	void overrideQuality(bool lowQuality);
 };
 
 } // End of namespace Scumm


Commit: 44061890c9ffd9a321ac0f3ce8bba8a65f5088fd
    https://github.com/scummvm/scummvm/commit/44061890c9ffd9a321ac0f3ce8bba8a65f5088fd
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: MACGUI: Clarify comment about music quality

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 50eb2798a96..566db69a234 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -3424,8 +3424,8 @@ bool MacLoomGui::runOptionsDialog() {
 		//
 		// (selections 1 and 2 appear to be the same music
 		// files but rendered at a different bitrate, while
-		// selection 0 activates a completely different set
-		// of files)
+		// selection 0 activates the low quality channel in
+		// the sequence files and mutes everything else)
 		//
 		// This is currently incomplete. Let's just set the proper
 		// value for VAR_SOUNDCARD...


Commit: 7ab3fa11c621c687b72ce00f34a7563aa818e57d
    https://github.com/scummvm/scummvm/commit/7ab3fa11c621c687b72ce00f34a7563aa818e57d
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Player_Mac: Ensure threadsafeness for _channelMask override

Changed paths:
    engines/scumm/players/player_mac.cpp


diff --git a/engines/scumm/players/player_mac.cpp b/engines/scumm/players/player_mac.cpp
index bd3524a10f6..c1d6cf48620 100644
--- a/engines/scumm/players/player_mac.cpp
+++ b/engines/scumm/players/player_mac.cpp
@@ -315,6 +315,7 @@ int Player_Mac::noteToPitchModifier(byte note, Instrument *instrument) {
 }
 
 void Player_Mac::overrideChannelMask(int newMask) {
+	Common::StackLock lock(_mutex);
 	_channelMask = newMask;
 }
 


Commit: 2efd9ab70ab6a1cd741d47c3c0864b80e4992338
    https://github.com/scummvm/scummvm/commit/2efd9ab70ab6a1cd741d47c3c0864b80e4992338
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
GRAPHICS: MACGUI: Add kWMModeForceMacBorder mode

This allows for having the Mac desktop arc even during Win95 mode.

Changed paths:
    graphics/macgui/macmenu.cpp
    graphics/macgui/macwindowmanager.h


diff --git a/graphics/macgui/macmenu.cpp b/graphics/macgui/macmenu.cpp
index ceee71b7761..0d200125a86 100644
--- a/graphics/macgui/macmenu.cpp
+++ b/graphics/macgui/macmenu.cpp
@@ -1003,7 +1003,8 @@ bool MacMenu::draw(ManagedSurface *g, bool forceRedraw) {
 
 	_screen.clear(_wm->_colorGreen);
 
-	drawFilledRoundRect(&_screen, r, (_wm->_mode & kWMModeWin95) ? 0 : kDesktopArc, _wm->_colorWhite);
+	bool shouldUseDesktopArc = !(_wm->_mode & kWMModeWin95) || (_wm->_mode & kWMModeForceMacBorder);
+	drawFilledRoundRect(&_screen, r, shouldUseDesktopArc ? kDesktopArc : 0, _wm->_colorWhite);
 
 	r.top = 7;
 	_screen.fillRect(r, _wm->_colorWhite);
diff --git a/graphics/macgui/macwindowmanager.h b/graphics/macgui/macwindowmanager.h
index b4e20dcff20..4267f48429f 100644
--- a/graphics/macgui/macwindowmanager.h
+++ b/graphics/macgui/macwindowmanager.h
@@ -90,6 +90,7 @@ enum {
 	kWMModeWin95				= (1 << 10),
 	kWMModeForceMacFontsInWin95 = (1 << 11), // Enforce Mac font for languages which don't have glyphs in ms_sans_serif.ttf
 	kWMModeNoCursorOverride     = (1 << 12),
+	kWMModeForceMacBorder       = (1 << 13),
 };
 
 }


Commit: 2965b5cb6262c2492f50c89c23ef5bdf5c40a18a
    https://github.com/scummvm/scummvm/commit/2965b5cb6262c2492f50c89c23ef5bdf5c40a18a
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: MACGUI: Add UX enhancement for Mac menu bar

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 566db69a234..f00cee6f0c6 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -2216,7 +2216,13 @@ void MacGui::menuCallback(int id, Common::String &name, void *data) {
 }
 
 void MacGui::initialize() {
-	_windowManager = new Graphics::MacWindowManager(Graphics::kWMModeNoDesktop | Graphics::kWMModeAutohideMenu | Graphics::kWMModalMenuMode | Graphics::kWMModeNoCursorOverride);
+	uint32 menuMode = Graphics::kWMModeNoDesktop | Graphics::kWMModeAutohideMenu | Graphics::kWMModalMenuMode | Graphics::kWMModeNoCursorOverride;
+
+	// Allow a more modern UX: the menu doesn't close if the mouse accidentally goes outside the menu area
+	if (_vm->enhancementEnabled(kEnhUIUX))
+		menuMode |= Graphics::kWMModeWin95 | Graphics::kWMModeForceMacFontsInWin95 | Graphics::kWMModeForceMacBorder;
+
+	_windowManager = new Graphics::MacWindowManager(menuMode);
 	_windowManager->setEngine(_vm);
 	_windowManager->setScreen(640, 400);
 


Commit: 204d900dd895c3c4eaea4b6a9ff9cedc8c79dc7c
    https://github.com/scummvm/scummvm/commit/204d900dd895c3c4eaea4b6a9ff9cedc8c79dc7c
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: MACGUI: Wire up quit and restart callbacks

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/input.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index f00cee6f0c6..20059679f76 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -2330,7 +2330,7 @@ bool MacGui::handleMenu(int id, Common::String &name) {
 
 	case 202:	// Restart
 		if (runRestartDialog())
-			debug("Game should restart now");
+			_vm->restart();
 		return true;
 
 	case 203:	// Pause
@@ -2960,7 +2960,7 @@ bool MacLoomGui::handleMenu(int id, Common::String &name) {
 
 	case 205:	// Quit
 		if (runQuitDialog())
-			debug("Game should quit now");
+			_vm->quitGame();
 		break;
 
 	default:
diff --git a/engines/scumm/input.cpp b/engines/scumm/input.cpp
index 0e6a0d7c8bf..9d5130ca713 100644
--- a/engines/scumm/input.cpp
+++ b/engines/scumm/input.cpp
@@ -281,13 +281,19 @@ void ScummEngine::parseEvent(Common::Event event) {
 		break;
 	case Common::EVENT_RETURN_TO_LAUNCHER:
 	case Common::EVENT_QUIT:
+	{
 		// Some backends send a key stroke event and the quit
 		// event which was triggered by the keystroke. Clear the key.
 		clearClickedStatus();
 
+		bool usesMacMenu = isUsingOriginalGUI() &&
+						   _game.platform == Common::kPlatformMacintosh &&
+						   _game.version < 4;
+
 		if (isUsingOriginalGUI() &&
 			_game.platform != Common::kPlatformSegaCD &&
-			_game.platform != Common::kPlatformNES) {
+			_game.platform != Common::kPlatformNES &&
+			!usesMacMenu) {
 			if (!_quitByGUIPrompt && !_mainMenuIsActive) {
 				bool exitType = (event.type == Common::EVENT_RETURN_TO_LAUNCHER);
 				// If another message banner is currently on the screen, close it
@@ -300,12 +306,13 @@ void ScummEngine::parseEvent(Common::Event event) {
 				} else {
 					_closeBannerAndQueryQuitFlag = true;
 				}
-			} else if (_quitByGUIPrompt) {
+			} else if (_quitByGUIPrompt || usesMacMenu) {
 				if (!getEventManager()->shouldReturnToLauncher())
 					quitGame();
 			}
 		}
 		break;
+	}
 	default:
 		break;
 	}


Commit: fb86877ec18cfc437520e2fd408893b5f9434f51
    https://github.com/scummvm/scummvm/commit/fb86877ec18cfc437520e2fd408893b5f9434f51
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: MACGUI: Wire up some more callbacks

I wired up almost every Indy3 option (except for audio, which
might need some special handling), and I implemented the
scrollable text list in the save menu.

Changed paths:
    engines/scumm/gfx_gui.cpp
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_gui.cpp b/engines/scumm/gfx_gui.cpp
index d0c68a133cb..46fd9cf9eab 100644
--- a/engines/scumm/gfx_gui.cpp
+++ b/engines/scumm/gfx_gui.cpp
@@ -2206,10 +2206,13 @@ void ScummEngine::fillSavegameLabels() {
 	Common::String name;
 	int curSaveSlot;
 	bool isLoomVga = (_game.id == GID_LOOM && _game.version == 4);
+	bool usesMacMenu = _game.platform == Common::kPlatformMacintosh &&
+					   _game.version < 4;
+
 	_savegameNames.clear();
 
 	for (int i = 0; i < 9; i++) {
-		curSaveSlot = i + (isLoomVga ? _firstSaveStateOfList : _curDisplayedSaveSlotPage * 9);
+		curSaveSlot = i + ((isLoomVga || usesMacMenu) ? _firstSaveStateOfList : _curDisplayedSaveSlotPage * 9);
 		if (_game.version > 4 || (_game.version == 4 && _game.id == GID_LOOM)) {
 			if (availSaves[curSaveSlot]) {
 				if (getSavegameName(curSaveSlot, name)) {
diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 20059679f76..62fb8458ef3 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -3281,7 +3281,7 @@ bool MacLoomGui::runSaveDialog() {
 	window->addButton(Common::Rect(254, 159, 334, 179), "Save", true);
 	window->addButton(Common::Rect(254, 128, 334, 148), "Cancel", true);
 	window->addButton(Common::Rect(254, 83, 334, 103), "Delete", true);
-	window->addSlider(216, 9, 128, 0, 50, 7, true);
+	window->addSlider(216, 9, 128, 0, 100, 9, true);
 
 	MacGui::MacEditText *editText = window->addEditText(Common::Rect(16, 164, 229, 180), "Game file", true);
 
@@ -3301,9 +3301,17 @@ bool MacLoomGui::runSaveDialog() {
 	// When quitting, the default action is to not open a saved game
 	bool ret = false;
 
+	_vm->_firstSaveStateOfList = window->getWidgetValue(3);
+	_vm->fillSavegameLabels();
+
+	MacGui::MacStaticText *savegameNames[8];
+	for (int i = 0; i < 8; i++) {
+		savegameNames[i] = window->addStaticText(Common::Rect(16, 10 + i * 16, 200, 30 + i * 15), _vm->_savegameNames[i], true);
+	}
+
 	while (!_vm->shouldQuit()) {
 		int clicked = window->runDialog();
-
+		debug("clicked %d", clicked);
 		if (clicked == 0) {
 			ret = true;
 			break;
@@ -3315,6 +3323,16 @@ bool MacLoomGui::runSaveDialog() {
 		if (clicked == 2) {
 			runOkCancelDialog("Are you sure you want to delete the saved game?");
 		}
+
+		if (clicked == 3) {
+			_vm->_firstSaveStateOfList = window->getWidgetValue(3);
+			_vm->fillSavegameLabels();
+			for (int i = 0; i < 8; i++) {
+				savegameNames[i]->setText(_vm->_savegameNames[i]);
+				savegameNames[i]->setRedraw();
+				savegameNames[i]->draw();
+			}
+		}
 	}
 
 	delete window;
@@ -5050,11 +5068,16 @@ bool MacIndy3Gui::runOptionsDialog() {
 	// 8 - Scrolling checkbox
 	// 9 - Text speed slider (manually created)
 
-	// TODO: Get these from the SCUMM engine
-	int sound = 0;
-	int music = 0;
-	int scrolling = 0;
-	int textSpeed = 5;
+	int sound = 1;
+	int music = 1;
+	if (_vm->VAR(167) == 2) {
+		sound = music = 0;
+	} else if (_vm->VAR(167) == 1) {
+		music = 0;
+	}
+
+	int scrolling = _vm->_snapScroll == 0;
+	int textSpeed = _vm->_defaultTextSpeed;
 
 	MacDialogWindow *window = createDialog(1000);
 
@@ -5089,10 +5112,39 @@ bool MacIndy3Gui::runOptionsDialog() {
 	}
 
 	if (ret) {
-		debug("sound: %d", window->getWidgetValue(2));
-		debug("music: %d", window->getWidgetValue(3));
-		debug("scrolling: %d", window->getWidgetValue(8));
-		debug("textSpeed: %d", window->getWidgetValue(9));
+		// Update settings
+
+		// TEXT SPEED
+		_vm->_defaultTextSpeed = CLIP<int>(window->getWidgetValue(9), 0, 9);
+		ConfMan.setInt("original_gui_text_speed", _vm->_defaultTextSpeed);
+		_vm->setTalkSpeed(_vm->_defaultTextSpeed);
+
+		// SOUND&MUSIC ACTIVATION
+		// 0 - Sound&Music on
+		// 1 - Sound on, music off
+		// 2 - Sound&Music off
+		//int musicVariableValue = 0;
+		//
+		//if (window->getWidgetValue(2) == 0) {
+		//	musicVariableValue = 2;
+		//} else if (window->getWidgetValue(2) == 1 && window->getWidgetValue(3) == 0) {
+		//	musicVariableValue = 1;
+		//}
+		//
+		//_vm->VAR(167) = musicVariableValue;
+
+		if (musicVariableValue != 0) {
+			if (_vm->VAR(169) != 0) {
+				_vm->_sound->stopSound(_vm->VAR(169));
+				_vm->VAR(169) = 0;
+			}
+		}
+
+		// SCROLLING ACTIVATION
+		_vm->_snapScroll = window->getWidgetValue(8) == 0;
+
+		_vm->syncSoundSettings();
+		ConfMan.flushToDisk();
 	}
 
 	delete window;
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 704ce0cf9c6..1f337525365 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -236,6 +236,7 @@ public:
 		MacStaticText(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::String text, bool enabled) : MacWidget(window, bounds, text, true) {}
 
 		bool findWidget(int x, int y) const { return false; }
+		void setText(Common::String text) { this->_text = text; }
 		void draw(bool drawFocused = false);
 	};
 
@@ -497,7 +498,7 @@ public:
 
 	void setPalette(const byte *palette, uint size);
 	virtual bool handleEvent(Common::Event &event);
-  
+
 	static void menuCallback(int id, Common::String &name, void *data);
 	virtual void initialize();
 	void updateWindowManager();


Commit: 6909ddbf37b5849659a565684d9c8dd827a88e19
    https://github.com/scummvm/scummvm/commit/6909ddbf37b5849659a565684d9c8dd827a88e19
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: MACGUI: Fix build

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 62fb8458ef3..e4b94489915 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -5133,12 +5133,12 @@ bool MacIndy3Gui::runOptionsDialog() {
 		//
 		//_vm->VAR(167) = musicVariableValue;
 
-		if (musicVariableValue != 0) {
-			if (_vm->VAR(169) != 0) {
-				_vm->_sound->stopSound(_vm->VAR(169));
-				_vm->VAR(169) = 0;
-			}
-		}
+		//if (musicVariableValue != 0) {
+		//	if (_vm->VAR(169) != 0) {
+		//		_vm->_sound->stopSound(_vm->VAR(169));
+		//		_vm->VAR(169) = 0;
+		//	}
+		//}
 
 		// SCROLLING ACTIVATION
 		_vm->_snapScroll = window->getWidgetValue(8) == 0;


Commit: 0aefad077509a6590a804566edfb80f7421cd43f
    https://github.com/scummvm/scummvm/commit/0aefad077509a6590a804566edfb80f7421cd43f
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: MACGUI: Add scrollable list to Loom load game menu

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index e4b94489915..da4a10a67b4 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -3257,6 +3257,15 @@ bool MacLoomGui::runOpenDialog() {
 	// When quitting, the default action is to not open a saved game
 	bool ret = false;
 
+	_vm->_firstSaveStateOfList = window->getWidgetValue(3);
+	_vm->fillSavegameLabels();
+
+	MacGui::MacStaticText *savegameNames[8];
+	for (int i = 0; i < 8; i++) {
+		savegameNames[i] = window->addStaticText(Common::Rect(16, 15 + i * 16, 200, 35 + i * 15), _vm->_savegameNames[i], true);
+	}
+
+
 	while (!_vm->shouldQuit()) {
 		int clicked = window->runDialog();
 
@@ -3267,6 +3276,16 @@ bool MacLoomGui::runOpenDialog() {
 
 		if (clicked == 1)
 			break;
+
+		if (clicked == 3) {
+			_vm->_firstSaveStateOfList = window->getWidgetValue(3);
+			_vm->fillSavegameLabels();
+			for (int i = 0; i < 8; i++) {
+				savegameNames[i]->setText(_vm->_savegameNames[i]);
+				savegameNames[i]->setRedraw();
+				savegameNames[i]->draw();
+			}
+		}
 	}
 
 	delete window;


Commit: 8254e3a0f83b20bab522cbc7e6290f94650d3416
    https://github.com/scummvm/scummvm/commit/8254e3a0f83b20bab522cbc7e6290f94650d3416
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: MACGUI: Implement temporary workaround for sound options

This is temporary because apparently muting sounds should be done
engine side, instead of mixer side.

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index da4a10a67b4..aa2f2f304c3 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -5087,13 +5087,8 @@ bool MacIndy3Gui::runOptionsDialog() {
 	// 8 - Scrolling checkbox
 	// 9 - Text speed slider (manually created)
 
-	int sound = 1;
-	int music = 1;
-	if (_vm->VAR(167) == 2) {
-		sound = music = 0;
-	} else if (_vm->VAR(167) == 1) {
-		music = 0;
-	}
+	int sound = _vm->_mixer->isSoundTypeMuted(Audio::Mixer::SoundType::kSFXSoundType) ? 0 : 1;
+	int music = _vm->_mixer->isSoundTypeMuted(Audio::Mixer::SoundType::kPlainSoundType) ? 0 : 1;
 
 	int scrolling = _vm->_snapScroll == 0;
 	int textSpeed = _vm->_defaultTextSpeed;
@@ -5137,32 +5132,20 @@ bool MacIndy3Gui::runOptionsDialog() {
 		_vm->_defaultTextSpeed = CLIP<int>(window->getWidgetValue(9), 0, 9);
 		ConfMan.setInt("original_gui_text_speed", _vm->_defaultTextSpeed);
 		_vm->setTalkSpeed(_vm->_defaultTextSpeed);
+		_vm->syncSoundSettings();
 
 		// SOUND&MUSIC ACTIVATION
 		// 0 - Sound&Music on
 		// 1 - Sound on, music off
 		// 2 - Sound&Music off
-		//int musicVariableValue = 0;
-		//
-		//if (window->getWidgetValue(2) == 0) {
-		//	musicVariableValue = 2;
-		//} else if (window->getWidgetValue(2) == 1 && window->getWidgetValue(3) == 0) {
-		//	musicVariableValue = 1;
-		//}
-		//
-		//_vm->VAR(167) = musicVariableValue;
-
-		//if (musicVariableValue != 0) {
-		//	if (_vm->VAR(169) != 0) {
-		//		_vm->_sound->stopSound(_vm->VAR(169));
-		//		_vm->VAR(169) = 0;
-		//	}
-		//}
+		bool disableSound = window->getWidgetValue(2) == 0;
+		bool disableMusic = window->getWidgetValue(3) == 0;
+		_vm->_mixer->muteSoundType(Audio::Mixer::SoundType::kSFXSoundType, disableSound);
+		_vm->_mixer->muteSoundType(Audio::Mixer::SoundType::kPlainSoundType, disableMusic || disableSound);
 
 		// SCROLLING ACTIVATION
 		_vm->_snapScroll = window->getWidgetValue(8) == 0;
 
-		_vm->syncSoundSettings();
 		ConfMan.flushToDisk();
 	}
 


Commit: d549325a7e379ff4abc49b248ef55b9387060573
    https://github.com/scummvm/scummvm/commit/d549325a7e379ff4abc49b248ef55b9387060573
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: MACGUI: Wire up quitGame() for Indy3

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index aa2f2f304c3..e691c8dbf10 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -4739,7 +4739,7 @@ bool MacIndy3Gui::handleMenu(int id, Common::String &name) {
 
 	case 206:	// Quit
 		if (runQuitDialog())
-			debug("Game should quit now");
+			_vm->quitGame();
 		break;
 
 	default:


Commit: f57f2b3af5c897491176b3f6d695051e66a197dd
    https://github.com/scummvm/scummvm/commit/f57f2b3af5c897491176b3f6d695051e66a197dd
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: MACGUI: Add MacListBox and add test code for Open Dialog

MacListBox code, courtesy by eriktorbjorn!

Changed paths:
    engines/scumm/gfx_gui.cpp
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h
    engines/scumm/scumm.h


diff --git a/engines/scumm/gfx_gui.cpp b/engines/scumm/gfx_gui.cpp
index 46fd9cf9eab..7b9a2edbf7a 100644
--- a/engines/scumm/gfx_gui.cpp
+++ b/engines/scumm/gfx_gui.cpp
@@ -2206,13 +2206,11 @@ void ScummEngine::fillSavegameLabels() {
 	Common::String name;
 	int curSaveSlot;
 	bool isLoomVga = (_game.id == GID_LOOM && _game.version == 4);
-	bool usesMacMenu = _game.platform == Common::kPlatformMacintosh &&
-					   _game.version < 4;
 
 	_savegameNames.clear();
 
 	for (int i = 0; i < 9; i++) {
-		curSaveSlot = i + ((isLoomVga || usesMacMenu) ? _firstSaveStateOfList : _curDisplayedSaveSlotPage * 9);
+		curSaveSlot = i + (isLoomVga ? _firstSaveStateOfList : _curDisplayedSaveSlotPage * 9);
 		if (_game.version > 4 || (_game.version == 4 && _game.id == GID_LOOM)) {
 			if (availSaves[curSaveSlot]) {
 				if (getSavegameName(curSaveSlot, name)) {
diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index e691c8dbf10..ef71c297999 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -526,8 +526,8 @@ void MacGui::MacStaticText::draw(bool drawFocused) {
 
 	debug(1, "MacGui::MacStaticText: Drawing text %d (_fullRedraw = %d, drawFocused = %d, _value = %d)", _id, _fullRedraw, drawFocused, _value);
 
-	_window->innerSurface()->fillRect(_bounds, kWhite);
-	drawText(_text, _bounds.left, _bounds.top, _bounds.width(), kBlack, Graphics::kTextAlignLeft, 1);
+	_window->innerSurface()->fillRect(_bounds, _bg);
+	drawText(_text, _bounds.left, _bounds.top, _bounds.width(), _fg, Graphics::kTextAlignLeft, 1);
 	_window->markRectAsDirty(_bounds);
 
 	_redraw = false;
@@ -1505,6 +1505,183 @@ void MacGui::MacPictureSlider::handleWheelDown() {
 	}
 }
 
+// ---------------------------------------------------------------------------
+// List box widget
+// ---------------------------------------------------------------------------
+
+MacGui::MacListBox::MacListBox(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::StringArray texts, bool enabled) : MacWidget(window, bounds, "ListBox", enabled), _texts(texts) {
+	int pageSize = _bounds.height() / 16;
+
+	int numSlots = MIN<int>(pageSize, texts.size());
+
+	for (int i = 0; i < numSlots; i++) {
+		Common::Rect r(_bounds.left + 1, _bounds.top + 1 + 16 * i, _bounds.right - 17, _bounds.top + 1 + 16 * (i + 1));
+		_textWidgets.push_back(new MacStaticText(window, r, texts[i], enabled));
+	}
+
+	_slider = new MacSlider(window, Common::Rect(_bounds.right - 16, _bounds.top, _bounds.right, _bounds.bottom), 0, texts.size() - pageSize, pageSize, enabled);
+
+	// The widget value indicates the selected element
+	_value = 0;
+	updateTexts();
+}
+
+MacGui::MacListBox::~MacListBox() {
+	_texts.clear();
+	delete _slider;
+
+	for (uint i = 0; i < _textWidgets.size(); i++)
+		delete _textWidgets[i];
+}
+
+bool MacGui::MacListBox::findWidget(int x, int y) const {
+	return MacWidget::findWidget(x, y) || _slider->findWidget(x, y);
+}
+
+void MacGui::MacListBox::setRedraw(bool fullRedraw) {
+	MacWidget::setRedraw(fullRedraw);
+	_slider->setRedraw(fullRedraw);
+
+	for (uint i = 0; i < _textWidgets.size(); i++)
+		_textWidgets[i]->setRedraw(fullRedraw);
+}
+
+void MacGui::MacListBox::updateTexts() {
+	int offset = _slider->getValue();
+
+	for (uint i = 0; i < _textWidgets.size(); i++) {
+		_textWidgets[i]->setText(_texts[i + offset]);
+
+		if ((int)(i + offset) == _value)
+			_textWidgets[i]->setColor(kWhite, kBlack);
+		else
+			_textWidgets[i]->setColor(kBlack, kWhite);
+	}
+}
+
+void MacGui::MacListBox::draw(bool drawFocused) {
+	for (uint i = 0; i < _textWidgets.size(); i++)
+		_textWidgets[i]->draw(drawFocused);
+
+	_slider->draw(drawFocused);
+
+	if (!_redraw && !_fullRedraw)
+		return;
+
+	debug(1, "MacGui::MacListBox: Drawing list box (_fullRedraw = %d, drawFocused = %d)", _fullRedraw, drawFocused);
+
+	Graphics::Surface *s = _window->innerSurface();
+
+	s->hLine(_bounds.left, _bounds.top, _bounds.right - 17, kBlack);
+	s->hLine(_bounds.left, _bounds.bottom - 1, _bounds.right - 17, kBlack);
+	s->vLine(_bounds.left, _bounds.top + 1, _bounds.bottom - 2, kBlack);
+
+	_redraw = false;
+	_fullRedraw = false;
+
+	_window->markRectAsDirty(_bounds);
+}
+
+void MacGui::MacListBox::handleMouseDown(Common::Event &event) {
+	Common::Point mousePos = _window->getMousePos();
+
+	if (_slider->findWidget(mousePos.x, mousePos.y)) {
+		int oldValue = _slider->getValue();
+
+		_sliderFocused = true;
+		_slider->handleMouseDown(event);
+
+		if (_slider->getValue() != oldValue)
+			updateTexts();
+
+		return;
+	}
+
+	int offset = _slider->getValue();
+
+	for (uint i = 0; i < _textWidgets.size(); i++) {
+		if (_textWidgets[i]->findWidget(mousePos.x, mousePos.y)) {
+			setValue(i + offset);
+			break;
+		}
+	}
+}
+
+void MacGui::MacListBox::handleMouseUp(Common::Event &event) {
+	if (_sliderFocused) {
+		int oldValue = _slider->getValue();
+
+		_sliderFocused = false;
+		_slider->handleMouseUp(event);
+
+		if (_slider->getValue() != oldValue)
+			updateTexts();
+	}
+}
+
+void MacGui::MacListBox::handleMouseMove(Common::Event &event) {
+	if (_sliderFocused) {
+		int oldValue = _slider->getValue();
+
+		_slider->handleMouseMove(event);
+
+		if (_slider->getValue() != oldValue)
+			updateTexts();
+	}
+}
+
+void MacGui::MacListBox::handleMouseHeld() {
+	if (_sliderFocused) {
+		int oldValue = _slider->getValue();
+
+		_slider->handleMouseHeld();
+
+		if (_slider->getValue() != oldValue)
+			updateTexts();
+	}
+}
+
+void MacGui::MacListBox::handleWheelUp() {
+	Common::Point mousePos = _window->getMousePos();
+
+	if (_slider->findWidget(mousePos.x, mousePos.y)) {
+		_slider->handleWheelUp();
+		updateTexts();
+		return;
+	}
+
+	int oldValue = _slider->getValue();
+
+	_slider->setValue(oldValue - 1);
+
+	// FIXME: This is not the most efficient way of redrawing the slider
+
+	if (_slider->getValue() != oldValue) {
+		_slider->setRedraw(true);
+		updateTexts();
+	}
+}
+
+void MacGui::MacListBox::handleWheelDown() {
+	Common::Point mousePos = _window->getMousePos();
+
+	if (_slider->findWidget(mousePos.x, mousePos.y)) {
+		_slider->handleWheelDown();
+		return;
+	}
+
+	int oldValue = _slider->getValue();
+
+	_slider->setValue(oldValue + 1);
+
+	// FIXME: This is not the most efficient way of redrawing the slider
+
+	if (_slider->getValue() != oldValue) {
+		_slider->setRedraw(true);
+		updateTexts();
+	}
+}
+
 // ---------------------------------------------------------------------------
 // Dialog window
 //
@@ -1675,6 +1852,12 @@ void MacGui::MacDialogWindow::markRectAsDirty(Common::Rect r) {
 	_dirtyRects.push_back(r);
 }
 
+MacGui::MacListBox *MacGui::MacDialogWindow::addListBox(Common::Rect bounds, Common::StringArray texts, bool enabled) {
+	MacGui::MacListBox *listBox = new MacListBox(this, bounds, texts, enabled);
+	_widgets.push_back(listBox);
+	return listBox;
+}
+
 void MacGui::MacDialogWindow::drawBeamCursor() {
 	int x0 = _beamCursorPos.x - _beamCursorHotspotX;
 	int y0 = _beamCursorPos.y - _beamCursorHotspotY;
@@ -2334,6 +2517,8 @@ bool MacGui::handleMenu(int id, Common::String &name) {
 		return true;
 
 	case 203:	// Pause
+		if (!_vm->_messageBannerActive)
+			_vm->mac_showOldStyleBannerAndPause(_vm->getGUIString(gsPause), -1);
 		return true;
 
 	// In the original, the Edit menu is active during save dialogs, though
@@ -3245,26 +3430,39 @@ bool MacLoomGui::runOpenDialog() {
 	window->addButton(Common::Rect(254, 135, 334, 155), "Open", true);
 	window->addButton(Common::Rect(254, 104, 334, 124), "Cancel", true);
 	window->addButton(Common::Rect(254, 59, 334, 79), "Delete", true);
-	window->addSlider(216, 13, 146, 0, 50, 7, true);
 
-	Graphics::Surface *s = window->innerSurface();
+	bool availSaves[100];
+	int saveIds[100];
+	int saveCounter = 0;
 
-	s->frameRect(Common::Rect(14, 13, 217, 159), kBlack);
-	s->hLine(253, 91, 334, kBlack);
+	for (int i = 0; i < ARRAYSIZE(saveIds); i++) {
+		saveIds[i] = -1;
+	}
 
-	window->setDefaultWidget(0);
+	Common::String name;
+	Common::StringArray savegameNames;
+	_vm->listSavegames(availSaves, ARRAYSIZE(availSaves));
 
-	// When quitting, the default action is to not open a saved game
-	bool ret = false;
+	for (int i = 0; i < ARRAYSIZE(availSaves); i++) {
+		if (availSaves[i]) {
+			// Save the slot ids for slots which actually contain savegames...
+			saveIds[saveCounter] = i;
+			saveCounter++;
+			if (_vm->getSavegameName(i, name)) {
+				savegameNames.push_back(Common::String::format("%s", name.c_str()));
+			} else {
+				// The original printed "WARNING... old savegame", but we do support old savegames :-)
+				savegameNames.push_back(Common::String::format("%s", "WARNING: wrong save version"));
+			}
+		}
+	}
 
-	_vm->_firstSaveStateOfList = window->getWidgetValue(3);
-	_vm->fillSavegameLabels();
+	window->addListBox(Common::Rect(14, 13, 217, 159), savegameNames, true);
 
-	MacGui::MacStaticText *savegameNames[8];
-	for (int i = 0; i < 8; i++) {
-		savegameNames[i] = window->addStaticText(Common::Rect(16, 15 + i * 16, 200, 35 + i * 15), _vm->_savegameNames[i], true);
-	}
+	window->setDefaultWidget(0);
 
+	// When quitting, the default action is to not open a saved game
+	bool ret = false;
 
 	while (!_vm->shouldQuit()) {
 		int clicked = window->runDialog();
@@ -3276,16 +3474,6 @@ bool MacLoomGui::runOpenDialog() {
 
 		if (clicked == 1)
 			break;
-
-		if (clicked == 3) {
-			_vm->_firstSaveStateOfList = window->getWidgetValue(3);
-			_vm->fillSavegameLabels();
-			for (int i = 0; i < 8; i++) {
-				savegameNames[i]->setText(_vm->_savegameNames[i]);
-				savegameNames[i]->setRedraw();
-				savegameNames[i]->draw();
-			}
-		}
 	}
 
 	delete window;
@@ -5346,7 +5534,10 @@ bool MacIndy3Gui::handleEvent(Common::Event &event) {
 	if (MacGui::handleEvent(event))
 		return true;
 
-	if (!isVerbGuiActive() || _vm->_userPut <= 0)
+	bool isPauseEvent = event.type == Common::EVENT_KEYDOWN &&
+		event.kbd == Common::KEYCODE_SPACE;
+
+	if (!isPauseEvent && (!isVerbGuiActive() || _vm->_userPut <= 0))
 		return false;
 
 	if (event.type == Common::EVENT_LBUTTONDOWN) {
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 1f337525365..2d05cb092c6 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -179,7 +179,7 @@ public:
 		virtual void getFocus() { setRedraw(); }
 		virtual void loseFocus() { setRedraw(); }
 
-		void setRedraw(bool fullRedraw = false);
+		virtual void setRedraw(bool fullRedraw = false);
 
 		void setEnabled(bool enabled);
 
@@ -232,11 +232,31 @@ public:
 	// custom findWidget().
 
 	class MacStaticText : public MacWidget {
+	private:
+		Color _fg = kBlack;
+		Color _bg = kWhite;
+
 	public:
 		MacStaticText(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::String text, bool enabled) : MacWidget(window, bounds, text, true) {}
 
-		bool findWidget(int x, int y) const { return false; }
-		void setText(Common::String text) { this->_text = text; }
+		void getFocus() {}
+		void loseFocus() {}
+
+		void setText(Common::String text) {
+			if (text != _text) {
+				_text = text;
+				setRedraw();
+			}
+		}
+
+		void setColor(Color fg, Color bg) {
+			if (fg != _fg || bg != _bg) {
+				_fg = fg;
+				_bg = bg;
+				setRedraw();
+			}
+		}
+
 		void draw(bool drawFocused = false);
 	};
 
@@ -388,6 +408,45 @@ public:
 		void handleWheelDown();
 	};
 
+	class MacListBox : public MacWidget {
+	private:
+		Common::StringArray _texts;
+		Common::Array<MacStaticText *> _textWidgets;
+		MacSlider *_slider;
+		bool _sliderFocused = false;
+
+		void updateTexts();
+
+	public:
+		MacListBox(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::StringArray texts, bool enabled);
+		~MacListBox();
+
+		void getFocus() {}
+		void loseFocus() {}
+
+		void setValue(int value) {
+			if (value != _value) {
+				_value = value;
+				updateTexts();
+			}
+		}
+
+		int getValue() {
+			return _value;
+		}
+
+		bool findWidget(int x, int y) const;
+		void setRedraw(bool fullRedraw = false);
+		void draw(bool drawFocused = false);
+
+		void handleMouseDown(Common::Event &event);
+		void handleMouseUp(Common::Event &event);
+		void handleMouseMove(Common::Event &event);
+		void handleMouseHeld();
+		void handleWheelUp();
+		void handleWheelDown();
+	};
+
 	class MacDialogWindow {
 	private:
 		Common::Rect _bounds;
@@ -468,6 +527,7 @@ public:
 		MacGui::MacPicture *addPicture(Common::Rect bounds, int id, bool enabled);
 		MacGui::MacSlider *addSlider(int x, int y, int h, int minValue, int maxValue, int pageSize, bool enabled);
 		MacGui::MacPictureSlider *addPictureSlider(int backgroundId, int handleId, bool enabled, int minX, int maxX, int minValue, int maxValue, int leftMargin = 0, int rightMargin = 0);
+		MacGui::MacListBox *addListBox(Common::Rect bounds, Common::StringArray texts, bool enabled);
 
 		void addSubstitution(Common::String text) { _substitutions.push_back(text); }
 		void replaceSubstitution(int nr, Common::String text) { _substitutions[nr] = text; }
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index 2bc646c74a3..2d55001d0cb 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -522,6 +522,7 @@ class ScummEngine : public Engine, public Common::Serializable {
 	friend class CharsetRendererClassic;
 	friend class CharsetRendererTownsClassic;
 	friend class ResourceManager;
+	friend class MacGui;
 	friend class MacIndy3Gui;
 	friend class MacLoomGui;
 


Commit: 1eccfc43819e4b905a281f8a65fe9c05822359d0
    https://github.com/scummvm/scummvm/commit/1eccfc43819e4b905a281f8a65fe9c05822359d0
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: MACGUI: Implement saving and loading for Loom

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index ef71c297999..78048036918 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -1509,14 +1509,20 @@ void MacGui::MacPictureSlider::handleWheelDown() {
 // List box widget
 // ---------------------------------------------------------------------------
 
-MacGui::MacListBox::MacListBox(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::StringArray texts, bool enabled) : MacWidget(window, bounds, "ListBox", enabled), _texts(texts) {
+MacGui::MacListBox::MacListBox(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::StringArray texts, bool enabled, bool contentUntouchable) : MacWidget(window, bounds, "ListBox", enabled), _texts(texts) {
 	int pageSize = _bounds.height() / 16;
 
 	int numSlots = MIN<int>(pageSize, texts.size());
 
+	_untouchableText = contentUntouchable;
+	MacStaticText *tmp;
 	for (int i = 0; i < numSlots; i++) {
 		Common::Rect r(_bounds.left + 1, _bounds.top + 1 + 16 * i, _bounds.right - 17, _bounds.top + 1 + 16 * (i + 1));
-		_textWidgets.push_back(new MacStaticText(window, r, texts[i], enabled));
+		tmp = new MacStaticText(window, r, texts[i], enabled);
+		if (contentUntouchable)
+			tmp->setColor(kLightGray, kWhite);
+
+		_textWidgets.push_back(tmp);
 	}
 
 	_slider = new MacSlider(window, Common::Rect(_bounds.right - 16, _bounds.top, _bounds.right, _bounds.bottom), 0, texts.size() - pageSize, pageSize, enabled);
@@ -1547,6 +1553,9 @@ void MacGui::MacListBox::setRedraw(bool fullRedraw) {
 }
 
 void MacGui::MacListBox::updateTexts() {
+	if (_untouchableText)
+		return;
+
 	int offset = _slider->getValue();
 
 	for (uint i = 0; i < _textWidgets.size(); i++) {
@@ -1852,8 +1861,8 @@ void MacGui::MacDialogWindow::markRectAsDirty(Common::Rect r) {
 	_dirtyRects.push_back(r);
 }
 
-MacGui::MacListBox *MacGui::MacDialogWindow::addListBox(Common::Rect bounds, Common::StringArray texts, bool enabled) {
-	MacGui::MacListBox *listBox = new MacListBox(this, bounds, texts, enabled);
+MacGui::MacListBox *MacGui::MacDialogWindow::addListBox(Common::Rect bounds, Common::StringArray texts, bool enabled, bool contentUntouchable) {
+	MacGui::MacListBox *listBox = new MacListBox(this, bounds, texts, enabled, contentUntouchable);
 	_widgets.push_back(listBox);
 	return listBox;
 }
@@ -2495,6 +2504,8 @@ bool MacGui::handleMenu(int id, Common::String &name) {
 	// to replicate that effect.
 
 	_windowManager->getMenu()->closeMenu();
+	int saveSlotToHandle = -1;
+	Common::String savegameName;
 
 	switch (id) {
 	case 100:	// About
@@ -2502,12 +2513,20 @@ bool MacGui::handleMenu(int id, Common::String &name) {
 		return true;
 
 	case 200:	// Open
-		if (runOpenDialog())
-			debug("Open a saved game");
+		if (runOpenDialog(saveSlotToHandle)) {
+			if (saveSlotToHandle > -1) {
+				_vm->loadGameState(saveSlotToHandle);
+			}
+		}
+
 		return true;
 
 	case 201:	// Save
-		if (runSaveDialog())
+		if (runSaveDialog(saveSlotToHandle, savegameName)) {
+			if (saveSlotToHandle > -1) {
+				_vm->saveGameState(saveSlotToHandle, savegameName);
+			}
+		}
 			debug("Save a game");
 		return true;
 
@@ -2977,6 +2996,31 @@ MacGui::MacDialogWindow *MacGui::createDialog(int dialogId) {
 // Standard dialogs
 // ---------------------------------------------------------------------------
 
+void MacGui::prepareSaveLoad(Common::StringArray &savegameNames, bool *availSlots, int *slotIds, int size) {
+	int saveCounter = 0;
+
+	for (int i = 0; i < size; i++) {
+		slotIds[i] = -1;
+	}
+
+	Common::String name;
+	_vm->listSavegames(availSlots, size);
+
+	for (int i = 0; i < size; i++) {
+		if (availSlots[i]) {
+			// Save the slot ids for slots which actually contain savegames...
+			slotIds[saveCounter] = i;
+			saveCounter++;
+			if (_vm->getSavegameName(i, name)) {
+				savegameNames.push_back(Common::String::format("%s", name.c_str()));
+			} else {
+				// The original printed "WARNING... old savegame", but we do support old savegames :-)
+				savegameNames.push_back(Common::String::format("%s", "WARNING: wrong save version"));
+			}
+		}
+	}
+}
+
 bool MacGui::runOkCancelDialog(Common::String text) {
 	// Widgets:
 	//
@@ -3422,7 +3466,7 @@ void MacLoomGui::runAboutDialog() {
 // A standard file picker dialog doesn't really make sense in ScummVM, so we
 // make something that just looks similar to one.
 
-bool MacLoomGui::runOpenDialog() {
+bool MacLoomGui::runOpenDialog(int &saveSlotToHandle) {
 	Common::Rect bounds(88, 28, 448, 208);
 
 	MacDialogWindow *window = createWindow(bounds);
@@ -3431,31 +3475,10 @@ bool MacLoomGui::runOpenDialog() {
 	window->addButton(Common::Rect(254, 104, 334, 124), "Cancel", true);
 	window->addButton(Common::Rect(254, 59, 334, 79), "Delete", true);
 
-	bool availSaves[100];
-	int saveIds[100];
-	int saveCounter = 0;
-
-	for (int i = 0; i < ARRAYSIZE(saveIds); i++) {
-		saveIds[i] = -1;
-	}
-
-	Common::String name;
+	bool availSlots[100];
+	int slotIds[100];
 	Common::StringArray savegameNames;
-	_vm->listSavegames(availSaves, ARRAYSIZE(availSaves));
-
-	for (int i = 0; i < ARRAYSIZE(availSaves); i++) {
-		if (availSaves[i]) {
-			// Save the slot ids for slots which actually contain savegames...
-			saveIds[saveCounter] = i;
-			saveCounter++;
-			if (_vm->getSavegameName(i, name)) {
-				savegameNames.push_back(Common::String::format("%s", name.c_str()));
-			} else {
-				// The original printed "WARNING... old savegame", but we do support old savegames :-)
-				savegameNames.push_back(Common::String::format("%s", "WARNING: wrong save version"));
-			}
-		}
-	}
+	prepareSaveLoad(savegameNames, availSlots, slotIds, ARRAYSIZE(availSlots));
 
 	window->addListBox(Common::Rect(14, 13, 217, 159), savegameNames, true);
 
@@ -3474,13 +3497,19 @@ bool MacLoomGui::runOpenDialog() {
 
 		if (clicked == 1)
 			break;
+
+		if (clicked == 3) {
+			saveSlotToHandle =
+				window->getWidgetValue(3) < ARRAYSIZE(slotIds) ?
+				slotIds[window->getWidgetValue(3)] : -1;
+		}
 	}
 
 	delete window;
 	return ret;
 }
 
-bool MacLoomGui::runSaveDialog() {
+bool MacLoomGui::runSaveDialog(int &saveSlotToHandle, Common::String &name) {
 	Common::Rect bounds(110, 27, 470, 231);
 
 	MacDialogWindow *window = createWindow(bounds);
@@ -3488,7 +3517,19 @@ bool MacLoomGui::runSaveDialog() {
 	window->addButton(Common::Rect(254, 159, 334, 179), "Save", true);
 	window->addButton(Common::Rect(254, 128, 334, 148), "Cancel", true);
 	window->addButton(Common::Rect(254, 83, 334, 103), "Delete", true);
-	window->addSlider(216, 9, 128, 0, 100, 9, true);
+	bool busySlots[100];
+	int slotIds[100];
+	Common::StringArray savegameNames;
+	prepareSaveLoad(savegameNames, busySlots, slotIds, ARRAYSIZE(busySlots));
+	int firstAvailableSlot = -1;
+	for (int i = 0; i < ARRAYSIZE(busySlots); i++) {
+		if (!busySlots[i]) {
+			firstAvailableSlot = i;
+			break;
+		}
+	}
+
+	window->addListBox(Common::Rect(14, 9, 217, 137), savegameNames, true, true);
 
 	MacGui::MacEditText *editText = window->addEditText(Common::Rect(16, 164, 229, 180), "Game file", true);
 
@@ -3496,7 +3537,6 @@ bool MacLoomGui::runSaveDialog() {
 	const Graphics::Font *font = getFont(kSystemFont);
 
 	s->frameRect(Common::Rect(14, 161, 232, 183), kBlack);
-	s->frameRect(Common::Rect(14, 9, 217, 137), kBlack);
 
 	s->hLine(253, 115, 334, kBlack);
 
@@ -3508,19 +3548,13 @@ bool MacLoomGui::runSaveDialog() {
 	// When quitting, the default action is to not open a saved game
 	bool ret = false;
 
-	_vm->_firstSaveStateOfList = window->getWidgetValue(3);
-	_vm->fillSavegameLabels();
-
-	MacGui::MacStaticText *savegameNames[8];
-	for (int i = 0; i < 8; i++) {
-		savegameNames[i] = window->addStaticText(Common::Rect(16, 10 + i * 16, 200, 30 + i * 15), _vm->_savegameNames[i], true);
-	}
-
 	while (!_vm->shouldQuit()) {
 		int clicked = window->runDialog();
 		debug("clicked %d", clicked);
 		if (clicked == 0) {
 			ret = true;
+			name = editText->getText();
+			saveSlotToHandle = firstAvailableSlot;
 			break;
 		}
 
@@ -3530,16 +3564,6 @@ bool MacLoomGui::runSaveDialog() {
 		if (clicked == 2) {
 			runOkCancelDialog("Are you sure you want to delete the saved game?");
 		}
-
-		if (clicked == 3) {
-			_vm->_firstSaveStateOfList = window->getWidgetValue(3);
-			_vm->fillSavegameLabels();
-			for (int i = 0; i < 8; i++) {
-				savegameNames[i]->setText(_vm->_savegameNames[i]);
-				savegameNames[i]->setRedraw();
-				savegameNames[i]->draw();
-			}
-		}
 	}
 
 	delete window;
@@ -5177,7 +5201,7 @@ void MacIndy3Gui::clearAboutDialog(MacDialogWindow *window) {
 	window->fillPattern(Common::Rect(2, 136, s->w - 2, s->h - 4), 0xA5A5);
 }
 
-bool MacIndy3Gui::runOpenDialog() {
+bool MacIndy3Gui::runOpenDialog(int &saveSlotToHandle) {
 	// Widgets:
 	//
 	// 0 - Open button
@@ -5220,7 +5244,7 @@ bool MacIndy3Gui::runOpenDialog() {
 	return ret;
 }
 
-bool MacIndy3Gui::runSaveDialog() {
+bool MacIndy3Gui::runSaveDialog(int &saveSlotToHandle, Common::String &name) {
 	// Widgets:
 	//
 	// 0 - Save button
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 2d05cb092c6..c02ff8ef76b 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -125,9 +125,10 @@ protected:
 	virtual bool handleMenu(int id, Common::String &name);
 
 	virtual void runAboutDialog() = 0;
-	virtual bool runOpenDialog() = 0;
-	virtual bool runSaveDialog() = 0;
+	virtual bool runOpenDialog(int &saveSlotToHandle) = 0;
+	virtual bool runSaveDialog(int &saveSlotToHandle, Common::String &name) = 0;
 	virtual bool runOptionsDialog() = 0;
+	void prepareSaveLoad(Common::StringArray &savegameNames, bool *availSlots, int *slotIds, int size);
 
 	bool runOkCancelDialog(Common::String text);
 	bool runQuitDialog();
@@ -289,6 +290,8 @@ public:
 		void getFocus() {}
 		void loseFocus() {}
 
+		Common::String getText() { return _text; }
+
 		void selectAll();
 
 		bool useBeamCursor() { return true; }
@@ -414,11 +417,12 @@ public:
 		Common::Array<MacStaticText *> _textWidgets;
 		MacSlider *_slider;
 		bool _sliderFocused = false;
+		bool _untouchableText = false;
 
 		void updateTexts();
 
 	public:
-		MacListBox(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::StringArray texts, bool enabled);
+		MacListBox(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::StringArray texts, bool enabled, bool contentUntouchable = true);
 		~MacListBox();
 
 		void getFocus() {}
@@ -527,7 +531,7 @@ public:
 		MacGui::MacPicture *addPicture(Common::Rect bounds, int id, bool enabled);
 		MacGui::MacSlider *addSlider(int x, int y, int h, int minValue, int maxValue, int pageSize, bool enabled);
 		MacGui::MacPictureSlider *addPictureSlider(int backgroundId, int handleId, bool enabled, int minX, int maxX, int minValue, int maxValue, int leftMargin = 0, int rightMargin = 0);
-		MacGui::MacListBox *addListBox(Common::Rect bounds, Common::StringArray texts, bool enabled);
+		MacGui::MacListBox *addListBox(Common::Rect bounds, Common::StringArray texts, bool enabled, bool contentUntouchable = false);
 
 		void addSubstitution(Common::String text) { _substitutions.push_back(text); }
 		void replaceSubstitution(int nr, Common::String text) { _substitutions[nr] = text; }
@@ -611,8 +615,8 @@ protected:
 	bool handleMenu(int id, Common::String &name);
 
 	void runAboutDialog();
-	bool runOpenDialog();
-	bool runSaveDialog();
+	bool runOpenDialog(int &saveSlotToHandle);
+	bool runSaveDialog(int &saveSlotToHandle, Common::String &name);
 	bool runOptionsDialog();
 
 private:
@@ -667,8 +671,8 @@ protected:
 	bool handleMenu(int id, Common::String &name);
 
 	void runAboutDialog();
-	bool runOpenDialog();
-	bool runSaveDialog();
+	bool runOpenDialog(int &saveSlotToHandle);
+	bool runSaveDialog(int &saveSlotToHandle, Common::String &name);
 	bool runOptionsDialog();
 	bool runIqPointsDialog();
 


Commit: 35acda4e341b9154e78aad8ec50f2eb1b3e025ba
    https://github.com/scummvm/scummvm/commit/35acda4e341b9154e78aad8ec50f2eb1b3e025ba
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: MACGUI: Cleanup

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 78048036918..d57c3bbf3af 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -2527,7 +2527,7 @@ bool MacGui::handleMenu(int id, Common::String &name) {
 				_vm->saveGameState(saveSlotToHandle, savegameName);
 			}
 		}
-			debug("Save a game");
+
 		return true;
 
 	case 202:	// Restart
@@ -3183,8 +3183,7 @@ bool MacLoomGui::handleMenu(int id, Common::String &name) {
 
 	switch (id) {
 	case 204:	// Options
-		if (runOptionsDialog())
-			debug("Options should be applied now");
+		runOptionsDialog();
 		break;
 
 	case 205:	// Quit
@@ -3498,6 +3497,11 @@ bool MacLoomGui::runOpenDialog(int &saveSlotToHandle) {
 		if (clicked == 1)
 			break;
 
+		if (clicked == 2) {
+			if (runOkCancelDialog("Are you sure you want to delete the saved game?"))
+				runOkCancelDialog("Deleting savegames is currently unsupported in ScummVM.");
+		}
+
 		if (clicked == 3) {
 			saveSlotToHandle =
 				window->getWidgetValue(3) < ARRAYSIZE(slotIds) ?
@@ -3516,11 +3520,13 @@ bool MacLoomGui::runSaveDialog(int &saveSlotToHandle, Common::String &name) {
 
 	window->addButton(Common::Rect(254, 159, 334, 179), "Save", true);
 	window->addButton(Common::Rect(254, 128, 334, 148), "Cancel", true);
-	window->addButton(Common::Rect(254, 83, 334, 103), "Delete", true);
+	window->addButton(Common::Rect(254, 83, 334, 103), "Delete", false);
+
 	bool busySlots[100];
 	int slotIds[100];
 	Common::StringArray savegameNames;
 	prepareSaveLoad(savegameNames, busySlots, slotIds, ARRAYSIZE(busySlots));
+
 	int firstAvailableSlot = -1;
 	for (int i = 0; i < ARRAYSIZE(busySlots); i++) {
 		if (!busySlots[i]) {
@@ -3550,7 +3556,7 @@ bool MacLoomGui::runSaveDialog(int &saveSlotToHandle, Common::String &name) {
 
 	while (!_vm->shouldQuit()) {
 		int clicked = window->runDialog();
-		debug("clicked %d", clicked);
+
 		if (clicked == 0) {
 			ret = true;
 			name = editText->getText();
@@ -3560,10 +3566,6 @@ bool MacLoomGui::runSaveDialog(int &saveSlotToHandle, Common::String &name) {
 
 		if (clicked == 1)
 			break;
-
-		if (clicked == 2) {
-			runOkCancelDialog("Are you sure you want to delete the saved game?");
-		}
 	}
 
 	delete window;


Commit: bd485e25d036982f681f57db023001a62f1cea46
    https://github.com/scummvm/scummvm/commit/bd485e25d036982f681f57db023001a62f1cea46
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Fix bugs in Mac list box and slider widgets

The worst one was that the scroll wheel could crash ScummVM. The rest is
mostly cosmetical.

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index d57c3bbf3af..572f7929a52 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -1022,7 +1022,7 @@ MacGui::MacSlider::MacSlider(MacGui::MacDialogWindow *window, Common::Rect bound
 }
 
 bool MacGui::MacSlider::findWidget(int x, int y) const {
-	if (_maxValue - _minValue <= _pageSize)
+	if (!isScrollable())
 		return false;
 
 	Common::Rect bounds = _bounds;
@@ -1102,7 +1102,7 @@ void MacGui::MacSlider::draw(bool drawFocused) {
 
 		Common::Rect fillRect(_boundsBody.left + 1, _boundsBody.top, _boundsBody.right - 1, _boundsBody.bottom);
 
-		if (_maxValue - _minValue > _pageSize) {
+		if (isScrollable()) {
 			fill(fillRect);
 
 			Common::Rect handleRect = getHandleRect(_value);
@@ -1517,7 +1517,7 @@ MacGui::MacListBox::MacListBox(MacGui::MacDialogWindow *window, Common::Rect bou
 	_untouchableText = contentUntouchable;
 	MacStaticText *tmp;
 	for (int i = 0; i < numSlots; i++) {
-		Common::Rect r(_bounds.left + 1, _bounds.top + 1 + 16 * i, _bounds.right - 17, _bounds.top + 1 + 16 * (i + 1));
+		Common::Rect r(_bounds.left + 1, _bounds.top + 1 + 16 * i, _bounds.right - 16, _bounds.top + 1 + 16 * (i + 1));
 		tmp = new MacStaticText(window, r, texts[i], enabled);
 		if (contentUntouchable)
 			tmp->setColor(kLightGray, kWhite);
@@ -1651,43 +1651,30 @@ void MacGui::MacListBox::handleMouseHeld() {
 }
 
 void MacGui::MacListBox::handleWheelUp() {
-	Common::Point mousePos = _window->getMousePos();
-
-	if (_slider->findWidget(mousePos.x, mousePos.y)) {
-		_slider->handleWheelUp();
-		updateTexts();
-		return;
-	}
-
-	int oldValue = _slider->getValue();
-
-	_slider->setValue(oldValue - 1);
-
-	// FIXME: This is not the most efficient way of redrawing the slider
-
-	if (_slider->getValue() != oldValue) {
-		_slider->setRedraw(true);
-		updateTexts();
-	}
+	handleWheel(-1);
 }
 
 void MacGui::MacListBox::handleWheelDown() {
-	Common::Point mousePos = _window->getMousePos();
+	handleWheel(1);
+}
 
-	if (_slider->findWidget(mousePos.x, mousePos.y)) {
-		_slider->handleWheelDown();
+void MacGui::MacListBox::handleWheel(int distance) {
+	if (!_slider->isScrollable())
 		return;
-	}
 
+	Common::Point mousePos = _window->getMousePos();
 	int oldValue = _slider->getValue();
 
-	_slider->setValue(oldValue + 1);
+	if (_slider->findWidget(mousePos.x, mousePos.y))
+		distance *= _slider->getPageSize();
+
+	_slider->setValue(oldValue + distance);
 
-	// FIXME: This is not the most efficient way of redrawing the slider
+	int newValue = _slider->getValue();
 
-	if (_slider->getValue() != oldValue) {
-		_slider->setRedraw(true);
+	if (newValue != oldValue) {
 		updateTexts();
+		_slider->redrawHandle(oldValue, newValue);
 	}
 }
 
@@ -3474,6 +3461,9 @@ bool MacLoomGui::runOpenDialog(int &saveSlotToHandle) {
 	window->addButton(Common::Rect(254, 104, 334, 124), "Cancel", true);
 	window->addButton(Common::Rect(254, 59, 334, 79), "Delete", true);
 
+	Graphics::Surface *s = window->innerSurface();
+	s->hLine(253, 91, 334, kBlack);
+
 	bool availSlots[100];
 	int slotIds[100];
 	Common::StringArray savegameNames;
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index c02ff8ef76b..db693292485 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -367,13 +367,16 @@ public:
 
 		void eraseDragHandle();
 		void drawHandle(Common::Rect r);
-		void redrawHandle(int oldValue, int newValue);
 
 	public:
 		MacSlider(MacGui::MacDialogWindow *window, Common::Rect bounds, int minValue, int maxValue, int pageSize, bool enabled);
 
+		bool isScrollable() const { return (_maxValue - _minValue) > 0; }
+		int getPageSize() const { return _pageSize; }
+
 		bool findWidget(int x, int y) const;
 		void draw(bool drawFocued = false);
+		void redrawHandle(int oldValue, int newValue);
 
 		void handleMouseDown(Common::Event &event);
 		void handleMouseUp(Common::Event &event);
@@ -420,6 +423,7 @@ public:
 		bool _untouchableText = false;
 
 		void updateTexts();
+		void handleWheel(int distance);
 
 	public:
 		MacListBox(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::StringArray texts, bool enabled, bool contentUntouchable = true);


Commit: c4ed2b61c04d08741b1120e84e52259b51687124
    https://github.com/scummvm/scummvm/commit/c4ed2b61c04d08741b1120e84e52259b51687124
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: MACGUI: Hardcode save and open menu for Indy3

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 572f7929a52..4752517b344 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -38,6 +38,7 @@
 #include "scumm/gfx_mac.h"
 #include "scumm/players/player_v3m.h"
 #include "scumm/scumm.h"
+#include "scumm/scumm_v4.h"
 #include "scumm/sound.h"
 #include "scumm/usage_bits.h"
 #include "scumm/verbs.h"
@@ -2876,6 +2877,9 @@ MacGui::MacDialogWindow *MacGui::createDialog(int dialogId) {
 
 	Common::Rect bounds;
 
+	bool isOpenDialog = dialogId == 4000 || dialogId == 4001;
+	bool isSaveDialog = dialogId == 3998 || dialogId == 3999;
+
 	res = resource.getResource(MKTAG('D', 'L', 'O', 'G'), dialogId);
 	if (res) {
 		bounds.top = res->readUint16BE();
@@ -2928,14 +2932,23 @@ MacGui::MacDialogWindow *MacGui::createDialog(int dialogId) {
 
 			switch (type & 0x7F) {
 			case 0:
+			{
 				// User item
-				window->innerSurface()->frameRect(r, kRed);
-				res->skip(len);
-				break;
+				debug("user item %d, rect %d %d %d %d", i, r.left, r.top, r.right, r.bottom);
 
+				// Skip drive label box and listbox
+				bool doNotDraw = (isOpenDialog && (i == 6 || i == 7)) || ((isOpenDialog || isSaveDialog) && i == 3);
+				if (!doNotDraw)
+					window->innerSurface()->frameRect(r, kBlack);
+
+				break;
+			}
 			case 4:
 				// Button
 				str = getDialogString(res, len);
+				if ((isOpenDialog || isSaveDialog) && (i == 4 || i == 5)) // "Eject" and "Drive"
+					enabled = false;
+
 				window->addButton(r, str, enabled);
 				break;
 
@@ -2948,15 +2961,22 @@ MacGui::MacDialogWindow *MacGui::createDialog(int dialogId) {
 			case 8:
 				// Static text
 				str = getDialogString(res, len);
+				if (isSaveDialog && i == 2)
+					str = "Save Game File as...";
+
 				window->addStaticText(r, str, enabled);
+				debug("string \"%s\" item %d", str.c_str(), i);
 				break;
 
 			case 16:
+			{
 				// Editable text
-				window->addEditText(r, "Lorem ipsum dolor sit amet, consectetur adipisicing elit", enabled);
+				MacGui::MacEditText *editText = window->addEditText(r, "Game file", enabled);
+				editText->selectAll();
+				window->innerSurface()->frameRect(Common::Rect(r.left - 2, r.top - 3, r.right + 3, r.bottom + 3), kBlack);
 				res->skip(len);
 				break;
-
+			}
 			case 64:
 				// Picture
 				window->addPicture(r, res->readUint16BE(), enabled);
@@ -4937,8 +4957,7 @@ bool MacIndy3Gui::handleMenu(int id, Common::String &name) {
 		break;
 
 	case 205:	// Options
-		if (runOptionsDialog())
-			debug("Options should be applied now");
+		runOptionsDialog();
 		break;
 
 	case 206:	// Quit
@@ -5217,6 +5236,13 @@ bool MacIndy3Gui::runOpenDialog(int &saveSlotToHandle) {
 	window->addSubstitution(Common::String::format("%d", _vm->VAR(244)));
 	window->addSubstitution(Common::String::format("%d", _vm->VAR(245)));
 
+	bool availSlots[100];
+	int slotIds[100];
+	Common::StringArray savegameNames;
+	prepareSaveLoad(savegameNames, availSlots, slotIds, ARRAYSIZE(availSlots));
+
+	window->addListBox(Common::Rect(14, 41, 248, 187), savegameNames, true);
+
 	// When quitting, the default action is to not open a saved game
 	bool ret = false;
 
@@ -5252,12 +5278,19 @@ bool MacIndy3Gui::runSaveDialog(int &saveSlotToHandle, Common::String &name) {
 	// 10 - "Series: ^1" text
 	// 11 - "(Indy Quotient)" text
 
-	MacDialogWindow *window = createDialog((_vm->_renderMode == Common::kRenderMacintoshBW) ? 3998: 3999);
+	MacDialogWindow *window = createDialog((_vm->_renderMode == Common::kRenderMacintoshBW) ? 3998 : 3999);
 
 	window->setDefaultWidget(0);
 	window->addSubstitution(Common::String::format("%d", _vm->VAR(244)));
 	window->addSubstitution(Common::String::format("%d", _vm->VAR(245)));
 
+	bool availSlots[100];
+	int slotIds[100];
+	Common::StringArray savegameNames;
+	prepareSaveLoad(savegameNames, availSlots, slotIds, ARRAYSIZE(availSlots));
+
+	window->addListBox(Common::Rect(16, 31, 199, 129), savegameNames, true, true);
+
 	// When quitting, the default action is not to save a game
 	bool ret = false;
 
@@ -5383,7 +5416,7 @@ bool MacIndy3Gui::runIqPointsDialog() {
 
 			window->redrawWidget(4);
 
-			debug("TODO: Actually clear the series IQ score");
+			((ScummEngine_v4 *)_vm)->clearSeriesIQPoints();
 		}
 	}
 


Commit: c1d4eb362ef8f81312c8b0c5e4f41aa6946ea7f7
    https://github.com/scummvm/scummvm/commit/c1d4eb362ef8f81312c8b0c5e4f41aa6946ea7f7
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: MACGUI: Add stub for IQ clearing

Changed paths:
    engines/scumm/script_v4.cpp
    engines/scumm/scumm_v4.h


diff --git a/engines/scumm/script_v4.cpp b/engines/scumm/script_v4.cpp
index 8d5d7d2db38..8651b9c653a 100644
--- a/engines/scumm/script_v4.cpp
+++ b/engines/scumm/script_v4.cpp
@@ -330,6 +330,10 @@ void ScummEngine_v4::updateIQPoints() {
 	saveIQPoints();
 }
 
+void ScummEngine_v4::clearSeriesIQPoints() {
+	// Stub
+}
+
 void ScummEngine_v4::saveIQPoints() {
 	// save Indy3 IQ-points
 	Common::OutSaveFile *file;
diff --git a/engines/scumm/scumm_v4.h b/engines/scumm/scumm_v4.h
index 2ca7a8aa645..dce6dc67798 100644
--- a/engines/scumm/scumm_v4.h
+++ b/engines/scumm/scumm_v4.h
@@ -36,6 +36,7 @@ public:
 	ScummEngine_v4(OSystem *syst, const DetectorResult &dr);
 
 	void resetScumm() override;
+	void clearSeriesIQPoints(); // Used by MacGui
 
 protected:
 	const byte _GUIPalette[13]    = {0x00, 0x01, 0x0B, 0x03, 0x00, 0x0B, 0x0B, 0x03, 0x01, 0x00, 0x01, 0x0B, 0x09};


Commit: 6bbc39d92db1477797335058b93e776ce2da3706
    https://github.com/scummvm/scummvm/commit/6bbc39d92db1477797335058b93e776ce2da3706
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: MACGUI: Implement saving and loading for Indy3

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 4752517b344..996f81e243b 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -2934,7 +2934,6 @@ MacGui::MacDialogWindow *MacGui::createDialog(int dialogId) {
 			case 0:
 			{
 				// User item
-				debug("user item %d, rect %d %d %d %d", i, r.left, r.top, r.right, r.bottom);
 
 				// Skip drive label box and listbox
 				bool doNotDraw = (isOpenDialog && (i == 6 || i == 7)) || ((isOpenDialog || isSaveDialog) && i == 3);
@@ -2965,7 +2964,6 @@ MacGui::MacDialogWindow *MacGui::createDialog(int dialogId) {
 					str = "Save Game File as...";
 
 				window->addStaticText(r, str, enabled);
-				debug("string \"%s\" item %d", str.c_str(), i);
 				break;
 
 			case 16:
@@ -2973,6 +2971,10 @@ MacGui::MacDialogWindow *MacGui::createDialog(int dialogId) {
 				// Editable text
 				MacGui::MacEditText *editText = window->addEditText(r, "Game file", enabled);
 				editText->selectAll();
+
+				// Ugly, UGLY hack!
+				((MacIndy3Gui *)(this))->_saveGameEditText = editText;
+
 				window->innerSurface()->frameRect(Common::Rect(r.left - 2, r.top - 3, r.right + 3, r.bottom + 3), kBlack);
 				res->skip(len);
 				break;
@@ -2983,7 +2985,7 @@ MacGui::MacDialogWindow *MacGui::createDialog(int dialogId) {
 				break;
 
 			default:
-				warning("Unknown item type %d", type);
+				warning("MacGui::createDialog(): Unknown item type %d", type);
 				res->skip(len);
 				break;
 			}
@@ -5241,7 +5243,7 @@ bool MacIndy3Gui::runOpenDialog(int &saveSlotToHandle) {
 	Common::StringArray savegameNames;
 	prepareSaveLoad(savegameNames, availSlots, slotIds, ARRAYSIZE(availSlots));
 
-	window->addListBox(Common::Rect(14, 41, 248, 187), savegameNames, true);
+	MacGui::MacListBox *listBox = window->addListBox(Common::Rect(14, 41, 248, 187), savegameNames, true);
 
 	// When quitting, the default action is to not open a saved game
 	bool ret = false;
@@ -5256,6 +5258,11 @@ bool MacIndy3Gui::runOpenDialog(int &saveSlotToHandle) {
 
 		if (clicked == 2)
 			break;
+
+		if (clicked == 10) {
+			saveSlotToHandle =
+				listBox->getValue() < ARRAYSIZE(slotIds) ? slotIds[listBox->getValue()] : -1;
+		}
 	}
 
 	delete window;
@@ -5278,16 +5285,25 @@ bool MacIndy3Gui::runSaveDialog(int &saveSlotToHandle, Common::String &name) {
 	// 10 - "Series: ^1" text
 	// 11 - "(Indy Quotient)" text
 
+	_saveGameEditText = nullptr;
 	MacDialogWindow *window = createDialog((_vm->_renderMode == Common::kRenderMacintoshBW) ? 3998 : 3999);
 
 	window->setDefaultWidget(0);
 	window->addSubstitution(Common::String::format("%d", _vm->VAR(244)));
 	window->addSubstitution(Common::String::format("%d", _vm->VAR(245)));
 
-	bool availSlots[100];
+	bool busySlots[100];
 	int slotIds[100];
 	Common::StringArray savegameNames;
-	prepareSaveLoad(savegameNames, availSlots, slotIds, ARRAYSIZE(availSlots));
+	prepareSaveLoad(savegameNames, busySlots, slotIds, ARRAYSIZE(busySlots));
+
+	int firstAvailableSlot = -1;
+	for (int i = 0; i < ARRAYSIZE(busySlots); i++) {
+		if (!busySlots[i]) {
+			firstAvailableSlot = i;
+			break;
+		}
+	}
 
 	window->addListBox(Common::Rect(16, 31, 199, 129), savegameNames, true, true);
 
@@ -5299,6 +5315,8 @@ bool MacIndy3Gui::runSaveDialog(int &saveSlotToHandle, Common::String &name) {
 
 		if (clicked == 0) {
 			ret = true;
+			name = _saveGameEditText->getText();
+			saveSlotToHandle = firstAvailableSlot;
 			break;
 		}
 
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index db693292485..7043b0d09a2 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -642,6 +642,7 @@ public:
 	const Common::String name() const { return "Indy"; }
 
 	Graphics::Surface _textArea;
+	MacGui::MacEditText *_saveGameEditText = nullptr;
 
 	const Graphics::Font *getFontByScummId(int32 id);
 


Commit: 64b678b86bc48aaf6fe38b711e4c012797078ccc
    https://github.com/scummvm/scummvm/commit/64b678b86bc48aaf6fe38b711e4c012797078ccc
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Make Loom save dialog list box a bit taller

This avoids glitches when scrolling (once scrolling is fixed).

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 996f81e243b..caf81dbb6c9 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -3547,7 +3547,7 @@ bool MacLoomGui::runSaveDialog(int &saveSlotToHandle, Common::String &name) {
 		}
 	}
 
-	window->addListBox(Common::Rect(14, 9, 217, 137), savegameNames, true, true);
+	window->addListBox(Common::Rect(14, 9, 217, 139), savegameNames, true, true);
 
 	MacGui::MacEditText *editText = window->addEditText(Common::Rect(16, 164, 229, 180), "Game file", true);
 


Commit: 4efb72ebc1d6d4bc8587cdd38b239726de66c522
    https://github.com/scummvm/scummvm/commit/4efb72ebc1d6d4bc8587cdd38b239726de66c522
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Fix list box scrolling in Loom save dialog

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index caf81dbb6c9..ece3b3d22e6 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -1554,18 +1554,17 @@ void MacGui::MacListBox::setRedraw(bool fullRedraw) {
 }
 
 void MacGui::MacListBox::updateTexts() {
-	if (_untouchableText)
-		return;
+	Color textColor = _untouchableText ? kLightGray : kBlack;
 
 	int offset = _slider->getValue();
 
 	for (uint i = 0; i < _textWidgets.size(); i++) {
 		_textWidgets[i]->setText(_texts[i + offset]);
 
-		if ((int)(i + offset) == _value)
+		if ((int)(i + offset) == _value && !_untouchableText)
 			_textWidgets[i]->setColor(kWhite, kBlack);
 		else
-			_textWidgets[i]->setColor(kBlack, kWhite);
+			_textWidgets[i]->setColor(textColor, kWhite);
 	}
 }
 


Commit: 761f2946fcfc927a3f78ea7523f5cff91f5c0bde
    https://github.com/scummvm/scummvm/commit/761f2946fcfc927a3f78ea7523f5cff91f5c0bde
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: MACGUI: Handle and clear IQ points for Indy3

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/script_v4.cpp
    engines/scumm/scumm_v4.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index ece3b3d22e6..63bf8824c06 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -2503,6 +2503,8 @@ bool MacGui::handleMenu(int id, Common::String &name) {
 		if (runOpenDialog(saveSlotToHandle)) {
 			if (saveSlotToHandle > -1) {
 				_vm->loadGameState(saveSlotToHandle);
+				if (_vm->_game.id == GID_INDY3)
+					((ScummEngine_v4 *)_vm)->updateIQPoints();
 			}
 		}
 
@@ -5419,6 +5421,7 @@ bool MacIndy3Gui::runIqPointsDialog() {
 
 	MacDialogWindow *window = createDialog((_vm->_renderMode == Common::kRenderMacintoshBW) ? 1001 : 1002);
 
+	((ScummEngine_v4 *)_vm)->updateIQPoints();
 	window->addSubstitution(Common::String::format("%d", _vm->VAR(244)));
 	window->addSubstitution(Common::String::format("%d", _vm->VAR(245)));
 
diff --git a/engines/scumm/script_v4.cpp b/engines/scumm/script_v4.cpp
index 8651b9c653a..dcd888799fa 100644
--- a/engines/scumm/script_v4.cpp
+++ b/engines/scumm/script_v4.cpp
@@ -331,7 +331,20 @@ void ScummEngine_v4::updateIQPoints() {
 }
 
 void ScummEngine_v4::clearSeriesIQPoints() {
-	// Stub
+	Common::OutSaveFile *file;
+	Common::String filename = _targetName + ".iq";
+
+	file = _saveFileMan->openForSaving(filename);
+	if (file != nullptr) {
+		int size = getResourceSize(rtString, STRINGID_IQ_EPISODE);
+
+		for (int i = 0; i < size; i++)
+			file->writeByte(0);
+
+		_scummVars[245] = 0;
+
+		delete file;
+	}
 }
 
 void ScummEngine_v4::saveIQPoints() {
diff --git a/engines/scumm/scumm_v4.h b/engines/scumm/scumm_v4.h
index dce6dc67798..9c77b029ca0 100644
--- a/engines/scumm/scumm_v4.h
+++ b/engines/scumm/scumm_v4.h
@@ -36,7 +36,10 @@ public:
 	ScummEngine_v4(OSystem *syst, const DetectorResult &dr);
 
 	void resetScumm() override;
-	void clearSeriesIQPoints(); // Used by MacGui
+
+	// Used by MacGui
+	void clearSeriesIQPoints();
+	void updateIQPoints();
 
 protected:
 	const byte _GUIPalette[13]    = {0x00, 0x01, 0x0B, 0x03, 0x00, 0x0B, 0x0B, 0x03, 0x01, 0x00, 0x01, 0x0B, 0x09};
@@ -57,7 +60,6 @@ protected:
 	void loadVars();
 	void saveIQPoints();
 	void loadIQPoints(byte *ptr, int size);
-	void updateIQPoints();
 
 	int getBannerColor(int bannerId) override;
 	void setUpMainMenuControls() override;


Commit: 59d5a24b1a3ec5cd809e1092281a6c574200e617
    https://github.com/scummvm/scummvm/commit/59d5a24b1a3ec5cd809e1092281a6c574200e617
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: MACGUI: Clear IQ points in a more accurate way

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/script_v4.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 63bf8824c06..4d386b86ab3 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -5432,11 +5432,9 @@ bool MacIndy3Gui::runIqPointsDialog() {
 			break;
 
 		if (clicked == 1) {
-			window->replaceSubstitution(1, Common::String::format("%d", 0));
-
-			window->redrawWidget(4);
-
 			((ScummEngine_v4 *)_vm)->clearSeriesIQPoints();
+			window->replaceSubstitution(1, Common::String::format("%d", _vm->VAR(245)));
+			window->redrawWidget(4);
 		}
 	}
 
diff --git a/engines/scumm/script_v4.cpp b/engines/scumm/script_v4.cpp
index dcd888799fa..9770746e481 100644
--- a/engines/scumm/script_v4.cpp
+++ b/engines/scumm/script_v4.cpp
@@ -341,7 +341,7 @@ void ScummEngine_v4::clearSeriesIQPoints() {
 		for (int i = 0; i < size; i++)
 			file->writeByte(0);
 
-		_scummVars[245] = 0;
+		updateIQPoints();
 
 		delete file;
 	}


Commit: ed61b521805863d4958f438c160bd7f1614a37a7
    https://github.com/scummvm/scummvm/commit/ed61b521805863d4958f438c160bd7f1614a37a7
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Make listbox strings disabled for save dialog

This means we won't have to set their color, and that clicking on them
automatically does nothing. (I hope I didn't accidentally disable
anything else in the process.)

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 4d386b86ab3..c4f434ba62a 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -239,7 +239,7 @@ MacGui::MacWidget::MacWidget(MacGui::MacDialogWindow *window, Common::Rect bound
 }
 
 bool MacGui::MacWidget::findWidget(int x, int y) const {
-	return _bounds.contains(x, y);
+	return _enabled && _bounds.contains(x, y);
 }
 
 void MacGui::MacWidget::setRedraw(bool fullRedraw) {
@@ -263,7 +263,7 @@ void MacGui::MacWidget::drawBitmap(Common::Rect r, const uint16 *bitmap, Color c
 	_window->_gui->drawBitmap(_window->innerSurface(), r, bitmap, color);
 }
 
-int MacGui::MacWidget::drawText(Common::String text, int x, int y, int w, Color color, Graphics::TextAlign align, int deltax) const {
+int MacGui::MacWidget::drawText(Common::String text, int x, int y, int w, Color fg, Color bg, Graphics::TextAlign align, int deltax) const {
 	if (text.empty())
 		return 0;
 
@@ -323,7 +323,7 @@ int MacGui::MacWidget::drawText(Common::String text, int x, int y, int w, Color
 	int y0 = y;
 
 	for (uint i = 0; i < lines.size(); i++) {
-		font->drawString(_window->innerSurface(), lines[i], x, y0, w, color, align, deltax);
+		font->drawString(_window->innerSurface(), lines[i], x, y0, w, fg, align, deltax);
 
 		if (!_enabled) {
 			Common::Rect textBox = font->getBoundingBox(lines[i], x, y0, w, align);
@@ -331,7 +331,7 @@ int MacGui::MacWidget::drawText(Common::String text, int x, int y, int w, Color
 			for (int y1 = textBox.top; y1 < textBox.bottom; y1++) {
 				for (int x1 = textBox.left; x1 < textBox.right; x1++) {
 					if (((x1 + y1) % 2) == 0)
-						_window->innerSurface()->setPixel(x1, y1, kWhite);
+						_window->innerSurface()->setPixel(x1, y1, bg);
 				}
 			}
 		}
@@ -404,7 +404,7 @@ void MacGui::MacButton::draw(bool drawFocused) {
 
 	const Graphics::Font *font = _window->_gui->getFont(kSystemFont);
 
-	drawText(_text, _bounds.left, _bounds.top + (_bounds.height() - font->getFontHeight()) / 2, _bounds.width(), fg, Graphics::kTextAlignCenter);
+	drawText(_text, _bounds.left, _bounds.top + (_bounds.height() - font->getFontHeight()) / 2, _bounds.width(), fg, bg, Graphics::kTextAlignCenter);
 
 	Common::Rect bounds = _bounds;
 
@@ -490,7 +490,7 @@ void MacGui::MacCheckbox::draw(bool drawFocused) {
 		int x = _hitBounds.left + 18;
 		int y = _hitBounds.top;
 
-		drawText(_text, x, y, _hitBounds.right - x, kBlack);
+		drawText(_text, x, y, _hitBounds.right - x);
 		_window->markRectAsDirty(_bounds);
 	} else
 		_window->markRectAsDirty(box);
@@ -528,7 +528,7 @@ void MacGui::MacStaticText::draw(bool drawFocused) {
 	debug(1, "MacGui::MacStaticText: Drawing text %d (_fullRedraw = %d, drawFocused = %d, _value = %d)", _id, _fullRedraw, drawFocused, _value);
 
 	_window->innerSurface()->fillRect(_bounds, _bg);
-	drawText(_text, _bounds.left, _bounds.top, _bounds.width(), _fg, Graphics::kTextAlignLeft, 1);
+	drawText(_text, _bounds.left, _bounds.top, _bounds.width(), _fg, _bg, Graphics::kTextAlignLeft, 1);
 	_window->markRectAsDirty(_bounds);
 
 	_redraw = false;
@@ -1515,14 +1515,12 @@ MacGui::MacListBox::MacListBox(MacGui::MacDialogWindow *window, Common::Rect bou
 
 	int numSlots = MIN<int>(pageSize, texts.size());
 
-	_untouchableText = contentUntouchable;
 	MacStaticText *tmp;
 	for (int i = 0; i < numSlots; i++) {
 		Common::Rect r(_bounds.left + 1, _bounds.top + 1 + 16 * i, _bounds.right - 16, _bounds.top + 1 + 16 * (i + 1));
 		tmp = new MacStaticText(window, r, texts[i], enabled);
 		if (contentUntouchable)
-			tmp->setColor(kLightGray, kWhite);
-
+			tmp->setEnabled(false);
 		_textWidgets.push_back(tmp);
 	}
 
@@ -1554,17 +1552,15 @@ void MacGui::MacListBox::setRedraw(bool fullRedraw) {
 }
 
 void MacGui::MacListBox::updateTexts() {
-	Color textColor = _untouchableText ? kLightGray : kBlack;
-
 	int offset = _slider->getValue();
 
 	for (uint i = 0; i < _textWidgets.size(); i++) {
 		_textWidgets[i]->setText(_texts[i + offset]);
 
-		if ((int)(i + offset) == _value && !_untouchableText)
+		if (_textWidgets[i]->isEnabled() && (int)i + offset == _value)
 			_textWidgets[i]->setColor(kWhite, kBlack);
 		else
-			_textWidgets[i]->setColor(textColor, kWhite);
+			_textWidgets[i]->setColor(kBlack, kWhite);
 	}
 }
 
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 7043b0d09a2..0ab75a28d5d 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -163,7 +163,7 @@ public:
 		Common::String _text;
 		int _value = 0;
 
-		int drawText(Common::String text, int x, int y, int w, Color color, Graphics::TextAlign align = Graphics::kTextAlignLeft, int deltax = 0) const;
+		int drawText(Common::String text, int x, int y, int w, Color fg = kBlack, Color bg = kWhite, Graphics::TextAlign align = Graphics::kTextAlignLeft, int deltax = 0) const;
 		void drawBitmap(Common::Rect r, const uint16 *bitmap, Color color) const;
 
 	public:
@@ -420,7 +420,6 @@ public:
 		Common::Array<MacStaticText *> _textWidgets;
 		MacSlider *_slider;
 		bool _sliderFocused = false;
-		bool _untouchableText = false;
 
 		void updateTexts();
 		void handleWheel(int distance);


Commit: a9a6ec592bbd3fc74b7fe6db3425ee85767e83a9
    https://github.com/scummvm/scummvm/commit/a9a6ec592bbd3fc74b7fe6db3425ee85767e83a9
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: Make word-wrapping optional in Mac static text widgets

Word-wrapping is used for message dialogs, but not e.g. for strings in
the save/load dialogs.

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index c4f434ba62a..5bd6c8067e1 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -263,7 +263,7 @@ void MacGui::MacWidget::drawBitmap(Common::Rect r, const uint16 *bitmap, Color c
 	_window->_gui->drawBitmap(_window->innerSurface(), r, bitmap, color);
 }
 
-int MacGui::MacWidget::drawText(Common::String text, int x, int y, int w, Color fg, Color bg, Graphics::TextAlign align, int deltax) const {
+int MacGui::MacWidget::drawText(Common::String text, int x, int y, int w, Color fg, Color bg, Graphics::TextAlign align, bool wordWrap, int deltax) const {
 	if (text.empty())
 		return 0;
 
@@ -284,38 +284,44 @@ int MacGui::MacWidget::drawText(Common::String text, int x, int y, int w, Color
 	// Word-wrap text
 
 	Common::StringArray lines;
-	int start = 0;
 	int maxLineWidth = 0;
-	int lineWidth = 0;
-	int lastSpace = -1;
 
-	for (uint i = 0; i < text.size(); i++) {
-		if (text[i] == ' ')
-			lastSpace = i;
+	if (wordWrap) {
+		int start = 0;
+		int lineWidth = 0;
+		int lastSpace = -1;
 
-		lineWidth += font->getCharWidth(text[i]);
+		for (uint i = 0; i < text.size(); i++) {
+			if (text[i] == ' ')
+				lastSpace = i;
 
-		if (lineWidth > w) {
-			if (lastSpace > start)
-				i = lastSpace;
+			lineWidth += font->getCharWidth(text[i]);
 
-			if (lineWidth > maxLineWidth)
-				maxLineWidth = lineWidth;
+			if (lineWidth > w) {
+				if (lastSpace > start)
+					i = lastSpace;
 
-			lines.push_back(text.substr(start, i - start));
-			lineWidth = 0;
+				if (lineWidth > maxLineWidth)
+					maxLineWidth = lineWidth;
 
-			if (lastSpace > start)
-				start = i + 1;
-			else
-				start = i;
+				lines.push_back(text.substr(start, i - start));
+				lineWidth = 0;
+
+				if (lastSpace > start)
+					start = i + 1;
+				else
+					start = i;
+			}
 		}
-	}
 
-	lines.push_back(text.substr(start));
+		lines.push_back(text.substr(start));
 
-	if (lineWidth > maxLineWidth)
-		maxLineWidth = lineWidth;
+		if (lineWidth > maxLineWidth)
+			maxLineWidth = lineWidth;
+	} else {
+		lines.push_back(text);
+		maxLineWidth = font->getStringWidth(text);
+	}
 
 	// Draw the text. Disabled text is implemented as a filter on top of
 	// the already drawn text.
@@ -528,7 +534,7 @@ void MacGui::MacStaticText::draw(bool drawFocused) {
 	debug(1, "MacGui::MacStaticText: Drawing text %d (_fullRedraw = %d, drawFocused = %d, _value = %d)", _id, _fullRedraw, drawFocused, _value);
 
 	_window->innerSurface()->fillRect(_bounds, _bg);
-	drawText(_text, _bounds.left, _bounds.top, _bounds.width(), _fg, _bg, Graphics::kTextAlignLeft, 1);
+	drawText(_text, _bounds.left, _bounds.top, _bounds.width(), _fg, _bg, Graphics::kTextAlignLeft, _wordWrap, 1);
 	_window->markRectAsDirty(_bounds);
 
 	_redraw = false;
@@ -3039,6 +3045,9 @@ bool MacGui::runOkCancelDialog(Common::String text) {
 	window->setDefaultWidget(0);
 	window->addSubstitution(text);
 
+	MacStaticText *widget = (MacStaticText *)window->getWidget(2);
+	widget->setWordWrap(true);
+
 	// When quitting, the default action is to quit
 	bool ret = true;
 
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 0ab75a28d5d..05a52c91919 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -163,7 +163,7 @@ public:
 		Common::String _text;
 		int _value = 0;
 
-		int drawText(Common::String text, int x, int y, int w, Color fg = kBlack, Color bg = kWhite, Graphics::TextAlign align = Graphics::kTextAlignLeft, int deltax = 0) const;
+		int drawText(Common::String text, int x, int y, int w, Color fg = kBlack, Color bg = kWhite, Graphics::TextAlign align = Graphics::kTextAlignLeft, bool wordWrap = false, int deltax = 0) const;
 		void drawBitmap(Common::Rect r, const uint16 *bitmap, Color color) const;
 
 	public:
@@ -236,6 +236,7 @@ public:
 	private:
 		Color _fg = kBlack;
 		Color _bg = kWhite;
+		bool _wordWrap = false;
 
 	public:
 		MacStaticText(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::String text, bool enabled) : MacWidget(window, bounds, text, true) {}
@@ -243,6 +244,8 @@ public:
 		void getFocus() {}
 		void loseFocus() {}
 
+		void setWordWrap(bool wordWrap) { _wordWrap = wordWrap; }
+
 		void setText(Common::String text) {
 			if (text != _text) {
 				_text = text;
@@ -510,6 +513,7 @@ public:
 		int runDialog();
 		void updateCursor();
 
+		MacWidget *getWidget(int nr) const { return _widgets[nr]; }
 		void setDefaultWidget(int nr) { _defaultWidget = _widgets[nr]; }
 		MacWidget *getDefaultWidget() const { return _defaultWidget; }
 


Commit: 83c94305ec56061dad2792a5cf54d1254c21d823
    https://github.com/scummvm/scummvm/commit/83c94305ec56061dad2792a5cf54d1254c21d823
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: MACGUI: Add deferred widgets handling & disable Save button on empty editText string

This prevents users from ever saving games with empty names, like
in the original and also removes my previous ugly hack for fetching
the savegame name string.

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 5bd6c8067e1..4926d37d06d 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -1967,10 +1967,12 @@ void MacGui::MacDialogWindow::fillPattern(Common::Rect r, uint16 pattern) {
 	markRectAsDirty(r);
 }
 
-int MacGui::MacDialogWindow::runDialog() {
+int MacGui::MacDialogWindow::runDialog(Common::Array<int> &deferredActionIds) {
 	// The first time the function is called, show the dialog and redraw
 	// all widgets completely.
 
+	deferredActionIds.clear();
+
 	if (!_visible) {
 		show();
 
@@ -2154,10 +2156,17 @@ int MacGui::MacDialogWindow::runDialog() {
 							_beamCursorVisible = false;
 							undrawBeamCursor();
 						}
+
+						if (_widgets[i]->shouldDeferAction())
+							deferredActionIds.push_back(_widgets[i]->getId());
+
 						break;
 					}
 				}
 
+				if (!deferredActionIds.empty())
+					return -2;
+
 				break;
 
 			default:
@@ -2975,9 +2984,6 @@ MacGui::MacDialogWindow *MacGui::createDialog(int dialogId) {
 				MacGui::MacEditText *editText = window->addEditText(r, "Game file", enabled);
 				editText->selectAll();
 
-				// Ugly, UGLY hack!
-				((MacIndy3Gui *)(this))->_saveGameEditText = editText;
-
 				window->innerSurface()->frameRect(Common::Rect(r.left - 2, r.top - 3, r.right + 3, r.bottom + 3), kBlack);
 				res->skip(len);
 				break;
@@ -3051,8 +3057,10 @@ bool MacGui::runOkCancelDialog(Common::String text) {
 	// When quitting, the default action is to quit
 	bool ret = true;
 
+	Common::Array<int> deferredActionsIds;
+
 	while (!_vm->shouldQuit()) {
-		int clicked = window->runDialog();
+		int clicked = window->runDialog(deferredActionsIds);
 
 		if (clicked == 0)
 			break;
@@ -3503,9 +3511,10 @@ bool MacLoomGui::runOpenDialog(int &saveSlotToHandle) {
 
 	// When quitting, the default action is to not open a saved game
 	bool ret = false;
+	Common::Array<int> deferredActionsIds;
 
 	while (!_vm->shouldQuit()) {
-		int clicked = window->runDialog();
+		int clicked = window->runDialog(deferredActionsIds);
 
 		if (clicked == 0) {
 			ret = true;
@@ -3571,9 +3580,10 @@ bool MacLoomGui::runSaveDialog(int &saveSlotToHandle, Common::String &name) {
 
 	// When quitting, the default action is to not open a saved game
 	bool ret = false;
+	Common::Array<int> deferredActionsIds;
 
 	while (!_vm->shouldQuit()) {
-		int clicked = window->runDialog();
+		int clicked = window->runDialog(deferredActionsIds);
 
 		if (clicked == 0) {
 			ret = true;
@@ -3584,6 +3594,19 @@ bool MacLoomGui::runSaveDialog(int &saveSlotToHandle, Common::String &name) {
 
 		if (clicked == 1)
 			break;
+
+		if (clicked == -2) {
+			// Cycle through deferred actions
+			for (uint i = 0; i < deferredActionsIds.size(); i++) {
+				// Edit text widget
+				if (deferredActionsIds[i] == 4) {
+					MacGui::MacWidget *wid = window->getWidget(deferredActionsIds[i]);
+
+					// Disable "Save" button when text is empty
+					window->getWidget(0)->setEnabled(!wid->getText().empty());
+				}
+			}
+		}
 	}
 
 	delete window;
@@ -3641,9 +3664,10 @@ bool MacLoomGui::runOptionsDialog() {
 
 	// When quitting, the default action is not to not apply options
 	bool ret = false;
+	Common::Array<int> deferredActionsIds;
 
 	while (!_vm->shouldQuit()) {
-		int clicked = window->runDialog();
+		int clicked = window->runDialog(deferredActionsIds);
 
 		if (clicked == 0) {
 			ret = true;
@@ -5253,9 +5277,10 @@ bool MacIndy3Gui::runOpenDialog(int &saveSlotToHandle) {
 
 	// When quitting, the default action is to not open a saved game
 	bool ret = false;
+	Common::Array<int> deferredActionsIds;
 
 	while (!_vm->shouldQuit()) {
-		int clicked = window->runDialog();
+		int clicked = window->runDialog(deferredActionsIds);
 
 		if (clicked == 0) {
 			ret = true;
@@ -5291,7 +5316,6 @@ bool MacIndy3Gui::runSaveDialog(int &saveSlotToHandle, Common::String &name) {
 	// 10 - "Series: ^1" text
 	// 11 - "(Indy Quotient)" text
 
-	_saveGameEditText = nullptr;
 	MacDialogWindow *window = createDialog((_vm->_renderMode == Common::kRenderMacintoshBW) ? 3998 : 3999);
 
 	window->setDefaultWidget(0);
@@ -5315,19 +5339,33 @@ bool MacIndy3Gui::runSaveDialog(int &saveSlotToHandle, Common::String &name) {
 
 	// When quitting, the default action is not to save a game
 	bool ret = false;
+	Common::Array<int> deferredActionsIds;
 
 	while (!_vm->shouldQuit()) {
-		int clicked = window->runDialog();
+		int clicked = window->runDialog(deferredActionsIds);
 
 		if (clicked == 0) {
 			ret = true;
-			name = _saveGameEditText->getText();
+			name = window->getWidget(5)->getText(); // Edit text widget
 			saveSlotToHandle = firstAvailableSlot;
 			break;
 		}
 
 		if (clicked == 1)
 			break;
+
+		if (clicked == -2) {
+			// Cycle through deferred actions
+			for (uint i = 0; i < deferredActionsIds.size(); i++) {
+				// Edit text widget
+				if (deferredActionsIds[i] == 5) {
+					MacGui::MacWidget *wid = window->getWidget(deferredActionsIds[i]);
+
+					// Disable "Save" button when text is empty
+					window->getWidget(0)->setEnabled(!wid->getText().empty());
+				}
+			}
+		}
 	}
 
 	delete window;
@@ -5370,9 +5408,10 @@ bool MacIndy3Gui::runOptionsDialog() {
 
 	// When quitting, the default action is not to not apply options
 	bool ret = false;
+	Common::Array<int> deferredActionsIds;
 
 	while (!_vm->shouldQuit()) {
-		int clicked = window->runDialog();
+		int clicked = window->runDialog(deferredActionsIds);
 
 		if (clicked == 0) {
 			ret = true;
@@ -5430,8 +5469,10 @@ bool MacIndy3Gui::runIqPointsDialog() {
 	window->addSubstitution(Common::String::format("%d", _vm->VAR(244)));
 	window->addSubstitution(Common::String::format("%d", _vm->VAR(245)));
 
+	Common::Array<int> deferredActionsIds;
+
 	while (!_vm->shouldQuit()) {
-		int clicked = window->runDialog();
+		int clicked = window->runDialog(deferredActionsIds);
 
 		if (clicked == 0)
 			break;
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 05a52c91919..8c66e93eb85 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -187,8 +187,11 @@ public:
 		virtual void setValue(int value);
 		int getValue() const { return _value; }
 
+		Common::String getText() { return _text; }
+
 		virtual bool useBeamCursor() { return false; }
 		virtual bool findWidget(int x, int y) const;
+		virtual bool shouldDeferAction() { return false; }
 
 		virtual void draw(bool drawFocused = false) = 0;
 
@@ -293,12 +296,11 @@ public:
 		void getFocus() {}
 		void loseFocus() {}
 
-		Common::String getText() { return _text; }
-
 		void selectAll();
 
 		bool useBeamCursor() { return true; }
 		bool findWidget(int x, int y) const;
+		bool shouldDeferAction() override { return true; }
 
 		void draw(bool drawFocused = false);
 
@@ -510,7 +512,7 @@ public:
 		bool isVisible() const { return _visible; }
 
 		void show();
-		int runDialog();
+		int runDialog(Common::Array<int> &deferredActionIds);
 		void updateCursor();
 
 		MacWidget *getWidget(int nr) const { return _widgets[nr]; }
@@ -645,7 +647,6 @@ public:
 	const Common::String name() const { return "Indy"; }
 
 	Graphics::Surface _textArea;
-	MacGui::MacEditText *_saveGameEditText = nullptr;
 
 	const Graphics::Font *getFontByScummId(int32 id);
 


Commit: 3f707d009ddc114d3e03fcb0dc4a88577152d2c4
    https://github.com/scummvm/scummvm/commit/3f707d009ddc114d3e03fcb0dc4a88577152d2c4
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: MACGUI: Implement quit dialog handling for out-of-menu quit events

Changed paths:
    engines/scumm/gfx_mac.h
    engines/scumm/input.cpp


diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 8c66e93eb85..907b467e0da 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -131,8 +131,6 @@ protected:
 	void prepareSaveLoad(Common::StringArray &savegameNames, bool *availSlots, int *slotIds, int size);
 
 	bool runOkCancelDialog(Common::String text);
-	bool runQuitDialog();
-	bool runRestartDialog();
 
 public:
 	class MacDialogWindow;
@@ -587,6 +585,9 @@ public:
 	virtual void resetAfterLoad() = 0;
 	virtual void update(int delta) = 0;
 
+	bool runQuitDialog();
+	bool runRestartDialog();
+
 	virtual void setupCursor(int &width, int &height, int &hotspotX, int &hotspotY, int &animate) = 0;
 
 	virtual Graphics::Surface *textArea() { return nullptr; }
diff --git a/engines/scumm/input.cpp b/engines/scumm/input.cpp
index 9d5130ca713..e8f205737a4 100644
--- a/engines/scumm/input.cpp
+++ b/engines/scumm/input.cpp
@@ -286,14 +286,9 @@ void ScummEngine::parseEvent(Common::Event event) {
 		// event which was triggered by the keystroke. Clear the key.
 		clearClickedStatus();
 
-		bool usesMacMenu = isUsingOriginalGUI() &&
-						   _game.platform == Common::kPlatformMacintosh &&
-						   _game.version < 4;
-
 		if (isUsingOriginalGUI() &&
 			_game.platform != Common::kPlatformSegaCD &&
-			_game.platform != Common::kPlatformNES &&
-			!usesMacMenu) {
+			_game.platform != Common::kPlatformNES) {
 			if (!_quitByGUIPrompt && !_mainMenuIsActive) {
 				bool exitType = (event.type == Common::EVENT_RETURN_TO_LAUNCHER);
 				// If another message banner is currently on the screen, close it
@@ -301,12 +296,26 @@ void ScummEngine::parseEvent(Common::Event event) {
 				getEventManager()->resetQuit();
 				getEventManager()->resetReturnToLauncher();
 				if (!_messageBannerActive) {
-					queryQuit(exitType);
+					if (_macGui) {
+						if (!(ConfMan.hasKey("confirm_exit") && ConfMan.getBool("confirm_exit")) ||
+							_macGui->runQuitDialog()) {
+							_quitByGUIPrompt = true;
+							if (exitType) {
+								Common::Event fakeEvent;
+								fakeEvent.type = Common::EVENT_RETURN_TO_LAUNCHER;
+								getEventManager()->pushEvent(fakeEvent);
+							} else {
+								quitGame();
+							}
+						}
+					} else {
+						queryQuit(exitType);
+					}
 					_closeBannerAndQueryQuitFlag = false;
 				} else {
 					_closeBannerAndQueryQuitFlag = true;
 				}
-			} else if (_quitByGUIPrompt || usesMacMenu) {
+			} else if (_quitByGUIPrompt) {
 				if (!getEventManager()->shouldReturnToLauncher())
 					quitGame();
 			}


Commit: 4232178ab2286ea27c45c74d0ce1b902445aba13
    https://github.com/scummvm/scummvm/commit/4232178ab2286ea27c45c74d0ce1b902445aba13
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-11-18T18:47:39+01:00

Commit Message:
SCUMM: MACGUI: Implement restart dialog handling outside the Mac menu

Changed paths:
    engines/scumm/input.cpp


diff --git a/engines/scumm/input.cpp b/engines/scumm/input.cpp
index e8f205737a4..c10a24b3a5e 100644
--- a/engines/scumm/input.cpp
+++ b/engines/scumm/input.cpp
@@ -995,7 +995,14 @@ void ScummEngine::processKeyboard(Common::KeyState lastKeyHit) {
 		restartKeyPressed &= !isSegaCD && !isNES;
 
 		if (restartKeyPressed) {
-			queryRestart();
+			if (_macGui) {
+				if (_macGui->runRestartDialog()) {
+					restart();
+				}
+			} else {
+				queryRestart();
+			}
+
 			return;
 		}
 




More information about the Scummvm-git-logs mailing list