[Scummvm-git-logs] scummvm master -> 2867f9715bac122e8bd870e271b77487ccd7b6d4

sev- noreply at scummvm.org
Mon Oct 16 15:41:07 UTC 2023


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

Summary:
e74f6d5a59 SCUMM: Disable some Mac-specific Indy 3 stuff
70d15976fe SCUMM: Convert CharsetRendererMac to use MacFontManager
1d154df781 SCUMM: Use Geneva for Indy 3 Mac GUI
73101d6aab SCUMM: Draw Indy 3 Mac outline font filled, and closer together
e22310c2ad GRAPHICS: Fix building with DEBUGSCALING
5009bb67e4 GRAPHICS: Fix bold text to be more (hopefully exeactly) like the original
789f2dfaf1 GRAPHICS: Make sure outline glyphs aren't cropped on the left side
3e0a726cb4 GRAPHICS: Calculate bitmapOffset correctly
e5e5f049be GRAPHICS: Implement kMacFontCondense and kMacFontExtend
babcb19088 SCUMM: Update Indy 3 verb drawing for the latest Mac font changes
c6d1a256ef SCUMM: Moved Mac Indy 3 GUI into its own class
c13771736a SCUMM: Add more drawing code for the Indy 3 Mac GUI
a9f26e9631 SCUMM: More Indy 3 Mac widget stuff
e2a11d0040 SCUMM: Moved some more Indy 3 GUI code into drawInventoryScrollbar()
4f247fec35 SCUMM: More Indy 3 Mac GUI stuff
33b6ee6c05 SCUMM: Remove verb position adjustments for Indy 3 Mac
d827858691 SCUMM: Clear all of the Mac Indy 3 verb area
716cd6e0e5 SCUMM: Remove Indy 3 Mac GUI mock-up. Begin work on proper GUI.
f37152d468 SCUMM: Indy 3 Mac is actually drawing scripted buttons now
fd4d53c27f SCUMM: Only draw Mac Indy 3 widgets (formerly buttons) when they change
bdd1de7431 SCUMM: Some remaning, and added animation to Indy 3 Mac buttons
89908e1b31 SCUMM: Kill Indy 3 Mac widgets when they're gone
fde1fc8717 SCUMM: Hide the Indy 3 Mac GUI when there's nothing to show in it
eeac1b24ef SCUMM: Indy 3 Mac responds to verbs now
654090043b SCUMM: Fix glitches in Indy 3 Mac GUI
ead11c29e7 SCUMM: Try to fix Indy 3 Mac GUI after loading
560f49d9cf SCUMM: Fix some Indy 3 Mac bugs, and add two more button widgets
1357be25f4 SCUMM: Migrate Indy 3 Mac widget positions to Common::Rect
95015600d1 SCUMM: Fix order of Mac Indy 3 verb buttons
1875d19349 SCUMM: Remove untrue Indy 3 GUI comment
702ec52d96 SCUMM: Lots of Indy 3 Mac GUI rewriting
b8d5096d7c SCUMM: Some more Indy 3 Mac inventory work
127c585362 SCUMM: More work on Indy 3 Mac GUI
98229e5155 SCUMM: Hopefully fix Indy 3 Mac glitch on travel map
3bf0b424cd SCUMM: Some more Indy 3 Mac GUI cleanup
a14de55dda SCUMM: More Indy 3 Mac GUI fixes
62af893348 SCUMM: Major (incomplete) refactoring of Indy 3 Mac GUI
ef6e3bf432 SCUMM: Indy 3 Mac GUI restructuring done. Cleanup next.
6f8f3b4b13 SCUMM: Update Indy 3 Mac GUI less frequently
fd63cf734d SCUMM: Hopefully fix Indy 3 Mac verb shortcut keys
6bc9ccadef SCUMM: Fix clicking on Indy 3 Mac GUI sentence line
3068bcff72 SCUMM: Removed last bit of pre-restructuring Indy 3 Mac GUI
cc8a1ce53d SCUMM: The Indy 3 Mac scrollbar is now a widget
7186851de6 SCUMM: Fix Indy 3 Mac GUI inventory widget being undrawn over and over
e40c3a2fbb SCUMM: More Indy 3 Mac GUI cleanup + inventory scrolling
44b7558756 SCUMM: Remove Indy 3 Mac hacks that are no longer needed
436872ffbb SCUMM: This should fix some transitions with the Indy 3 Mac GUI
30367e1dd2 SCUMM: Fix Indy 3 Mac GUI inventory string drawing
16d610f69d SCUMM: On second thought, don't adjust the Indy 3 Mac GUI scroll offset
2bc18cf971 SCUMM: Mac Indy 3 GUI: Add "const"
e0f856f82c SCUMM: Indy 3 Mac GUI scroll bar fixes
264295b029 SCUMM: Rework Indy 3 Mac GUI verb script check
a67b1e9758 SCUMM: Remove Indy 3 Mac GUI verb script check
ae3dbd74f3 SCUMM: Cleanup Indy 3 Mac GUI scrollbar logic
811347569f SCUMM: More Indy 3 Mac GUI cleanup
a052cd4eea SCUMM: Slight drawing optimization in Indy 3 Mac GUI
6d501281ff SCUMM: Redraw Indy 3 Mac GUI widget immediately if it responds to an event
260b64db73 SCUMM: The Mac executables for Indy 3 and Loom are now required.
8402d2999c SCUMM: Remove low-resolution fallback for Indy 3 Mac credit undrawing
131b5b8238 SCUMM: Remove another Indy 3 Mac hack
e54128eba9 SCUMM: Fix clicking on Indy 3 Mac inventory objects
2223c96395 SCUMM: More Indy 3 Mac GUI cleanups
c186a025a3 SCUMM: Redraw the Indy 3 Mac inventory scrollbar when necessary
0e3eac4c34 SCUMM: Add comment to Indy 3 Mac GUI
274ebe2db4 SCUMM: Moved Indy 3 Mac GUI "hiding" debug message
afcdabe18f SCUMM: Indy 3 Mac GUI dleanup
175d53905c SCUMM: Increased savegame version
9902d8e133 SCUMM: Indy 3 Mac GUI cleanup
ee120bbd52 SCUMM: Fix double-clicking on Indy 3 Mac inventory items
b84fb46c49 SCUMM: Fix Indy 3 Mac scrollbar clicking behavior to be like the original
508ade605c SCUMM: Indy 3 Mac GUI cleanup
38852234ec SCUMM: Fix printing of '...' character in Indy 3 Mac GUI
a11092859c SCUMM: Fix Mac Loom regression
12738d8856 SCUMM: Create _macIndy3Gui earlier, not in resetScumm()
2867f9715b SCUMM: Minor Indy 3 Mac GUI cleanups from bluegr's review


Commit: e74f6d5a59be28d36da66d2ef235788c7bd09b51
    https://github.com/scummvm/scummvm/commit/e74f6d5a59be28d36da66d2ef235788c7bd09b51
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Disable some Mac-specific Indy 3 stuff

Just as an experiment.

Changed paths:
    engines/scumm/script_v5.cpp


diff --git a/engines/scumm/script_v5.cpp b/engines/scumm/script_v5.cpp
index c376a5bca99..f0df6349c13 100644
--- a/engines/scumm/script_v5.cpp
+++ b/engines/scumm/script_v5.cpp
@@ -2592,6 +2592,7 @@ void ScummEngine_v5::o5_setVarRange() {
 
 	// Macintosh version of indy3ega used different interface, so adjust values.
 	if (_game.id == GID_INDY3 && _game.platform == Common::kPlatformMacintosh) {
+#if 0
 		VAR(68) = 0;
 		VAR(69) = 0;
 		VAR(70) = 168;
@@ -2606,6 +2607,7 @@ void ScummEngine_v5::o5_setVarRange() {
 		VAR(79) = 184;
 		VAR(80) = 192;
 		VAR(81) = 192;
+#endif
 	}
 }
 
@@ -2949,6 +2951,7 @@ void ScummEngine_v5::o5_verbOps() {
 			vs->curRect.top = getVarOrDirectWord(PARAM_2);
 			// Macintosh version of indy3ega used different interface, so adjust values.
 			if ((_game.platform == Common::kPlatformMacintosh) && (_game.id == GID_INDY3)) {
+#if 0
 				switch (verb) {
 				case 1:
 				case 2:
@@ -2982,6 +2985,7 @@ void ScummEngine_v5::o5_verbOps() {
 				default:
 					break;
 				}
+#endif
 			} else if (_game.platform == Common::kPlatformFMTowns && ConfMan.getBool("trim_fmtowns_to_200_pixels")) {
 				if (_game.id == GID_ZAK && verb == 116)
 					// WORKAROUND: FM-TOWNS Zak used the extra 40 pixels at the bottom to increase the inventory to 10 items


Commit: 70d15976fe25ba97d294114f2a22d21fae929752
    https://github.com/scummvm/scummvm/commit/70d15976fe25ba97d294114f2a22d21fae929752
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Convert CharsetRendererMac to use MacFontManager

This will make it much easier to use the system fonts with bold and/or
outline, which Last Crusade will need.

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


diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp
index 829879d20b3..1ec77a306a6 100644
--- a/engines/scumm/charset.cpp
+++ b/engines/scumm/charset.cpp
@@ -19,7 +19,9 @@
  *
  */
 
-#include "common/macresman.h"
+#include "graphics/fonts/macfont.h"
+#include "graphics/macgui/macwindowmanager.h"
+#include "graphics/macgui/macfontmanager.h"
 
 #include "scumm/charset.h"
 #include "scumm/file.h"
@@ -1576,55 +1578,36 @@ CharsetRendererMac::CharsetRendererMac(ScummEngine *vm, const Common::String &fo
 	// 60 is an upside-down note, i.e. the one used for c'.
 	// 95 is a used for the rest of the notes.
 
-	Common::MacResManager resource;
-	resource.open(fontFile);
+	_macFontManager = new Graphics::MacFontManager(0, Common::Language::UNK_LANG);
+	_macFontManager->loadFonts(fontFile);
 
-	Common::String fontFamilyName = (_vm->_game.id == GID_LOOM) ? "Loom" : "Indy";
-
-	Common::SeekableReadStream *fond = resource.getResource(MKTAG('F', 'O', 'N', 'D'), fontFamilyName);
-
-	if (!fond)
-		return;
-
-	Graphics::MacFontFamily fontFamily(fontFamilyName);
-	if (!fontFamily.load(*fond)) {
-		delete fond;
-		return;
+	Common::String fontFamily = (_vm->_game.id == GID_LOOM) ? "Loom" : "Indy";
+	const Common::Array<Graphics::MacFontFamily *> &fontFamilies = _macFontManager->getFontFamilies();
+	for (uint i = 0; i < fontFamilies.size(); i++) {
+		if (fontFamilies[i]->getName() == fontFamily) {
+			_macFontManager->registerFontName(fontFamilies[i]->getName(), fontFamilies[i]->getFontFamilyId());
+			break;
+		}
 	}
 
-	Common::Array<Graphics::MacFontFamily::AsscEntry> *assoc = fontFamily.getAssocTable();
-	for (uint i = 0; i < assoc->size(); i++) {
-		int fontId = -1;
-		int fontSize = (*assoc)[i]._fontSize;
+	for (uint i = 0; i < ARRAYSIZE(_macFonts); i++)
+		_macFonts[i] = nullptr;
 
-		if (_vm->_game.id == GID_INDY3) {
-			if (fontSize == 9)
-				fontId = 1;
-			else if (fontSize == 12)
-				fontId = 0;
-		} else {
-			if (fontSize == 13)
-				fontId = 0;
-			else if (fontSize == 12)
-				fontId = 1;
-		}
-		if (fontId != -1) {
-			Common::SeekableReadStream *font = resource.getResource(MKTAG('F', 'O', 'N', 'T'), (*assoc)[i]._fontID);
-			_macFonts[fontId].loadFont(*font, &fontFamily, fontSize, 0);
-			delete font;
-		}
+	if (_vm->_game.id == GID_INDY3) {
+		_macFonts[0] = _macFontManager->getFont(Graphics::MacFont(_macFontManager->getFontIdByName(fontFamily), 12));
+		_macFonts[1] = _macFontManager->getFont(Graphics::MacFont(_macFontManager->getFontIdByName(fontFamily), 9));
+	} else {
+		_macFonts[0] = _macFontManager->getFont(Graphics::MacFont(_macFontManager->getFontIdByName(fontFamily), 13));
+		_macFonts[1] = _macFontManager->getFont(Graphics::MacFont(_macFontManager->getFontIdByName(fontFamily), 12));
 	}
 
-	delete fond;
-
 	if (_vm->_renderMode == Common::kRenderMacintoshBW) {
-		int numFonts = (_vm->_game.id == GID_INDY3) ? 2 : 1;
 		int maxHeight = -1;
 		int maxWidth = -1;
 
-		for (int i = 0; i < numFonts; i++) {
-			maxHeight = MAX(maxHeight, _macFonts[i].getFontHeight());
-			maxWidth = MAX(maxWidth, _macFonts[i].getMaxCharWidth());
+		for (int i = 0; i < ARRAYSIZE(_macFonts); i++) {
+			maxHeight = MAX(maxHeight, _macFonts[i]->getFontHeight());
+			maxWidth = MAX(maxWidth, _macFonts[i]->getMaxCharWidth());
 		}
 
 		_glyphSurface = new Graphics::Surface();
@@ -1637,6 +1620,7 @@ CharsetRendererMac::~CharsetRendererMac() {
 		_glyphSurface->free();
 		delete _glyphSurface;
 	}
+	delete _macFontManager;
 }
 
 void CharsetRendererMac::setCurID(int32 id) {
@@ -1689,7 +1673,7 @@ int CharsetRendererMac::getStringWidth(int arg, const byte *text) {
 }
 
 int CharsetRendererMac::getDrawWidthIntern(uint16 chr) const {
-	return _macFonts[_curId].getCharWidth(chr);
+	return _macFonts[_curId]->getCharWidth(chr);
 }
 
 // HACK: Usually, we want the approximate width and height in the unscaled
@@ -1697,7 +1681,7 @@ 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();
+	int height = _macFonts[_curId]->getFontHeight();
 
         // If we ever need the height for font 1 in Last Crusade (we don't at
 	// the moment), we need the actual height.
@@ -1824,12 +1808,12 @@ void CharsetRendererMac::printChar(int chr, bool ignoreCharsetMask) {
 		left = macLeft / 2;
 		right = (macLeft + width + 3) / 2;
 		top = macTop / 2;
-		bottom = (macTop + _macFonts[_curId].getFontHeight() + 3) / 2;
+		bottom = (macTop + _macFonts[_curId]->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 + _macFonts[_curId]->getFontHeight() + 1) / 2;
 	}
 
 	if (_firstChar) {
@@ -1904,32 +1888,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);
+			_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);
 
 			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);
+				_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);
 			}
 		} 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);
+			_macFonts[_curId]->drawChar(&_vm->_textSurface, chr, x + 1, y + 1, 0);
+			_macFonts[_curId]->drawChar(_vm->_macScreen, chr, x + 1, y + 1, shadowColor);
 		}
 	}
 
-	_macFonts[_curId].drawChar(&_vm->_textSurface, chr, x, y, 0);
+	_macFonts[_curId]->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);
+			_macFonts[_curId]->drawChar(_glyphSurface, chr, 0, 0, 15);
 
 			byte *src = (byte *)_glyphSurface->getBasePtr(0, 0);
 			byte *dst = (byte *)_vm->_macScreen->getBasePtr(x, y);
@@ -1950,7 +1934,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);
+			_macFonts[_curId]->drawChar(_vm->_macScreen, chr, x, y, color);
 		}
 	}
 }
@@ -1971,7 +1955,7 @@ 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);
+	_macFonts[_curId]->drawChar(_vm->_macIndy3TextBox, chr, x + 5, y + 11, color);
 }
 
 void CharsetRendererMac::drawChar(int chr, Graphics::Surface &s, int x, int y) {
@@ -1984,7 +1968,7 @@ 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);
+	_macFonts[_curId]->drawChar(&s, chr, x, y, color);
 }
 
 void CharsetRendererMac::setColor(byte color) {
diff --git a/engines/scumm/charset.h b/engines/scumm/charset.h
index 879276fc37a..8f1590e7159 100644
--- a/engines/scumm/charset.h
+++ b/engines/scumm/charset.h
@@ -24,12 +24,16 @@
 
 #include "common/scummsys.h"
 #include "common/rect.h"
-#include "graphics/fonts/macfont.h"
 #include "graphics/sjis.h"
 #include "scumm/charset_v7.h"
 #include "scumm/scumm.h"
 #include "scumm/gfx.h"
 
+namespace Graphics {
+class MacFontManager;
+class Font;
+}
+
 namespace Scumm {
 
 class ScummEngine;
@@ -281,13 +285,13 @@ public:
 
 class CharsetRendererMac : public CharsetRendererCommon {
 protected:
-	Graphics::MacFONTFont _macFonts[2];
+	Graphics::MacFontManager *_macFontManager;
+	const Graphics::Font *_macFonts[2];
 	bool _useRealCharWidth;
 	bool _useCorrectFontSpacing;
 	bool _pad;
 	int _lastTop;
 
-
 	int getDrawWidthIntern(uint16 chr) const;
 
 	void printCharInternal(int chr, int color, bool shadow, int x, int y);


Commit: 1d154df781d1ffbcd651d223fdfb1fb4b75e9b09
    https://github.com/scummvm/scummvm/commit/1d154df781d1ffbcd651d223fdfb1fb4b75e9b09
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Use Geneva for Indy 3 Mac GUI

Font rendering is broken at the moment. I have to figure out if it's a
problem with my code, or with the Mac Font class.

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


diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp
index 1ec77a306a6..cc5d352d185 100644
--- a/engines/scumm/charset.cpp
+++ b/engines/scumm/charset.cpp
@@ -1583,9 +1583,10 @@ CharsetRendererMac::CharsetRendererMac(ScummEngine *vm, const Common::String &fo
 
 	Common::String fontFamily = (_vm->_game.id == GID_LOOM) ? "Loom" : "Indy";
 	const Common::Array<Graphics::MacFontFamily *> &fontFamilies = _macFontManager->getFontFamilies();
+	int fontId = 0;
 	for (uint i = 0; i < fontFamilies.size(); i++) {
 		if (fontFamilies[i]->getName() == fontFamily) {
-			_macFontManager->registerFontName(fontFamilies[i]->getName(), fontFamilies[i]->getFontFamilyId());
+			fontId = _macFontManager->registerFontName(fontFamilies[i]->getName(), fontFamilies[i]->getFontFamilyId());
 			break;
 		}
 	}
@@ -1594,11 +1595,14 @@ CharsetRendererMac::CharsetRendererMac(ScummEngine *vm, const Common::String &fo
 		_macFonts[i] = nullptr;
 
 	if (_vm->_game.id == GID_INDY3) {
-		_macFonts[0] = _macFontManager->getFont(Graphics::MacFont(_macFontManager->getFontIdByName(fontFamily), 12));
-		_macFonts[1] = _macFontManager->getFont(Graphics::MacFont(_macFontManager->getFontIdByName(fontFamily), 9));
+		_macFonts[0] = _macFontManager->getFont(Graphics::MacFont(fontId, 12));
+		_macFonts[1] = _macFontManager->getFont(Graphics::MacFont(fontId, 9));
+		_macFonts[2] = _macFontManager->getFont(Graphics::MacFont(Graphics::kMacFontGeneva, 9));
+		_macFonts[3] = _macFontManager->getFont(Graphics::MacFont(Graphics::kMacFontGeneva, 9, Graphics::kMacFontBold));
+		_macFonts[4] = _macFontManager->getFont(Graphics::MacFont(Graphics::kMacFontGeneva, 9, Graphics::kMacFontBold | Graphics::kMacFontOutline));
 	} else {
-		_macFonts[0] = _macFontManager->getFont(Graphics::MacFont(_macFontManager->getFontIdByName(fontFamily), 13));
-		_macFonts[1] = _macFontManager->getFont(Graphics::MacFont(_macFontManager->getFontIdByName(fontFamily), 12));
+		_macFonts[0] = _macFontManager->getFont(Graphics::MacFont(fontId, 13));
+		_macFonts[1] = _macFontManager->getFont(Graphics::MacFont(fontId, 12));
 	}
 
 	if (_vm->_renderMode == Common::kRenderMacintoshBW) {
@@ -1635,15 +1639,20 @@ void CharsetRendererMac::setCurID(int32 id) {
 	// 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.
+	//
+	// Id 3 and upwards are used by the GUI.
 	if (_vm->_game.id == GID_INDY3) {
-		if (id == 1) {
+		if (id == 0 || id == 1) {
 			id = 0;
 		} else if (id == 2) {
 			id = 1;
+		} else if (id >= 3) {
+			id--;
+			_useRealCharWidth = true;
 		}
 	}
 
-	if (id > 1) {
+	if (id < 0 || id > ARRAYSIZE(_macFonts) || !_macFonts[id]) {
 		warning("CharsetRendererMac::setCurID(%d) - invalid charset", id);
 		id = 0;
 	}
diff --git a/engines/scumm/charset.h b/engines/scumm/charset.h
index 8f1590e7159..38d64ef8320 100644
--- a/engines/scumm/charset.h
+++ b/engines/scumm/charset.h
@@ -286,7 +286,7 @@ public:
 class CharsetRendererMac : public CharsetRendererCommon {
 protected:
 	Graphics::MacFontManager *_macFontManager;
-	const Graphics::Font *_macFonts[2];
+	const Graphics::Font *_macFonts[5];
 	bool _useRealCharWidth;
 	bool _useCorrectFontSpacing;
 	bool _pad;
diff --git a/engines/scumm/verbs.cpp b/engines/scumm/verbs.cpp
index f638d4050fd..c060df80191 100644
--- a/engines/scumm/verbs.cpp
+++ b/engines/scumm/verbs.cpp
@@ -1136,7 +1136,15 @@ void ScummEngine::drawVerb(int verb, int mode) {
 			return;
 
 		tmp = _charset->_center;
-		drawString(4, msg);
+
+		if (_game.id == GID_INDY3 && _game.platform == Common::kPlatformMacintosh && _macScreen) {
+			int oldId = _string[4].charset;
+			_string[4].charset = (vs->curmode == 2) ? 4 : 5;
+			drawString(4, msg);
+			_string[4].charset = oldId;
+		} else
+			drawString(4, msg);
+
 		_charset->_center = tmp;
 
 		if (isRtl)


Commit: 73101d6aab9aeb8f12054c2c79e73911d1f90c82
    https://github.com/scummvm/scummvm/commit/73101d6aab9aeb8f12054c2c79e73911d1f90c82
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Draw Indy 3 Mac outline font filled, and closer together

Changed paths:
    engines/scumm/charset.cpp


diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp
index cc5d352d185..5c9a0822079 100644
--- a/engines/scumm/charset.cpp
+++ b/engines/scumm/charset.cpp
@@ -1682,7 +1682,12 @@ int CharsetRendererMac::getStringWidth(int arg, const byte *text) {
 }
 
 int CharsetRendererMac::getDrawWidthIntern(uint16 chr) const {
-	return _macFonts[_curId]->getCharWidth(chr);
+	int width = _macFonts[_curId]->getCharWidth(chr);
+
+	// Indy 3 draws the button texts closer than outlined text usually is.
+	if (_vm->_game.id == GID_INDY3 && _curId == 4)
+		width--;
+	return width;
 }
 
 // HACK: Usually, we want the approximate width and height in the unscaled
@@ -1862,8 +1867,8 @@ void CharsetRendererMac::printChar(int chr, bool ignoreCharsetMask) {
 byte CharsetRendererMac::getTextColor() {
 	if (_vm->_renderMode == Common::kRenderMacintoshBW) {
 		// White and black can be rendered as is, and 8 is the color
-		// used for disabled text (verbs in Indy 3, notes in Loom).
-		// Everything else should be white.
+		// used for disabled text (notes in Loom). Everything else
+		// should be white.
 
 		if (_color == 0 || _color == 15 || _color == 8)
 			return _color;
@@ -1944,6 +1949,13 @@ void CharsetRendererMac::printCharInternal(int chr, int color, bool shadow, int
 			}
 		} 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, y, 0);
+				_macFonts[3]->drawChar(_vm->_macScreen, chr, x, y, 15);
+			}
 		}
 	}
 }


Commit: e22310c2ad8f97f0e0cb22aaf831c2b8f5938496
    https://github.com/scummvm/scummvm/commit/e22310c2ad8f97f0e0cb22aaf831c2b8f5938496
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
GRAPHICS: Fix building with DEBUGSCALING

Changed paths:
    graphics/fonts/macfont.cpp


diff --git a/graphics/fonts/macfont.cpp b/graphics/fonts/macfont.cpp
index a97fd51ba25..fcf409cf08a 100644
--- a/graphics/fonts/macfont.cpp
+++ b/graphics/fonts/macfont.cpp
@@ -594,7 +594,7 @@ MacFONTFont *MacFONTFont::scaleFont(const MacFONTFont *src, int newSize, int sla
 		int grayLevel = src->_data._fRectHeight * srcglyph->width / 4;
 
 #if DEBUGSCALING
-		int ccc = 'c';
+		uint ccc = 'c';
 #endif
 
 		MacGlyph *glyph = (i == src->_data._glyphs.size()) ? &data._defaultChar : &data._glyphs[i];
@@ -670,7 +670,7 @@ MacFONTFont *MacFONTFont::scaleFont(const MacFONTFont *src, int newSize, int sla
 			if (i == ccc) {
 				debugN(1, "--> %d ", grayLevel);
 
-				grayPtr = &dstGray[y * glyph->width];
+				int *grayPtr = &dstGray[y * glyph->width];
 				for (int x = 0; x < glyph->width; x++, grayPtr++)
 					debugN("%c", *grayPtr > grayLevel ? '#' : '.');
 			}


Commit: 5009bb67e4069771c696ae7a3117a0c83755dd07
    https://github.com/scummvm/scummvm/commit/5009bb67e4069771c696ae7a3117a0c83755dd07
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
GRAPHICS: Fix bold text to be more (hopefully exeactly) like the original

QuickDraw appears to simply draw the character once normally and once
shifted one pixel to the right. This was perhaps most noticeable on
small characters with lots of edges. (I noticed it first on "W" in
Geneva 9.)

Changed paths:
    graphics/fonts/macfont.cpp


diff --git a/graphics/fonts/macfont.cpp b/graphics/fonts/macfont.cpp
index fcf409cf08a..c629121d2e8 100644
--- a/graphics/fonts/macfont.cpp
+++ b/graphics/fonts/macfont.cpp
@@ -725,17 +725,8 @@ static void makeBold(Surface *src, int *dstGray, MacGlyph *glyph, int height) {
 		int *dst = &dstGray[y * glyph->bitmapWidth];
 
 		for (uint16 x = 0; x < glyph->bitmapWidth; x++, srcPtr++, dst++) {
-			bool left = x ? *(srcPtr - 1) == 1 : false;
-			bool center = *srcPtr == 1;
-			bool right = x > glyph->bitmapWidth - 1 ? false : *(srcPtr + 1) == 1;
-
-			bool edge, bold, res;
-
-			bold = center || left;
-			edge = !center && right;
-			res = (bold && !edge);
-
-			*dst = res ? 1 : 0;
+			*dst |= *srcPtr;
+			*(dst + 1) |= *srcPtr;
 		}
 	}
 }


Commit: 789f2dfaf1ac6c110893a980d632ac344e0dadcb
    https://github.com/scummvm/scummvm/commit/789f2dfaf1ac6c110893a980d632ac344e0dadcb
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
GRAPHICS: Make sure outline glyphs aren't cropped on the left side

This will offset all outlined glyphs one pixel to the right. I hope that
doesn't cause issues elsewhere.

Changed paths:
    graphics/fonts/macfont.cpp


diff --git a/graphics/fonts/macfont.cpp b/graphics/fonts/macfont.cpp
index c629121d2e8..61164dff54f 100644
--- a/graphics/fonts/macfont.cpp
+++ b/graphics/fonts/macfont.cpp
@@ -743,11 +743,11 @@ static void makeOutline(Surface *src, Surface *dst, MacGlyph *glyph, int height)
 		byte *dstPtr = (byte *)dst->getBasePtr(0, y);
 
 		for (uint16 x = 0; x < glyph->bitmapWidth; x++, dstPtr++, srcPtr++) {
-			if (*srcPtr)
+			if (x && *(srcPtr - 1))
 				continue;
 			// for every white pixel, if there is black pixel around it. It means that the white pixel is boundary, then we draw it as black pixel.
 			for (int i = 0; i < 8; i++) {
-				int nx = x + dx[i];
+				int nx = x + dx[i] - 1;
 				int ny = y + dy[i];
 				if (nx >= src->w || nx < 0 || ny >= src->h || ny < 0)
 					continue;


Commit: 3e0a726cb4500191ce57c893a929004803570c4b
    https://github.com/scummvm/scummvm/commit/3e0a726cb4500191ce57c893a929004803570c4b
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
GRAPHICS: Calculate bitmapOffset correctly

We have to take into consideration that text can use any combination of
slants. Otherwise, glyphs may spill into memory used for other glyphs.
This was probably only an issue when the glyph storage crossed the
boundary from one byte length into another.

Changed paths:
    graphics/fonts/macfont.cpp


diff --git a/graphics/fonts/macfont.cpp b/graphics/fonts/macfont.cpp
index 61164dff54f..a141bf5616c 100644
--- a/graphics/fonts/macfont.cpp
+++ b/graphics/fonts/macfont.cpp
@@ -559,13 +559,15 @@ MacFONTFont *MacFONTFont::scaleFont(const MacFONTFont *src, int newSize, int sla
 	int newBitmapWidth = 0;
 
 	// add the offset which we may use when we are making fonts
-	int bitmapOffset = 2;
+	int bitmapOffset = 0;
 
-	// for italic, we need to calc our self. for shadow, it's 3
-	// for bold and outline, it's 2
+	if (slant & kMacFontBold)
+		bitmapOffset++;
+	if (slant & kMacFontOutline)
+		bitmapOffset += 2;
 	if (slant & kMacFontItalic)
 		bitmapOffset = (data._fRectHeight - 1) / SLANTDEEP;
-	else if (slant & kMacFontShadow)
+	if (slant & kMacFontShadow)
 		bitmapOffset++;
 
 	for (uint i = 0; i < src->_data._glyphs.size() + 1; i++) {


Commit: e5e5f049be95e5cfcb127eacc4bf88b745211f28
    https://github.com/scummvm/scummvm/commit/e5e5f049be95e5cfcb127eacc4bf88b745211f28
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
GRAPHICS: Implement kMacFontCondense and kMacFontExtend

Judging by the experiments I made in a Mac emulator, condensed and
extended slant simply decreases and increases the glyph width by one.
You'd think it would depend on the font size, but apparently not. It is
possible to use both condensed and extended slant at the same time, and
it's just as useless as it sounds.

Changed paths:
    graphics/fonts/macfont.cpp


diff --git a/graphics/fonts/macfont.cpp b/graphics/fonts/macfont.cpp
index a141bf5616c..3111b42dd41 100644
--- a/graphics/fonts/macfont.cpp
+++ b/graphics/fonts/macfont.cpp
@@ -649,6 +649,12 @@ MacFONTFont *MacFONTFont::scaleFont(const MacFONTFont *src, int newSize, int sla
 			srcSurf.copyFrom(tmpSurf);
 		}
 
+		if (slant & kMacFontCondense)
+			glyph->width--;
+
+		if (slant & kMacFontExtend)
+			glyph->width++;
+
 		byte *ptr = &data._bitImage[glyph->bitmapOffset / 8];
 
 		for (int y = 0; y < data._fRectHeight; y++) {


Commit: babcb19088315ee6813fec6e82dbf54b7494feed
    https://github.com/scummvm/scummvm/commit/babcb19088315ee6813fec6e82dbf54b7494feed
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Update Indy 3 verb drawing for the latest Mac font changes

Changed paths:
    engines/scumm/charset.cpp


diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp
index 5c9a0822079..372ec6b7e8f 100644
--- a/engines/scumm/charset.cpp
+++ b/engines/scumm/charset.cpp
@@ -1599,7 +1599,7 @@ CharsetRendererMac::CharsetRendererMac(ScummEngine *vm, const Common::String &fo
 		_macFonts[1] = _macFontManager->getFont(Graphics::MacFont(fontId, 9));
 		_macFonts[2] = _macFontManager->getFont(Graphics::MacFont(Graphics::kMacFontGeneva, 9));
 		_macFonts[3] = _macFontManager->getFont(Graphics::MacFont(Graphics::kMacFontGeneva, 9, Graphics::kMacFontBold));
-		_macFonts[4] = _macFontManager->getFont(Graphics::MacFont(Graphics::kMacFontGeneva, 9, Graphics::kMacFontBold | Graphics::kMacFontOutline));
+		_macFonts[4] = _macFontManager->getFont(Graphics::MacFont(Graphics::kMacFontGeneva, 9, Graphics::kMacFontBold | Graphics::kMacFontOutline | Graphics::kMacFontCondense));
 	} else {
 		_macFonts[0] = _macFontManager->getFont(Graphics::MacFont(fontId, 13));
 		_macFonts[1] = _macFontManager->getFont(Graphics::MacFont(fontId, 12));
@@ -1682,12 +1682,7 @@ int CharsetRendererMac::getStringWidth(int arg, const byte *text) {
 }
 
 int CharsetRendererMac::getDrawWidthIntern(uint16 chr) const {
-	int width = _macFonts[_curId]->getCharWidth(chr);
-
-	// Indy 3 draws the button texts closer than outlined text usually is.
-	if (_vm->_game.id == GID_INDY3 && _curId == 4)
-		width--;
-	return width;
+	return _macFonts[_curId]->getCharWidth(chr);
 }
 
 // HACK: Usually, we want the approximate width and height in the unscaled
@@ -1953,8 +1948,8 @@ void CharsetRendererMac::printCharInternal(int chr, int color, bool shadow, int
 			// 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, y, 0);
-				_macFonts[3]->drawChar(_vm->_macScreen, chr, x, y, 15);
+				_macFonts[3]->drawChar(&_vm->_textSurface, chr, x + 1, y, 0);
+				_macFonts[3]->drawChar(_vm->_macScreen, chr, x + 1, y, 15);
 			}
 		}
 	}


Commit: c6d1a256efef9d0f5860660d27babc70e9a420a3
    https://github.com/scummvm/scummvm/commit/c6d1a256efef9d0f5860660d27babc70e9a420a3
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Moved Mac Indy 3 GUI into its own class

This is still just a mock-up, and a bad one at that. It will all be for
nought unless I can figure out how to wire up these buttons to the SCUMM
verbs.

Changed paths:
  A engines/scumm/gfx_mac.h
    engines/scumm/charset.cpp
    engines/scumm/charset.h
    engines/scumm/gfx_mac.cpp
    engines/scumm/input.cpp
    engines/scumm/saveload.cpp
    engines/scumm/script.cpp
    engines/scumm/scumm.cpp
    engines/scumm/scumm.h
    engines/scumm/verbs.cpp


diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp
index 372ec6b7e8f..0ab392fccda 100644
--- a/engines/scumm/charset.cpp
+++ b/engines/scumm/charset.cpp
@@ -20,7 +20,6 @@
  */
 
 #include "graphics/fonts/macfont.h"
-#include "graphics/macgui/macwindowmanager.h"
 #include "graphics/macgui/macfontmanager.h"
 
 #include "scumm/charset.h"
@@ -1578,15 +1577,16 @@ CharsetRendererMac::CharsetRendererMac(ScummEngine *vm, const Common::String &fo
 	// 60 is an upside-down note, i.e. the one used for c'.
 	// 95 is a used for the rest of the notes.
 
-	_macFontManager = new Graphics::MacFontManager(0, Common::Language::UNK_LANG);
-	_macFontManager->loadFonts(fontFile);
+	Graphics::MacFontManager *mfm = _vm->_macFontManager;
+
+	mfm->loadFonts(fontFile);
 
 	Common::String fontFamily = (_vm->_game.id == GID_LOOM) ? "Loom" : "Indy";
-	const Common::Array<Graphics::MacFontFamily *> &fontFamilies = _macFontManager->getFontFamilies();
+	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 = _macFontManager->registerFontName(fontFamilies[i]->getName(), fontFamilies[i]->getFontFamilyId());
+			fontId = mfm->registerFontName(fontFamilies[i]->getName(), fontFamilies[i]->getFontFamilyId());
 			break;
 		}
 	}
@@ -1595,14 +1595,11 @@ CharsetRendererMac::CharsetRendererMac(ScummEngine *vm, const Common::String &fo
 		_macFonts[i] = nullptr;
 
 	if (_vm->_game.id == GID_INDY3) {
-		_macFonts[0] = _macFontManager->getFont(Graphics::MacFont(fontId, 12));
-		_macFonts[1] = _macFontManager->getFont(Graphics::MacFont(fontId, 9));
-		_macFonts[2] = _macFontManager->getFont(Graphics::MacFont(Graphics::kMacFontGeneva, 9));
-		_macFonts[3] = _macFontManager->getFont(Graphics::MacFont(Graphics::kMacFontGeneva, 9, Graphics::kMacFontBold));
-		_macFonts[4] = _macFontManager->getFont(Graphics::MacFont(Graphics::kMacFontGeneva, 9, Graphics::kMacFontBold | Graphics::kMacFontOutline | Graphics::kMacFontCondense));
+		_macFonts[0] = mfm->getFont(Graphics::MacFont(fontId, 12));
+		_macFonts[1] = mfm->getFont(Graphics::MacFont(fontId, 9));
 	} else {
-		_macFonts[0] = _macFontManager->getFont(Graphics::MacFont(fontId, 13));
-		_macFonts[1] = _macFontManager->getFont(Graphics::MacFont(fontId, 12));
+		_macFonts[0] = mfm->getFont(Graphics::MacFont(fontId, 13));
+		_macFonts[1] = mfm->getFont(Graphics::MacFont(fontId, 12));
 	}
 
 	if (_vm->_renderMode == Common::kRenderMacintoshBW) {
@@ -1624,7 +1621,6 @@ CharsetRendererMac::~CharsetRendererMac() {
 		_glyphSurface->free();
 		delete _glyphSurface;
 	}
-	delete _macFontManager;
 }
 
 void CharsetRendererMac::setCurID(int32 id) {
@@ -1639,16 +1635,11 @@ void CharsetRendererMac::setCurID(int32 id) {
 	// 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.
-	//
-	// Id 3 and upwards are used by the GUI.
 	if (_vm->_game.id == GID_INDY3) {
 		if (id == 0 || id == 1) {
 			id = 0;
 		} else if (id == 2) {
 			id = 1;
-		} else if (id >= 3) {
-			id--;
-			_useRealCharWidth = true;
 		}
 	}
 
diff --git a/engines/scumm/charset.h b/engines/scumm/charset.h
index 38d64ef8320..089e2c5a7bd 100644
--- a/engines/scumm/charset.h
+++ b/engines/scumm/charset.h
@@ -30,7 +30,6 @@
 #include "scumm/gfx.h"
 
 namespace Graphics {
-class MacFontManager;
 class Font;
 }
 
@@ -285,8 +284,7 @@ public:
 
 class CharsetRendererMac : public CharsetRendererCommon {
 protected:
-	Graphics::MacFontManager *_macFontManager;
-	const Graphics::Font *_macFonts[5];
+	const Graphics::Font *_macFonts[2];
 	bool _useRealCharWidth;
 	bool _useCorrectFontSpacing;
 	bool _pad;
diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 7e5a4ec65f2..2e13da84682 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -20,9 +20,14 @@
  */
 
 #include "common/system.h"
+
 #include "graphics/macega.h"
+#include "graphics/fonts/macfont.h"
+#include "graphics/macgui/macfontmanager.h"
+
 #include "scumm/actor.h"
 #include "scumm/charset.h"
+#include "scumm/gfx_mac.h"
 #include "scumm/usage_bits.h"
 #include "scumm/verbs.h"
 
@@ -49,6 +54,9 @@ 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
+	if (vs->number == kVerbVirtScreen && VAR(VAR_VERB_SCRIPT) == 4)
+		return;
 
 	const byte *pixels = vs->getPixels(x, top);
 	const byte *ts = (byte *)_textSurface.getBasePtr(x * 2, y * 2);
@@ -350,4 +358,70 @@ Common::KeyState ScummEngine::mac_showOldStyleBannerAndPause(const char *msg, in
 	return ks;
 }
 
+MacIndy3Gui::MacIndy3Gui(OSystem *system, ScummEngine *vm) :
+	_system(system), _vm(vm), _macScreen(vm->_macScreen) {
+	Graphics::MacFontManager *mfm = _vm->_macFontManager;
+
+	_fonts[0] = mfm->getFont(Graphics::MacFont(Graphics::kMacFontGeneva, 9));
+	_fonts[1] = mfm->getFont(Graphics::MacFont(Graphics::kMacFontGeneva, 9, Graphics::kMacFontBold));
+	_fonts[2] = mfm->getFont(Graphics::MacFont(Graphics::kMacFontGeneva, 9, Graphics::kMacFontBold | Graphics::kMacFontOutline | Graphics::kMacFontCondense));
+}
+
+MacIndy3Gui::~MacIndy3Gui() {
+}
+
+void MacIndy3Gui::handleEvent(Common::Event &event) {
+	if (event.type != Common::EVENT_LBUTTONDOWN)
+		return;
+
+	debug("Handle Indy 3 click at %d, %d", event.mouse.x, event.mouse.y);
+}
+
+void MacIndy3Gui::clear() {
+	_macScreen->fillRect(Common::Rect(0, 288, 640, 289), 0);
+	_macScreen->fillRect(Common::Rect(0, 290, 640, 373), 7);
+
+	byte corner[] = {
+		0, 0, 0, 0,
+		0, 0, 7, 7,
+		0, 7, 7, 7,
+		0, 7, 7, 7
+	};
+
+	byte *ul = (byte *)_macScreen->getBasePtr(0, 290);
+	byte *ur = (byte *)_macScreen->getBasePtr(636, 290);
+	byte *ll = (byte *)_macScreen->getBasePtr(0, 369);
+	byte *lr = (byte *)_macScreen->getBasePtr(636, 369);
+
+	int pitch = _macScreen->pitch;
+
+	for (int y = 0; y < 4; y++) {
+		for (int x = 0; x < 4; x++) {
+			*(ul + y * pitch + x) = corner[y * 4 + x];
+			*(ur + y * pitch + x) = corner[y * 4 + (3 - x)];
+			*(ll + y * pitch + x) = corner[(3 - y) * 4 + x];
+			*(lr + y * pitch + x) = corner[(3 - y) * 4 + (3 - x)];
+		}
+	}
+
+	for (int i = 0; i < 15; i++)
+		drawButton(i, false);
+
+	_system->copyRectToScreen(_macScreen->getBasePtr(0, 288), _macScreen->pitch, 0, 288, 640, 112);
+}
+
+void MacIndy3Gui::drawButton(int n, bool pressed) {
+	int x = _buttons[n].x;
+	int y = _buttons[n].y;
+	int w = _buttons[n].w;
+	int h = _buttons[n].h;
+
+	_macScreen->fillRect(Common::Rect(x + 1, y + 1, x + w, y + h), 15);
+
+	_macScreen->hLine(x, y, x + w, 0);
+	_macScreen->hLine(x, y + h, x + w, 0);
+	_macScreen->vLine(x, y, y + h, 0);
+	_macScreen->vLine(x + w, y, y + h, 0);
+}
+
 } // End of namespace Scumm
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
new file mode 100644
index 00000000000..d5d5a5f39b8
--- /dev/null
+++ b/engines/scumm/gfx_mac.h
@@ -0,0 +1,84 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SCUMM_GFX_MAC_H
+#define SCUMM_GFX_MAC_H
+
+class OSystem;
+
+namespace Graphics {
+class Surface;
+class Font;
+}
+
+namespace Scumm {
+
+class ScummEngine;
+
+class MacIndy3Gui {
+public:
+	MacIndy3Gui(OSystem *system, ScummEngine *vm);
+	~MacIndy3Gui();
+	
+	void handleEvent(Common::Event &event);
+	void clear();
+	void drawButton(int n, bool pressed);
+
+private:
+	OSystem *_system;
+	ScummEngine *_vm;
+	Graphics::Surface *_macScreen;
+	const Graphics::Font *_fonts[3];
+
+	struct GuiButtonData {
+		int x;
+		int y;
+		int w;
+		int h;
+	};
+
+	const GuiButtonData _buttons[20] = {
+		{  67, 292, 348, 18 }, // Sentence line
+		{  67, 312,  68, 18 }, // Push
+		{  67, 332,  68, 18 }, // Pull
+		{  67, 352,  68, 18 }, // Give
+		{ 137, 312,  68, 18 }, // Open
+		{ 137, 332,  68, 18 }, // Close
+		{ 137, 352,  68, 18 }, // Look at
+		{ 207, 312,  68, 18 }, // Walk to
+		{ 207, 332,  68, 18 }, // Pick up
+		{ 207, 352,  68, 18 }, // What is
+		{ 277, 312,  68, 18 }, // Use
+		{ 277, 332,  68, 18 }, // Turn on
+		{ 277, 352,  68, 18 }, // Turn off
+		{ 347, 312,  68, 18 }, // Talk
+		{ 347, 332,  68, 18 }, // Travel
+		{  67, 292, 507, 18 }, // Conversation 1
+		{  67, 312, 507, 18 }, // Conversation 2
+		{  67, 332, 507, 18 }, // Conversation 3
+		{  67, 352, 151, 18 }, // Conversation 4
+		{ 423, 352, 151, 18 }  // Conversation 5
+	};
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/input.cpp b/engines/scumm/input.cpp
index 2c3699ecc8e..c4c1b018b91 100644
--- a/engines/scumm/input.cpp
+++ b/engines/scumm/input.cpp
@@ -27,6 +27,7 @@
 
 #include "scumm/debugger.h"
 #include "scumm/dialogs.h"
+#include "scumm/gfx_mac.h"
 #include "scumm/insane/insane.h"
 #include "scumm/imuse/imuse.h"
 #include "scumm/imuse_digi/dimuse_engine.h"
@@ -219,6 +220,10 @@ void ScummEngine::parseEvent(Common::Event event) {
 		_mouse.x = event.mouse.x;
 		_mouse.y = event.mouse.y;
 
+		// Handle Mac Indy3 events before scaling the mouse coordinates
+		if (_macIndy3Gui)
+			_macIndy3Gui->handleEvent(event);
+
 		if (_renderMode == Common::kRenderHercA || _renderMode == Common::kRenderHercG) {
 			_mouse.x -= (kHercWidth - _screenWidth * 2) / 2;
 			_mouse.x >>= 1;
diff --git a/engines/scumm/saveload.cpp b/engines/scumm/saveload.cpp
index e6c98be983b..74b656e1913 100644
--- a/engines/scumm/saveload.cpp
+++ b/engines/scumm/saveload.cpp
@@ -27,6 +27,7 @@
 
 #include "scumm/actor.h"
 #include "scumm/charset.h"
+#include "scumm/gfx_mac.h"
 #include "scumm/imuse_digi/dimuse_engine.h"
 #include "scumm/imuse/imuse.h"
 #include "scumm/players/player_towns.h"
@@ -865,6 +866,9 @@ bool ScummEngine::loadState(int slot, bool compat, Common::String &filename) {
 		_macScreen->fillRect(Common::Rect(_macScreen->w, _macScreen->h), 0);
 	clearTextSurface();
 
+	if (_macIndy3Gui && VAR(VAR_VERB_SCRIPT) == 4)
+		_macIndy3Gui->clear();
+
 	_lastCodePtr = nullptr;
 	_drawObjectQueNr = 0;
 	_verbMouseOver = 0;
diff --git a/engines/scumm/script.cpp b/engines/scumm/script.cpp
index 332f7f232f0..8d8ef426c49 100644
--- a/engines/scumm/script.cpp
+++ b/engines/scumm/script.cpp
@@ -24,6 +24,7 @@
 #include "common/system.h"
 
 #include "scumm/actor.h"
+#include "scumm/gfx_mac.h"
 #include "scumm/object.h"
 #include "scumm/resource.h"
 #include "scumm/util.h"
@@ -759,6 +760,12 @@ void ScummEngine::writeVar(uint var, int value) {
 			}
 		}
 
+		// I don't know how the original did it, but we trigger the
+		// Indy3 Mac GUI based on the verb script.
+
+		if (_macIndy3Gui && var == VAR_VERB_SCRIPT && value == 4)
+			_macIndy3Gui->clear();
+
 		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 b5334bd80e2..7619b214db2 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -36,6 +36,7 @@
 #include "gui/message.h"
 
 #include "graphics/cursorman.h"
+#include "graphics/macgui/macfontmanager.h"
 
 #include "scumm/akos.h"
 #include "scumm/charset.h"
@@ -45,6 +46,7 @@
 #include "scumm/dialogs.h"
 #include "scumm/file.h"
 #include "scumm/file_nes.h"
+#include "scumm/gfx_mac.h"
 #include "scumm/imuse/imuse.h"
 #include "scumm/imuse_digi/dimuse_engine.h"
 #include "scumm/smush/smush_player.h"
@@ -427,6 +429,7 @@ ScummEngine::~ScummEngine() {
 	for (int i = 0; i < 20; i++)
 		if (_2byteMultiFontPtr[i])
 			delete _2byteMultiFontPtr[i];
+	delete _macFontManager;
 	delete _charset;
 	delete _messageDialog;
 	delete _pauseDialog;
@@ -474,6 +477,8 @@ ScummEngine::~ScummEngine() {
 		delete _macIndy3TextBox;
 	}
 
+	delete _macIndy3Gui;
+
 #ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
 	delete _townsScreen;
 #ifdef USE_RGB_COLOR
@@ -1593,9 +1598,10 @@ void ScummEngine::setupCharsetRenderer(const Common::String &macFontFile) {
 #endif
 		if (_game.platform == Common::kPlatformFMTowns)
 			_charset = new CharsetRendererTownsV3(this);
-		else if (_game.platform == Common::kPlatformMacintosh && !macFontFile.empty())
+		else if (_game.platform == Common::kPlatformMacintosh && !macFontFile.empty()) {
+			_macFontManager = new Graphics::MacFontManager(0, Common::Language::UNK_LANG);
 			_charset = new CharsetRendererMac(this, macFontFile);
-		else
+		} else
 			_charset = new CharsetRendererV3(this);
 #ifdef ENABLE_SCUMM_7_8
 	} else if (_game.version == 7) {
@@ -1675,6 +1681,7 @@ void ScummEngine::resetScumm() {
 
 	if (_macIndy3TextBox) {
 		_macIndy3TextBox->fillRect(Common::Rect(_macIndy3TextBox->w, _macIndy3TextBox->h), 0);
+		_macIndy3Gui = new MacIndy3Gui(_system, this);
 	}
 
 	if (_game.version == 0) {
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index 918af67abc7..0b336e879f0 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -67,6 +67,7 @@ class SeekableWriteStream;
 }
 namespace Graphics {
 class FontSJIS;
+class MacFontManager;
 }
 
 /**
@@ -90,6 +91,7 @@ class BaseScummFile;
 class CharsetRenderer;
 class IMuse;
 class IMuseDigital;
+class MacIndy3Gui;
 class MusicEngine;
 class Player_Towns;
 class ScummEngine;
@@ -1559,8 +1561,11 @@ public:
 	 */
 	Graphics::Surface _textSurface;
 	int _textSurfaceMultiplier = 0;
+
+	Graphics::MacFontManager *_macFontManager = nullptr;
 	Graphics::Surface *_macScreen = nullptr;
 	Graphics::Surface *_macIndy3TextBox = nullptr;
+	MacIndy3Gui *_macIndy3Gui = nullptr;
 
 protected:
 	byte _charsetColor = 0;
diff --git a/engines/scumm/verbs.cpp b/engines/scumm/verbs.cpp
index c060df80191..d6aeb682cc2 100644
--- a/engines/scumm/verbs.cpp
+++ b/engines/scumm/verbs.cpp
@@ -1078,6 +1078,9 @@ void ScummEngine::drawVerb(int verb, int mode) {
 	if (!verb)
 		return;
 
+	if (_macIndy3Gui)
+		return;
+
 	// The way we implement high-resolution font on a scaled low-resolution
 	// background requires there to always be a text surface telling which
 	// pixels have been drawn on. This means that the "has mask" feature is
@@ -1136,15 +1139,7 @@ void ScummEngine::drawVerb(int verb, int mode) {
 			return;
 
 		tmp = _charset->_center;
-
-		if (_game.id == GID_INDY3 && _game.platform == Common::kPlatformMacintosh && _macScreen) {
-			int oldId = _string[4].charset;
-			_string[4].charset = (vs->curmode == 2) ? 4 : 5;
-			drawString(4, msg);
-			_string[4].charset = oldId;
-		} else
-			drawString(4, msg);
-
+		drawString(4, msg);
 		_charset->_center = tmp;
 
 		if (isRtl)


Commit: c13771736aa9ef39e9ea4599e3cc6f7aefbf46bd
    https://github.com/scummvm/scummvm/commit/c13771736aa9ef39e9ea4599e3cc6f7aefbf46bd
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Add more drawing code for the Indy 3 Mac GUI

This contains code to mock-up the interface, but I'll remove that later.
At the moment, it's just too useful to not have.

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 2e13da84682..83c2dae0fbb 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -379,49 +379,128 @@ void MacIndy3Gui::handleEvent(Common::Event &event) {
 
 void MacIndy3Gui::clear() {
 	_macScreen->fillRect(Common::Rect(0, 288, 640, 289), 0);
-	_macScreen->fillRect(Common::Rect(0, 290, 640, 373), 7);
+	fill(Common::Rect(0, 290, 640, 373));
 
 	byte corner[] = {
-		0, 0, 0, 0,
-		0, 0, 7, 7,
-		0, 7, 7, 7,
-		0, 7, 7, 7
+		1, 1, 1, 1,
+		1, 1, 0, 0,
+		1, 0, 0, 0,
+		1, 0, 0, 0
 	};
 
 	byte *ul = (byte *)_macScreen->getBasePtr(0, 290);
-	byte *ur = (byte *)_macScreen->getBasePtr(636, 290);
-	byte *ll = (byte *)_macScreen->getBasePtr(0, 369);
-	byte *lr = (byte *)_macScreen->getBasePtr(636, 369);
+	byte *ur = (byte *)_macScreen->getBasePtr(639, 290);
+	byte *ll = (byte *)_macScreen->getBasePtr(0, 372);
+	byte *lr = (byte *)_macScreen->getBasePtr(639, 372);
 
 	int pitch = _macScreen->pitch;
 
 	for (int y = 0; y < 4; y++) {
 		for (int x = 0; x < 4; x++) {
-			*(ul + y * pitch + x) = corner[y * 4 + x];
-			*(ur + y * pitch + x) = corner[y * 4 + (3 - x)];
-			*(ll + y * pitch + x) = corner[(3 - y) * 4 + x];
-			*(lr + y * pitch + x) = corner[(3 - y) * 4 + (3 - x)];
+			if (corner[y * 4 + x]) {
+				*(ul + y * pitch + x) = 0;
+				*(ur + y * pitch - x) = 0;
+				*(ll - y * pitch + x) = 0;
+				*(lr - y * pitch - x) = 0;
+			}
 		}
 	}
 
+	const char *text[] = {
+		"Drawn by ScummVM, but just a mock-up",
+		"Push", "Pull", "Give",
+		"Open", "Close", "Look at",
+		"Walk to", "Pick up", "What is",
+		"Use", "Turn on", "Turn off",
+		"Talk", "Travel"
+	};
+
 	for (int i = 0; i < 15; i++)
-		drawButton(i, false);
+		drawButton(i, (char *)text[i], i < 13, i == 8);
 
 	_system->copyRectToScreen(_macScreen->getBasePtr(0, 288), _macScreen->pitch, 0, 288, 640, 112);
 }
 
-void MacIndy3Gui::drawButton(int n, bool pressed) {
+void MacIndy3Gui::fill(Common::Rect r) {
+	if (_vm->_renderMode == Common::kRenderMacintoshBW) {
+		byte *row = (byte *)_macScreen->getBasePtr(r.left, r.top);
+		int pitch = _macScreen->pitch;
+
+		for (int y = r.top; y < r.bottom; y++) {
+			byte *ptr = row;
+			for (int x = r.left; x < r.right; x++) {
+				*ptr++ = ((x + y) & 1) ? 15 : 0;
+			}
+			row += pitch;
+		}
+	} else
+		_macScreen->fillRect(r, 7);
+}
+
+void MacIndy3Gui::drawButton(int n, char *text, bool enabled, bool pressed) {
 	int x = _buttons[n].x;
 	int y = _buttons[n].y;
 	int w = _buttons[n].w;
 	int h = _buttons[n].h;
 
-	_macScreen->fillRect(Common::Rect(x + 1, y + 1, x + w, y + h), 15);
+	fill(Common::Rect(x, y, x + w, y + h));
 
-	_macScreen->hLine(x, y, x + w, 0);
-	_macScreen->hLine(x, y + h, x + w, 0);
-	_macScreen->vLine(x, y, y + h, 0);
-	_macScreen->vLine(x + w, y, y + h, 0);
+	if (!pressed) {
+		_macScreen->hLine(x + 1, y, x + w - 3, 0);
+		_macScreen->hLine(x + 1, y + h - 2, x + w - 3, 0);
+		_macScreen->vLine(x, y + 1, y + h - 3, 0);
+		_macScreen->vLine(x + w - 2, y + 1, y + h - 3, 0);
+
+		_macScreen->hLine(x + 2, y + h - 1, x + w - 1, 0);
+		_macScreen->vLine(x + w - 1, y + 2, y + h - 1, 0);
+		_macScreen->hLine(x + 1, y + 1, x + w - 3, 15);
+		_macScreen->vLine(x + 1, y + 2, y + h - 3, 15);
+	} else {
+		// I have only been able to capture a screenshot of the pressed
+		// button in black and white, where the checkerboard background
+		// makes it hard to see exactly which pixels should be drawn.
+		// Basilisk II runs it too fast, and I haven't gotten Mini vMac
+		// to run it in 16-color mode.
+		//
+		// All I can say for certain is that the upper left corner is
+		// rounded while the lower right is not. I'm going to assume
+		// that the shadow is always drawn, and the rest of the button
+		// is just shifted down to the right. That would make the other
+		// two corners rounded.
+		_macScreen->hLine(x + 2, y + 1, x + w - 2, 0);
+		_macScreen->hLine(x + 2, y + h - 1, x + w - 1, 0);
+		_macScreen->vLine(x + 1, y + 2, y + h - 2, 0);
+		_macScreen->vLine(x + w - 1, y + 2, y + h - 2, 0);
+
+		_macScreen->hLine(x + 2, y + 2, x + w - 2, 15);
+		_macScreen->vLine(x + 2, y + 3, y + h - 2, 15);
+	}
+
+	// The text is drawn centered. Based on experimentation, I think the
+	// width is always based on the outlined font, and the button shadow is
+	// not counted as part of the button width.
+	//
+	// This gives us pixel perfect rendering for the English verbs.
+
+	int stringWidth = 0;
+	for (int i = 0; text[i]; i++)
+		stringWidth += _fonts[2]->getCharWidth(text[i]);
+
+	int textX = (x + (_buttons[n].w - 1 - stringWidth) / 2) - 1;
+	int textY = y + 2;
+	int color = enabled ? 15 : 0;
+
+	if (pressed) {
+		textX++;
+		textY++;
+	}
+
+	for (int i = 0; text[i]; i++) {
+		if (enabled)
+			_fonts[2]->drawChar(_macScreen, text[i], textX, textY, 0);
+		_fonts[1]->drawChar(_macScreen, text[i], textX + 1, textY, color);
+		textX += _fonts[2]->getCharWidth(text[i]);
+	}
 }
 
 } // End of namespace Scumm
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index d5d5a5f39b8..857b605626f 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -40,7 +40,7 @@ public:
 	
 	void handleEvent(Common::Event &event);
 	void clear();
-	void drawButton(int n, bool pressed);
+	void drawButton(int n, char *text, bool enabled, bool pressed);
 
 private:
 	OSystem *_system;
@@ -77,6 +77,8 @@ private:
 		{  67, 352, 151, 18 }, // Conversation 4
 		{ 423, 352, 151, 18 }  // Conversation 5
 	};
+
+	void fill(Common::Rect r);
 };
 
 } // End of namespace Scumm


Commit: a9f26e96312a43494b9b5ee62b1e69f387bf6c1b
    https://github.com/scummvm/scummvm/commit/a9f26e96312a43494b9b5ee62b1e69f387bf6c1b
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: More Indy 3 Mac widget stuff

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 83c2dae0fbb..c1cc880048e 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -381,7 +381,7 @@ void MacIndy3Gui::clear() {
 	_macScreen->fillRect(Common::Rect(0, 288, 640, 289), 0);
 	fill(Common::Rect(0, 290, 640, 373));
 
-	byte corner[] = {
+	const byte corner[] = {
 		1, 1, 1, 1,
 		1, 1, 0, 0,
 		1, 0, 0, 0,
@@ -452,7 +452,8 @@ void MacIndy3Gui::drawButton(int n, char *text, bool enabled, bool pressed) {
 		_macScreen->vLine(x + w - 2, y + 1, y + h - 3, 0);
 
 		_macScreen->hLine(x + 2, y + h - 1, x + w - 1, 0);
-		_macScreen->vLine(x + w - 1, y + 2, y + h - 1, 0);
+		_macScreen->vLine(x + w - 1, y + 2, y + h - 2, 0);
+
 		_macScreen->hLine(x + 1, y + 1, x + w - 3, 15);
 		_macScreen->vLine(x + 1, y + 2, y + h - 3, 15);
 	} else {
@@ -501,6 +502,142 @@ void MacIndy3Gui::drawButton(int n, char *text, bool enabled, bool pressed) {
 		_fonts[1]->drawChar(_macScreen, text[i], textX + 1, textY, color);
 		textX += _fonts[2]->getCharWidth(text[i]);
 	}
+
+	drawInventoryWidget();
+}
+
+// Desired behavior:
+//
+// The drag handle of the scrollbar never changes size. It's only drawn when
+// there are enough inventory items to scroll.
+//
+// The size of the scroll handle is fixed, not scaled to indicate the number of
+// objects in your inventory.
+//
+// The exact positions of the scroll handle are not yet known. Probably just a
+// simple calculation.
+//
+// Clicking on an arrow scrolls up or down by one row. Clicking and holding
+// scrolls the inventory. The time between scrolling is constant, i.e. the
+// first delay is not different. The delay seems to depend on the speed of the
+// Mac, so pick something that feels right.
+//
+// Clicking above or below the handle scrolls up or down by - probably - one
+// page. I've never seen the inventory full enough for this to mean anything
+// else than scrolling to the top or bottom.
+//
+// Dragging the handle is not possible. Clicking on the handle is probably
+// indistinguishable from clicking above or below.
+
+void MacIndy3Gui::drawInventoryWidget() {
+	fill(Common::Rect(417, 292, 574, 370));
+
+	// Main outline and shadow
+	_macScreen->hLine(418, 292, 571, 0);
+	_macScreen->hLine(418, 368, 571, 0);
+	_macScreen->vLine(417, 293, 367, 0);
+	_macScreen->vLine(572, 293, 367, 0);
+
+	_macScreen->hLine(419, 369, 573, 0);
+	_macScreen->vLine(573, 294, 368, 0);
+
+	_macScreen->hLine(418, 293, 571, 15);
+	_macScreen->vLine(418, 294, 367, 15);
+
+	// Inner frame
+	_macScreen->hLine(421, 296, 551, 0);
+	_macScreen->hLine(421, 297, 551, 0);
+	_macScreen->hLine(421, 365, 551, 0);
+	_macScreen->vLine(421, 298, 364, 0);
+	_macScreen->vLine(422, 298, 364, 0);
+	_macScreen->vLine(551, 298, 364, 0);
+
+	_macScreen->fillRect(Common::Rect(423, 298, 551, 365), 15);
+
+	// Scrollbar, outer frame
+	_macScreen->hLine(554, 296, 569, 0);
+	_macScreen->hLine(554, 365, 569, 0);
+	_macScreen->vLine(554, 297, 364, 0);
+	_macScreen->vLine(569, 297, 364, 0);
+
+	_macScreen->hLine(555, 311, 568, 0);
+	_macScreen->hLine(555, 350, 568, 0);
+
+	drawInventoryScrollbar();
+
+	_macScreen->hLine(555, 297, 568, 15);
+	_macScreen->vLine(555, 298, 310, 15);
+	_macScreen->hLine(555, 351, 568, 15);
+	_macScreen->vLine(555, 352, 364, 15);
+
+	drawInventoryArrowUp(false);
+	drawInventoryArrowDown(false);
+}
+
+void MacIndy3Gui::drawInventoryArrow(int arrowX, int arrowY, bool highlighted, bool flipped) {
+	const byte arrow[110] = {
+		0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0,
+		0, 0, 0, 1, 2, 2, 2, 1, 0, 0, 0,
+		0, 0, 1, 2, 2, 2, 2, 2, 1, 0, 0,
+		0, 1, 2, 2, 2, 2, 2, 2, 2, 1, 0,
+		1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1,
+		0, 0, 1, 2, 2, 2, 2, 2, 1, 0, 0,
+		0, 0, 1, 2, 2, 2, 2, 2, 1, 0, 0,
+		0, 0, 1, 2, 2, 2, 2, 2, 1, 0, 0,
+		0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0
+	};
+
+	byte palette[] = { 0, 15 };
+
+	if (highlighted)
+		palette[1] = 0;
+
+	int y0, y1, yd;
+
+	if (flipped) {
+		y0 = 9;
+		y1 = 0;
+		yd = -1;
+	} else {
+		y0 = 0;
+		y1 = 9;
+		yd = 1;
+	}
+
+	byte *ptr = (byte *)_macScreen->getBasePtr(arrowX, arrowY);
+	int pitch = _macScreen->pitch;
+
+	int y = y0 - yd;
+	do {
+		y += yd;
+		for (int x = 0; x < 11; x++) {
+			byte color = arrow[11 * y + x];
+			if (color)
+				ptr[x] = palette[color - 1];
+		}
+		ptr += pitch;
+	} while (y != y1);
+}
+
+void MacIndy3Gui::drawInventoryArrowUp(bool highlighted) {
+	drawInventoryArrow(557, 299, highlighted, false);
+}
+
+void MacIndy3Gui::drawInventoryArrowDown(bool highlighted) {
+	drawInventoryArrow(557, 354, highlighted, true);
+}
+
+void MacIndy3Gui::drawInventoryScrollbar() {
+	_macScreen->hLine(555, 312, 568, 0);
+	_macScreen->vLine(555, 313, 349, 0);
+
+	fill(Common::Rect(556, 313, 569, 350));
+}
+
+#if 0
+void MacIndy3Gui::drawInventoryText(int slot, char *text) {
 }
+#endif
 
 } // End of namespace Scumm
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 857b605626f..1dc38d2a7de 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -41,6 +41,10 @@ public:
 	void handleEvent(Common::Event &event);
 	void clear();
 	void drawButton(int n, char *text, bool enabled, bool pressed);
+	void drawInventoryWidget();
+	void drawInventoryScrollbar();
+	void drawInventoryArrowUp(bool highlight);
+	void drawInventoryArrowDown(bool highlight);
 
 private:
 	OSystem *_system;
@@ -79,6 +83,7 @@ private:
 	};
 
 	void fill(Common::Rect r);
+	void drawInventoryArrow(int arrowX, int arrowY, bool highlighted, bool flipped);
 };
 
 } // End of namespace Scumm


Commit: e2a11d00402eb2b67ea04e10a5c410c0eb5309f1
    https://github.com/scummvm/scummvm/commit/e2a11d00402eb2b67ea04e10a5c410c0eb5309f1
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Moved some more Indy 3 GUI code into drawInventoryScrollbar()

Since I guess it might be overdrawn by the scrollbar handle.

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index c1cc880048e..ed0c4feefd1 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -560,9 +560,6 @@ void MacIndy3Gui::drawInventoryWidget() {
 	_macScreen->vLine(554, 297, 364, 0);
 	_macScreen->vLine(569, 297, 364, 0);
 
-	_macScreen->hLine(555, 311, 568, 0);
-	_macScreen->hLine(555, 350, 568, 0);
-
 	drawInventoryScrollbar();
 
 	_macScreen->hLine(555, 297, 568, 15);
@@ -629,6 +626,9 @@ void MacIndy3Gui::drawInventoryArrowDown(bool highlighted) {
 }
 
 void MacIndy3Gui::drawInventoryScrollbar() {
+	_macScreen->hLine(555, 311, 568, 0);
+	_macScreen->hLine(555, 350, 568, 0);
+
 	_macScreen->hLine(555, 312, 568, 0);
 	_macScreen->vLine(555, 313, 349, 0);
 


Commit: 4f247fec351bbec8ba171dd3a94fbc15c487201d
    https://github.com/scummvm/scummvm/commit/4f247fec351bbec8ba171dd3a94fbc15c487201d
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: More Indy 3 Mac GUI stuff

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 ed0c4feefd1..433afd0e3d2 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -569,6 +569,13 @@ void MacIndy3Gui::drawInventoryWidget() {
 
 	drawInventoryArrowUp(false);
 	drawInventoryArrowDown(false);
+
+	drawInventoryText(0, "small key", false);
+	drawInventoryText(1, "painting", false);
+	drawInventoryText(2, "old book", false);
+	drawInventoryText(3, "Grail Diary", false);
+	drawInventoryText(4, "whip", false);
+	drawInventoryText(5, "manual", false);
 }
 
 void MacIndy3Gui::drawInventoryArrow(int arrowX, int arrowY, bool highlighted, bool flipped) {
@@ -635,9 +642,39 @@ void MacIndy3Gui::drawInventoryScrollbar() {
 	fill(Common::Rect(556, 313, 569, 350));
 }
 
+void MacIndy3Gui::drawInventoryText(int slot, char *text, bool highlighted) {
+	int slotX = 423;
+	int slotY = 298 + slot * 11;
+
+	int fg, bg;
+
+	if (highlighted) {
+		fg = 15;
+		bg = 0;
+	} else {
+		fg = 0;
+		bg = 15;
+	}
+
 #if 0
-void MacIndy3Gui::drawInventoryText(int slot, char *text) {
-}
+	// The inventory slots overlap slightly, so we have to clear the entire
+	// area, then highlight the at most single one that's highlighted.
+	_macScreen->fillRect(Common::Rect(423, 298, 551, 365), 15);
 #endif
 
+	int height = 12;
+	int width = 128;
+
+	if (highlighted)
+		_macScreen->fillRect(Common::Rect(slotX, slotY, slotX + width, slotY + height), bg);
+
+	int y = slotY - 1;
+	int x = slotX + 4;
+
+	for (int i = 0; text[i]; i++) {
+		_fonts[0]->drawChar(_macScreen, text[i], x, y, fg);
+		x += _fonts[0]->getCharWidth(text[i]);
+	}
+}
+
 } // End of namespace Scumm
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 1dc38d2a7de..9bc43081949 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -45,6 +45,7 @@ public:
 	void drawInventoryScrollbar();
 	void drawInventoryArrowUp(bool highlight);
 	void drawInventoryArrowDown(bool highlight);
+	void drawInventoryText(int slot, char *text, bool highlighted);
 
 private:
 	OSystem *_system;


Commit: 33b6ee6c05e723206e58653ce1b1b9fe03f9719e
    https://github.com/scummvm/scummvm/commit/33b6ee6c05e723206e58653ce1b1b9fe03f9719e
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Remove verb position adjustments for Indy 3 Mac

Changed paths:
    engines/scumm/script_v5.cpp


diff --git a/engines/scumm/script_v5.cpp b/engines/scumm/script_v5.cpp
index f0df6349c13..081e9d05318 100644
--- a/engines/scumm/script_v5.cpp
+++ b/engines/scumm/script_v5.cpp
@@ -2949,44 +2949,7 @@ void ScummEngine_v5::o5_verbOps() {
 		case 5:		// SO_VERB_AT
 			vs->curRect.left = getVarOrDirectWord(PARAM_1);
 			vs->curRect.top = getVarOrDirectWord(PARAM_2);
-			// Macintosh version of indy3ega used different interface, so adjust values.
-			if ((_game.platform == Common::kPlatformMacintosh) && (_game.id == GID_INDY3)) {
-#if 0
-				switch (verb) {
-				case 1:
-				case 2:
-				case 9:
-					vs->curRect.left += 16;
-					break;
-				case 10:
-				case 11:
-				case 12:
-					vs->curRect.left += 36;
-					break;
-				case 4:
-				case 5:
-				case 8:
-					vs->curRect.left += 60;
-					break;
-				case 13:
-				case 32:
-				case 33:
-				case 34:
-					vs->curRect.left += 90;
-					break;
-				case 107:
-					vs->curRect.left -= 54;
-					vs->curRect.top += 16;
-					break;
-				case 108:
-					vs->curRect.left -= 54;
-					vs->curRect.top += 8;
-					break;
-				default:
-					break;
-				}
-#endif
-			} else if (_game.platform == Common::kPlatformFMTowns && ConfMan.getBool("trim_fmtowns_to_200_pixels")) {
+			if (_game.platform == Common::kPlatformFMTowns && ConfMan.getBool("trim_fmtowns_to_200_pixels")) {
 				if (_game.id == GID_ZAK && verb == 116)
 					// WORKAROUND: FM-TOWNS Zak used the extra 40 pixels at the bottom to increase the inventory to 10 items
 					// if we trim to 200 pixels, we need to move the 'down arrow' (verb 116) to higher location


Commit: d8278586914236564e667fdd0f2d4c0e56f55bf7
    https://github.com/scummvm/scummvm/commit/d8278586914236564e667fdd0f2d4c0e56f55bf7
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Clear all of the Mac Indy 3 verb area

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 433afd0e3d2..1366849a8a5 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -380,6 +380,7 @@ void MacIndy3Gui::handleEvent(Common::Event &event) {
 void MacIndy3Gui::clear() {
 	_macScreen->fillRect(Common::Rect(0, 288, 640, 289), 0);
 	fill(Common::Rect(0, 290, 640, 373));
+	_macScreen->fillRect(Common::Rect(0, 373, 640, 400), 0);
 
 	const byte corner[] = {
 		1, 1, 1, 1,


Commit: 716cd6e0e5a5dc733cb65f57ad4ecad9bea3d2dc
    https://github.com/scummvm/scummvm/commit/716cd6e0e5a5dc733cb65f57ad4ecad9bea3d2dc
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Remove Indy 3 Mac GUI mock-up. Begin work on proper GUI.

The goal is to make the interface as simple as possible, and to put as
much of the knowledge of what's going on into MacIndy3Gui itself.

Changed paths:
    engines/scumm/gfx_mac.cpp
    engines/scumm/gfx_mac.h
    engines/scumm/saveload.cpp
    engines/scumm/script.cpp
    engines/scumm/script_v5.cpp
    engines/scumm/scumm.cpp
    engines/scumm/verbs.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 1366849a8a5..84c35117968 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -54,8 +54,9 @@ 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
-	if (vs->number == kVerbVirtScreen && VAR(VAR_VERB_SCRIPT) == 4)
+	// The verb screen is completely replaced with a custom GUI. All
+	// other drawing to that area is suspended.
+	if (vs->number == kVerbVirtScreen && _macIndy3Gui->isActive())
 		return;
 
 	const byte *pixels = vs->getPixels(x, top);
@@ -370,6 +371,21 @@ MacIndy3Gui::MacIndy3Gui(OSystem *system, ScummEngine *vm) :
 MacIndy3Gui::~MacIndy3Gui() {
 }
 
+bool MacIndy3Gui::isActive() {
+	int verbScript = _vm->VAR(_vm->VAR_VERB_SCRIPT);
+	return verbScript == 4 || verbScript == 18;
+}
+
+void MacIndy3Gui::update() {
+	if (isActive()) {
+		if (!_visible)
+			show();
+	} else {
+		if (_visible)
+			hide();
+	}
+}
+
 void MacIndy3Gui::handleEvent(Common::Event &event) {
 	if (event.type != Common::EVENT_LBUTTONDOWN)
 		return;
@@ -377,6 +393,26 @@ void MacIndy3Gui::handleEvent(Common::Event &event) {
 	debug("Handle Indy 3 click at %d, %d", event.mouse.x, event.mouse.y);
 }
 
+void MacIndy3Gui::show() {
+	debug("SHOW");
+	if (_visible)
+		return;
+
+	clear();
+	_visible = true;
+}
+
+void MacIndy3Gui::hide() {
+	debug("HIDE");
+	if (!_visible)
+		return;
+
+	_visible = false;
+
+	_macScreen->fillRect(Common::Rect(0, 288, 640, 400), 0);
+	_system->copyRectToScreen(_macScreen->getBasePtr(0, 288), _macScreen->pitch, 0, 288, 640, 112);
+}
+
 void MacIndy3Gui::clear() {
 	_macScreen->fillRect(Common::Rect(0, 288, 640, 289), 0);
 	fill(Common::Rect(0, 290, 640, 373));
@@ -407,18 +443,6 @@ void MacIndy3Gui::clear() {
 		}
 	}
 
-	const char *text[] = {
-		"Drawn by ScummVM, but just a mock-up",
-		"Push", "Pull", "Give",
-		"Open", "Close", "Look at",
-		"Walk to", "Pick up", "What is",
-		"Use", "Turn on", "Turn off",
-		"Talk", "Travel"
-	};
-
-	for (int i = 0; i < 15; i++)
-		drawButton(i, (char *)text[i], i < 13, i == 8);
-
 	_system->copyRectToScreen(_macScreen->getBasePtr(0, 288), _macScreen->pitch, 0, 288, 640, 112);
 }
 
@@ -570,13 +594,6 @@ void MacIndy3Gui::drawInventoryWidget() {
 
 	drawInventoryArrowUp(false);
 	drawInventoryArrowDown(false);
-
-	drawInventoryText(0, "small key", false);
-	drawInventoryText(1, "painting", false);
-	drawInventoryText(2, "old book", false);
-	drawInventoryText(3, "Grail Diary", false);
-	drawInventoryText(4, "whip", false);
-	drawInventoryText(5, "manual", false);
 }
 
 void MacIndy3Gui::drawInventoryArrow(int arrowX, int arrowY, bool highlighted, bool flipped) {
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 9bc43081949..9d6783bd9fe 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -37,15 +37,10 @@ class MacIndy3Gui {
 public:
 	MacIndy3Gui(OSystem *system, ScummEngine *vm);
 	~MacIndy3Gui();
-	
+
+	bool isActive();
+	void update();
 	void handleEvent(Common::Event &event);
-	void clear();
-	void drawButton(int n, char *text, bool enabled, bool pressed);
-	void drawInventoryWidget();
-	void drawInventoryScrollbar();
-	void drawInventoryArrowUp(bool highlight);
-	void drawInventoryArrowDown(bool highlight);
-	void drawInventoryText(int slot, char *text, bool highlighted);
 
 private:
 	OSystem *_system;
@@ -53,6 +48,8 @@ private:
 	Graphics::Surface *_macScreen;
 	const Graphics::Font *_fonts[3];
 
+	bool _visible = false;
+
 	struct GuiButtonData {
 		int x;
 		int y;
@@ -83,8 +80,17 @@ private:
 		{ 423, 352, 151, 18 }  // Conversation 5
 	};
 
+	void clear();
+	void show();
+	void hide();
 	void fill(Common::Rect r);
+	void drawButton(int n, char *text, bool enabled, bool pressed);
+	void drawInventoryWidget();
 	void drawInventoryArrow(int arrowX, int arrowY, bool highlighted, bool flipped);
+	void drawInventoryArrowUp(bool highlight);
+	void drawInventoryArrowDown(bool highlight);
+	void drawInventoryScrollbar();
+	void drawInventoryText(int slot, char *text, bool highlighted);
 };
 
 } // End of namespace Scumm
diff --git a/engines/scumm/saveload.cpp b/engines/scumm/saveload.cpp
index 74b656e1913..e6c98be983b 100644
--- a/engines/scumm/saveload.cpp
+++ b/engines/scumm/saveload.cpp
@@ -27,7 +27,6 @@
 
 #include "scumm/actor.h"
 #include "scumm/charset.h"
-#include "scumm/gfx_mac.h"
 #include "scumm/imuse_digi/dimuse_engine.h"
 #include "scumm/imuse/imuse.h"
 #include "scumm/players/player_towns.h"
@@ -866,9 +865,6 @@ bool ScummEngine::loadState(int slot, bool compat, Common::String &filename) {
 		_macScreen->fillRect(Common::Rect(_macScreen->w, _macScreen->h), 0);
 	clearTextSurface();
 
-	if (_macIndy3Gui && VAR(VAR_VERB_SCRIPT) == 4)
-		_macIndy3Gui->clear();
-
 	_lastCodePtr = nullptr;
 	_drawObjectQueNr = 0;
 	_verbMouseOver = 0;
diff --git a/engines/scumm/script.cpp b/engines/scumm/script.cpp
index 8d8ef426c49..332f7f232f0 100644
--- a/engines/scumm/script.cpp
+++ b/engines/scumm/script.cpp
@@ -24,7 +24,6 @@
 #include "common/system.h"
 
 #include "scumm/actor.h"
-#include "scumm/gfx_mac.h"
 #include "scumm/object.h"
 #include "scumm/resource.h"
 #include "scumm/util.h"
@@ -760,12 +759,6 @@ void ScummEngine::writeVar(uint var, int value) {
 			}
 		}
 
-		// I don't know how the original did it, but we trigger the
-		// Indy3 Mac GUI based on the verb script.
-
-		if (_macIndy3Gui && var == VAR_VERB_SCRIPT && value == 4)
-			_macIndy3Gui->clear();
-
 		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/script_v5.cpp b/engines/scumm/script_v5.cpp
index 081e9d05318..c38d67bafd5 100644
--- a/engines/scumm/script_v5.cpp
+++ b/engines/scumm/script_v5.cpp
@@ -3026,6 +3026,7 @@ void ScummEngine_v5::o5_verbOps() {
 			break;
 		case 20:	// SO_VERB_NAME_STR
 			ptr = getResourceAddress(rtString, getVarOrDirectWord(PARAM_1));
+
 			if (!ptr)
 				_res->nukeResource(rtVerb, slot);
 			else {
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 7619b214db2..a554fdce582 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -2463,6 +2463,10 @@ void ScummEngine::waitForTimer(int quarterFrames) {
 		uint32 screenUpdateTimerStart = _system->getMillis();
 		towns_updateGfx();
 #endif
+
+		if (_macIndy3Gui)
+			_macIndy3Gui->update();
+
 		_system->updateScreen();
 		cur = _system->getMillis();
 
diff --git a/engines/scumm/verbs.cpp b/engines/scumm/verbs.cpp
index d6aeb682cc2..f638d4050fd 100644
--- a/engines/scumm/verbs.cpp
+++ b/engines/scumm/verbs.cpp
@@ -1078,9 +1078,6 @@ void ScummEngine::drawVerb(int verb, int mode) {
 	if (!verb)
 		return;
 
-	if (_macIndy3Gui)
-		return;
-
 	// The way we implement high-resolution font on a scaled low-resolution
 	// background requires there to always be a text surface telling which
 	// pixels have been drawn on. This means that the "has mask" feature is


Commit: f37152d4687691c4d425b4935a3133c8010e16ea
    https://github.com/scummvm/scummvm/commit/f37152d4687691c4d425b4935a3133c8010e16ea
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Indy 3 Mac is actually drawing scripted buttons now

This is by no means the final code for it, but it's still a good place
to commit.

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


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 84c35117968..7f0883ff3a8 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -116,7 +116,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);
 }
 
@@ -377,13 +376,51 @@ bool MacIndy3Gui::isActive() {
 }
 
 void MacIndy3Gui::update() {
-	if (isActive()) {
-		if (!_visible)
-			show();
-	} else {
+	if (!isActive()) {
 		if (_visible)
 			hide();
+		return;
 	}
+
+	// REMEMBER: Once we've mapped all the verb ids to their buttons,
+	// we may be able to revoke the friendship with ScummEngine, and
+	// optimize this loop quite a bit.
+
+	for (int i = 0; i < _vm->_numVerbs; i++) {
+		VerbSlot *vs = &_vm->_verbs[i];
+		if (!vs->saveid && vs->curmode && vs->verbid) {
+			// Verb is there
+
+			const byte *ptr = _vm->getResourceAddress(rtVerb, i);
+			if (ptr) {
+				byte buf[270];
+
+				_vm->convertMessageToString(ptr, buf, sizeof(buf));
+				int button = -1;
+
+				if (vs->verbid >= 1 && vs->verbid <= 13) {
+					button = i;
+				} else if (vs->verbid == 32) {
+					button = 14;
+				} else if (vs->verbid == 100) {
+					button = 0;
+				} else if (vs->verbid >= 120 && vs->verbid <= 123) {
+					button = vs->verbid - 105;
+				} else {
+					debug("Unknown verb: [%d] %s", vs->verbid, buf);
+				}
+
+				if (button != -1) {
+					drawButton(button, buf, vs->curmode != 2, false);
+				}
+			}
+		} else {
+			// Verb is gone
+		}
+	}
+
+	if (!_visible)
+		show();
 }
 
 void MacIndy3Gui::handleEvent(Common::Event &event) {
@@ -462,7 +499,7 @@ void MacIndy3Gui::fill(Common::Rect r) {
 		_macScreen->fillRect(r, 7);
 }
 
-void MacIndy3Gui::drawButton(int n, char *text, bool enabled, bool pressed) {
+void MacIndy3Gui::drawButton(int n, byte *text, bool enabled, bool pressed) {
 	int x = _buttons[n].x;
 	int y = _buttons[n].y;
 	int w = _buttons[n].w;
@@ -528,7 +565,7 @@ void MacIndy3Gui::drawButton(int n, char *text, bool enabled, bool pressed) {
 		textX += _fonts[2]->getCharWidth(text[i]);
 	}
 
-	drawInventoryWidget();
+	_system->copyRectToScreen(_macScreen->getBasePtr(x, y), _macScreen->pitch, x, y, w, h);
 }
 
 // Desired behavior:
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 9d6783bd9fe..b058e8b72e9 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -57,34 +57,41 @@ private:
 		int h;
 	};
 
-	const GuiButtonData _buttons[20] = {
-		{  67, 292, 348, 18 }, // Sentence line
-		{  67, 312,  68, 18 }, // Push
-		{  67, 332,  68, 18 }, // Pull
-		{  67, 352,  68, 18 }, // Give
-		{ 137, 312,  68, 18 }, // Open
-		{ 137, 332,  68, 18 }, // Close
-		{ 137, 352,  68, 18 }, // Look at
-		{ 207, 312,  68, 18 }, // Walk to
-		{ 207, 332,  68, 18 }, // Pick up
-		{ 207, 352,  68, 18 }, // What is
-		{ 277, 312,  68, 18 }, // Use
-		{ 277, 332,  68, 18 }, // Turn on
-		{ 277, 352,  68, 18 }, // Turn off
-		{ 347, 312,  68, 18 }, // Talk
-		{ 347, 332,  68, 18 }, // Travel
-		{  67, 292, 507, 18 }, // Conversation 1
-		{  67, 312, 507, 18 }, // Conversation 2
-		{  67, 332, 507, 18 }, // Conversation 3
-		{  67, 352, 151, 18 }, // Conversation 4
-		{ 423, 352, 151, 18 }  // Conversation 5
+	// This list has been arranged so that most of the standard verbs
+	// have the same index as their verb number in the game. The rest
+	// have been arranged tastefully.
+	//
+	// I think 101-106 are inventory items.
+
+	const GuiButtonData _buttons[21] = {
+		{  67, 292, 348, 18 }, // 100: Sentence line
+		{ 137, 312,  68, 18 }, // 1: Open
+		{ 137, 332,  68, 18 }, // 2: Close
+		{  67, 352,  68, 18 }, // 3: Give
+		{ 277, 332,  68, 18 }, // 4: Turn on
+		{ 277, 352,  68, 18 }, // 5: Turn off
+		{  67, 312,  68, 18 }, // 6: Push
+		{  67, 332,  68, 18 }, // 7: Pull
+		{ 277, 312,  68, 18 }, // 8: Use
+		{ 137, 352,  68, 18 }, // 9: Look at
+		{ 207, 312,  68, 18 }, // 10: Walk to
+		{ 207, 332,  68, 18 }, // 11: Pick up
+		{ 207, 352,  68, 18 }, // 12: What is
+		{ 347, 312,  68, 18 }, // 13: Talk
+		{ 347, 332,  68, 18 }, // 32: Travel
+		{  67, 292, 507, 18 }, // 120: Conversation 1
+		{  67, 312, 507, 18 }, // 121: Conversation 2
+		{  67, 332, 507, 18 }, // 122: Conversation 3
+		{  67, 352, 507, 18 }, // 123: Conversation 4
+		{  67, 352, 151, 18 }, // Conversation 5
+		{ 423, 352, 151, 18 }  // Conversation 6
 	};
 
 	void clear();
 	void show();
 	void hide();
 	void fill(Common::Rect r);
-	void drawButton(int n, char *text, bool enabled, bool pressed);
+	void drawButton(int n, byte *text, bool enabled, bool pressed);
 	void drawInventoryWidget();
 	void drawInventoryArrow(int arrowX, int arrowY, bool highlighted, bool flipped);
 	void drawInventoryArrowUp(bool highlight);
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index 0b336e879f0..0d31bd52098 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -521,6 +521,7 @@ class ScummEngine : public Engine, public Common::Serializable {
 	friend class CharsetRenderer;
 	friend class CharsetRendererTownsClassic;
 	friend class ResourceManager;
+	friend class MacIndy3Gui;
 
 public:
 	/* Put often used variables at the top.
diff --git a/engines/scumm/verbs.cpp b/engines/scumm/verbs.cpp
index f638d4050fd..075c36afe8c 100644
--- a/engines/scumm/verbs.cpp
+++ b/engines/scumm/verbs.cpp
@@ -21,6 +21,7 @@
 
 #include "scumm/actor.h"
 #include "scumm/charset.h"
+#include "scumm/gfx_mac.h"
 #include "scumm/he/intern_he.h"
 #include "scumm/object.h"
 #include "scumm/resource.h"
@@ -960,6 +961,9 @@ void ScummEngine::verbMouseOver(int verb) {
 }
 
 int ScummEngine::findVerbAtPos(int x, int y) const {
+	if (_macIndy3Gui && _macIndy3Gui->isActive())
+		return 0;
+
 	if (!_numVerbs)
 		return 0;
 


Commit: fd4d53c27fb77a76b7c1336e85ab3e6a03221004
    https://github.com/scummvm/scummvm/commit/fd4d53c27fb77a76b7c1336e85ab3e6a03221004
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Only draw Mac Indy 3 widgets (formerly buttons) when they change

I still need to undraw buttons when they're removed, but one step at a
time.

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 7f0883ff3a8..42ef546ad22 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -365,11 +365,49 @@ MacIndy3Gui::MacIndy3Gui(OSystem *system, ScummEngine *vm) :
 	_fonts[0] = mfm->getFont(Graphics::MacFont(Graphics::kMacFontGeneva, 9));
 	_fonts[1] = mfm->getFont(Graphics::MacFont(Graphics::kMacFontGeneva, 9, Graphics::kMacFontBold));
 	_fonts[2] = mfm->getFont(Graphics::MacFont(Graphics::kMacFontGeneva, 9, Graphics::kMacFontBold | Graphics::kMacFontOutline | Graphics::kMacFontCondense));
+	// This list has been arranged so that most of the standard verbs
+	// have the same index as their verb number in the game. The rest
+	// have been arranged tastefully.
+	//
+	// I think 101-106 are inventory items.
+
+	initWidget( 0,  67, 292, 348, 18); // 100: Sentence line
+	initWidget( 1, 137, 312,  68, 18); // 1: Open
+	initWidget( 2, 137, 332,  68, 18); // 2: Close
+	initWidget( 3,  67, 352,  68, 18); // 3: Give
+	initWidget( 4, 277, 332,  68, 18); // 4: Turn on
+	initWidget( 5, 277, 352,  68, 18); // 5: Turn off
+	initWidget( 6,  67, 312,  68, 18); // 6: Push
+	initWidget( 7,  67, 332,  68, 18); // 7: Pull
+	initWidget( 8, 277, 312,  68, 18); // 8: Use
+	initWidget( 9, 137, 352,  68, 18); // 9: Look at
+	initWidget(10, 207, 312,  68, 18); // 10: Walk to
+	initWidget(11, 207, 332,  68, 18); // 11: Pick up
+	initWidget(12, 207, 352,  68, 18); // 12: What is
+	initWidget(13, 347, 312,  68, 18); // 13: Talk
+	initWidget(14, 347, 332,  68, 18); // 32: Travel
+	initWidget(15,  67, 292, 507, 18); // 120: Conversation 1
+	initWidget(16,  67, 312, 507, 18); // 121: Conversation 2
+	initWidget(17,  67, 332, 507, 18); // 122: Conversation 3
+	initWidget(18,  67, 352, 507, 18); // 123: Conversation 4
+	initWidget(19,  67, 352, 151, 18); // Conversation 5
+	initWidget(20, 423, 352, 151, 18); // Conversation 6
 }
 
 MacIndy3Gui::~MacIndy3Gui() {
 }
 
+void MacIndy3Gui::initWidget(int n, int x, int y, int w, int h) {
+	Widget *widget = &_widgets[n];
+
+	widget->x = x;
+	widget->y = y;
+	widget->w = w;
+	widget->h = h;
+	widget->visible = false;
+	widget->enabled = false;
+}
+
 bool MacIndy3Gui::isActive() {
 	int verbScript = _vm->VAR(_vm->VAR_VERB_SCRIPT);
 	return verbScript == 4 || verbScript == 18;
@@ -382,12 +420,16 @@ void MacIndy3Gui::update() {
 		return;
 	}
 
+	if (!_visible)
+		show();
+
 	// REMEMBER: Once we've mapped all the verb ids to their buttons,
 	// we may be able to revoke the friendship with ScummEngine, and
 	// optimize this loop quite a bit.
 
 	for (int i = 0; i < _vm->_numVerbs; i++) {
 		VerbSlot *vs = &_vm->_verbs[i];
+
 		if (!vs->saveid && vs->curmode && vs->verbid) {
 			// Verb is there
 
@@ -396,31 +438,34 @@ void MacIndy3Gui::update() {
 				byte buf[270];
 
 				_vm->convertMessageToString(ptr, buf, sizeof(buf));
-				int button = -1;
+				int id = -1;
 
 				if (vs->verbid >= 1 && vs->verbid <= 13) {
-					button = i;
+					id = i;
 				} else if (vs->verbid == 32) {
-					button = 14;
+					id = 14;
 				} else if (vs->verbid == 100) {
-					button = 0;
+					id = 0;
 				} else if (vs->verbid >= 120 && vs->verbid <= 123) {
-					button = vs->verbid - 105;
+					id = vs->verbid - 105;
 				} else {
-					debug("Unknown verb: [%d] %s", vs->verbid, buf);
+//					debug("Unknown verb: [%d] %s", vs->verbid, buf);
 				}
 
-				if (button != -1) {
-					drawButton(button, buf, vs->curmode != 2, false);
+				if (id != -1) {
+					Widget *w = &_widgets[id];
+					bool enabled = (vs->curmode == 2);
+
+					if (!w->visible || w->enabled != enabled) {
+						debug("Drawing button: %s", buf);
+						drawButton(id, buf, vs->curmode != 2, false);
+						w->visible = true;
+						w->enabled = enabled;
+					}
 				}
 			}
-		} else {
-			// Verb is gone
 		}
 	}
-
-	if (!_visible)
-		show();
 }
 
 void MacIndy3Gui::handleEvent(Common::Event &event) {
@@ -500,10 +545,10 @@ void MacIndy3Gui::fill(Common::Rect r) {
 }
 
 void MacIndy3Gui::drawButton(int n, byte *text, bool enabled, bool pressed) {
-	int x = _buttons[n].x;
-	int y = _buttons[n].y;
-	int w = _buttons[n].w;
-	int h = _buttons[n].h;
+	int x = _widgets[n].x;
+	int y = _widgets[n].y;
+	int w = _widgets[n].w;
+	int h = _widgets[n].h;
 
 	fill(Common::Rect(x, y, x + w, y + h));
 
@@ -549,7 +594,7 @@ void MacIndy3Gui::drawButton(int n, byte *text, bool enabled, bool pressed) {
 	for (int i = 0; text[i]; i++)
 		stringWidth += _fonts[2]->getCharWidth(text[i]);
 
-	int textX = (x + (_buttons[n].w - 1 - stringWidth) / 2) - 1;
+	int textX = (x + (w - 1 - stringWidth) / 2) - 1;
 	int textY = y + 2;
 	int color = enabled ? 15 : 0;
 
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index b058e8b72e9..c18ac915fbd 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -50,43 +50,18 @@ private:
 
 	bool _visible = false;
 
-	struct GuiButtonData {
+	struct Widget {
 		int x;
 		int y;
 		int w;
 		int h;
+		int visible;
+		int enabled;
 	};
 
-	// This list has been arranged so that most of the standard verbs
-	// have the same index as their verb number in the game. The rest
-	// have been arranged tastefully.
-	//
-	// I think 101-106 are inventory items.
-
-	const GuiButtonData _buttons[21] = {
-		{  67, 292, 348, 18 }, // 100: Sentence line
-		{ 137, 312,  68, 18 }, // 1: Open
-		{ 137, 332,  68, 18 }, // 2: Close
-		{  67, 352,  68, 18 }, // 3: Give
-		{ 277, 332,  68, 18 }, // 4: Turn on
-		{ 277, 352,  68, 18 }, // 5: Turn off
-		{  67, 312,  68, 18 }, // 6: Push
-		{  67, 332,  68, 18 }, // 7: Pull
-		{ 277, 312,  68, 18 }, // 8: Use
-		{ 137, 352,  68, 18 }, // 9: Look at
-		{ 207, 312,  68, 18 }, // 10: Walk to
-		{ 207, 332,  68, 18 }, // 11: Pick up
-		{ 207, 352,  68, 18 }, // 12: What is
-		{ 347, 312,  68, 18 }, // 13: Talk
-		{ 347, 332,  68, 18 }, // 32: Travel
-		{  67, 292, 507, 18 }, // 120: Conversation 1
-		{  67, 312, 507, 18 }, // 121: Conversation 2
-		{  67, 332, 507, 18 }, // 122: Conversation 3
-		{  67, 352, 507, 18 }, // 123: Conversation 4
-		{  67, 352, 151, 18 }, // Conversation 5
-		{ 423, 352, 151, 18 }  // Conversation 6
-	};
+	Widget _widgets[21];
 
+	void initWidget(int n, int x, int y, int w, int h);
 	void clear();
 	void show();
 	void hide();


Commit: bdd1de74313ebd01a12ac4a12c5d8a528b7bbd52
    https://github.com/scummvm/scummvm/commit/bdd1de74313ebd01a12ac4a12c5d8a528b7bbd52
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Some remaning, and added animation to Indy 3 Mac buttons

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 42ef546ad22..943c04a7f03 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -397,15 +397,17 @@ MacIndy3Gui::MacIndy3Gui(OSystem *system, ScummEngine *vm) :
 MacIndy3Gui::~MacIndy3Gui() {
 }
 
-void MacIndy3Gui::initWidget(int n, int x, int y, int w, int h) {
-	Widget *widget = &_widgets[n];
-
-	widget->x = x;
-	widget->y = y;
-	widget->w = w;
-	widget->h = h;
-	widget->visible = false;
-	widget->enabled = false;
+void MacIndy3Gui::initWidget(int n, int x, int y, int width, int height) {
+	Widget *w = &_widgets[n];
+
+	w->x = x;
+	w->y = y;
+	w->width = width;
+	w->height = height;
+	w->timer = 0;
+	w->visible = false;
+	w->enabled = false;
+	w->dirty = false;
 }
 
 bool MacIndy3Gui::isActive() {
@@ -454,13 +456,20 @@ void MacIndy3Gui::update() {
 
 				if (id != -1) {
 					Widget *w = &_widgets[id];
-					bool enabled = (vs->curmode == 2);
+					bool enabled = (vs->curmode == 1);
 
-					if (!w->visible || w->enabled != enabled) {
+					if (w->timer) {
+						w->timer--;
+						if (!w->timer)
+							w->dirty = true;
+					}
+
+					if (!w->visible || w->dirty || w->enabled != enabled) {
 						debug("Drawing button: %s", buf);
-						drawButton(id, buf, vs->curmode != 2, false);
+						drawButton(id, buf, vs->curmode != 2, w->timer);
 						w->visible = true;
 						w->enabled = enabled;
+						w->dirty = false;
 					}
 				}
 			}
@@ -472,7 +481,19 @@ void MacIndy3Gui::handleEvent(Common::Event &event) {
 	if (event.type != Common::EVENT_LBUTTONDOWN)
 		return;
 
-	debug("Handle Indy 3 click at %d, %d", event.mouse.x, event.mouse.y);
+	int x = event.mouse.x;
+	int y = event.mouse.y;
+
+	for (int i = 0; i < ARRAYSIZE(_widgets); i++) {
+		Widget *w = &_widgets[i];
+
+		if (w->visible && w->enabled) {
+			if (x >= w->x && x < w->x + w->width && y >= w->y && y < w->y + w->height) {
+				w->dirty = true;
+				w->timer = 15;
+			}
+		}
+	}
 }
 
 void MacIndy3Gui::show() {
@@ -547,22 +568,22 @@ void MacIndy3Gui::fill(Common::Rect r) {
 void MacIndy3Gui::drawButton(int n, byte *text, bool enabled, bool pressed) {
 	int x = _widgets[n].x;
 	int y = _widgets[n].y;
-	int w = _widgets[n].w;
-	int h = _widgets[n].h;
+	int width = _widgets[n].width;
+	int height = _widgets[n].height;
 
-	fill(Common::Rect(x, y, x + w, y + h));
+	fill(Common::Rect(x, y, x + width, y + height));
 
 	if (!pressed) {
-		_macScreen->hLine(x + 1, y, x + w - 3, 0);
-		_macScreen->hLine(x + 1, y + h - 2, x + w - 3, 0);
-		_macScreen->vLine(x, y + 1, y + h - 3, 0);
-		_macScreen->vLine(x + w - 2, y + 1, y + h - 3, 0);
+		_macScreen->hLine(x + 1, y, x + width - 3, 0);
+		_macScreen->hLine(x + 1, y + height - 2, x + width - 3, 0);
+		_macScreen->vLine(x, y + 1, y + height - 3, 0);
+		_macScreen->vLine(x + width - 2, y + 1, y + height - 3, 0);
 
-		_macScreen->hLine(x + 2, y + h - 1, x + w - 1, 0);
-		_macScreen->vLine(x + w - 1, y + 2, y + h - 2, 0);
+		_macScreen->hLine(x + 2, y + height - 1, x + width - 1, 0);
+		_macScreen->vLine(x + width - 1, y + 2, y + height - 2, 0);
 
-		_macScreen->hLine(x + 1, y + 1, x + w - 3, 15);
-		_macScreen->vLine(x + 1, y + 2, y + h - 3, 15);
+		_macScreen->hLine(x + 1, y + 1, x + width - 3, 15);
+		_macScreen->vLine(x + 1, y + 2, y + height - 3, 15);
 	} else {
 		// I have only been able to capture a screenshot of the pressed
 		// button in black and white, where the checkerboard background
@@ -575,13 +596,13 @@ void MacIndy3Gui::drawButton(int n, byte *text, bool enabled, bool pressed) {
 		// that the shadow is always drawn, and the rest of the button
 		// is just shifted down to the right. That would make the other
 		// two corners rounded.
-		_macScreen->hLine(x + 2, y + 1, x + w - 2, 0);
-		_macScreen->hLine(x + 2, y + h - 1, x + w - 1, 0);
-		_macScreen->vLine(x + 1, y + 2, y + h - 2, 0);
-		_macScreen->vLine(x + w - 1, y + 2, y + h - 2, 0);
+		_macScreen->hLine(x + 2, y + 1, x + width - 2, 0);
+		_macScreen->hLine(x + 2, y + height - 1, x + width - 1, 0);
+		_macScreen->vLine(x + 1, y + 2, y + height - 2, 0);
+		_macScreen->vLine(x + width - 1, y + 2, y + height - 2, 0);
 
-		_macScreen->hLine(x + 2, y + 2, x + w - 2, 15);
-		_macScreen->vLine(x + 2, y + 3, y + h - 2, 15);
+		_macScreen->hLine(x + 2, y + 2, x + width - 2, 15);
+		_macScreen->vLine(x + 2, y + 3, y + height - 2, 15);
 	}
 
 	// The text is drawn centered. Based on experimentation, I think the
@@ -594,7 +615,7 @@ void MacIndy3Gui::drawButton(int n, byte *text, bool enabled, bool pressed) {
 	for (int i = 0; text[i]; i++)
 		stringWidth += _fonts[2]->getCharWidth(text[i]);
 
-	int textX = (x + (w - 1 - stringWidth) / 2) - 1;
+	int textX = (x + (width - 1 - stringWidth) / 2) - 1;
 	int textY = y + 2;
 	int color = enabled ? 15 : 0;
 
@@ -610,7 +631,7 @@ void MacIndy3Gui::drawButton(int n, byte *text, bool enabled, bool pressed) {
 		textX += _fonts[2]->getCharWidth(text[i]);
 	}
 
-	_system->copyRectToScreen(_macScreen->getBasePtr(x, y), _macScreen->pitch, x, y, w, h);
+	_system->copyRectToScreen(_macScreen->getBasePtr(x, y), _macScreen->pitch, x, y, width, height);
 }
 
 // Desired behavior:
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index c18ac915fbd..ed830012ddd 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -53,10 +53,12 @@ private:
 	struct Widget {
 		int x;
 		int y;
-		int w;
-		int h;
-		int visible;
-		int enabled;
+		int width;
+		int height;
+		int timer;
+		bool visible;
+		bool enabled;
+		bool dirty;
 	};
 
 	Widget _widgets[21];


Commit: 89908e1b31a581e9dad8a24f6749eeb2f7b37c5d
    https://github.com/scummvm/scummvm/commit/89908e1b31a581e9dad8a24f6749eeb2f7b37c5d
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Kill Indy 3 Mac widgets when they're gone

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 943c04a7f03..7b272dc63b5 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -400,6 +400,7 @@ MacIndy3Gui::~MacIndy3Gui() {
 void MacIndy3Gui::initWidget(int n, int x, int y, int width, int height) {
 	Widget *w = &_widgets[n];
 
+	w->slot = -1;
 	w->x = x;
 	w->y = y;
 	w->width = width;
@@ -407,7 +408,8 @@ void MacIndy3Gui::initWidget(int n, int x, int y, int width, int height) {
 	w->timer = 0;
 	w->visible = false;
 	w->enabled = false;
-	w->dirty = false;
+	w->redraw = false;
+	w->kill = false;
 }
 
 bool MacIndy3Gui::isActive() {
@@ -425,6 +427,17 @@ void MacIndy3Gui::update() {
 	if (!_visible)
 		show();
 
+	// Decrease all timers
+	for (int i = 0; i < ARRAYSIZE(_widgets); i++) {
+		Widget *w = &_widgets[i];
+		if (w->timer > 0) {
+			w->timer--;
+			if (w->timer == 0 && w->visible)
+				w->redraw = true;
+		}
+		w->kill = true;
+	}
+
 	// REMEMBER: Once we've mapped all the verb ids to their buttons,
 	// we may be able to revoke the friendship with ScummEngine, and
 	// optimize this loop quite a bit.
@@ -433,45 +446,63 @@ void MacIndy3Gui::update() {
 		VerbSlot *vs = &_vm->_verbs[i];
 
 		if (!vs->saveid && vs->curmode && vs->verbid) {
-			// Verb is there
+			int id = -1;
+
+			if (vs->verbid >= 1 && vs->verbid <= 13) {
+				id = i;
+			} else if (vs->verbid == 32) {
+				id = 14;
+			} else if (vs->verbid == 100) {
+				id = 0;
+			} else if (vs->verbid >= 120 && vs->verbid <= 123) {
+				id = vs->verbid - 105;
+			} else {
+//				debug("Unknown verb: %d", vs->verbid);
+			}
+
+			if (id != -1) {
+				Widget *w = &_widgets[id];
+				bool enabled = (vs->curmode == 1);
+
+				w->slot = i;
+
+				if (!w->visible || w->redraw || w->enabled != enabled)
+					w->redraw = true;
+				w->kill = false;
+			}
+		}
+	}
 
-			const byte *ptr = _vm->getResourceAddress(rtVerb, i);
+	for (int i = 0; i < ARRAYSIZE(_widgets); i++) {
+		Widget *w = &_widgets[i];
+		if (w->kill && w->visible) {
+			fill(Common::Rect(w->x, w->y, w->x + w->width, w->y + w->height));
+			w->slot = -1;
+			w->timer = 0;
+			w->enabled = false;
+			w->visible = false;
+			w->redraw = false;
+		}
+	}
+
+	for (int i = 0; i < ARRAYSIZE(_widgets); i++) {
+		Widget *w = &_widgets[i];
+
+		if (w->slot == -1)
+			continue;
+
+		if (w->redraw) {
+			const byte *ptr = _vm->getResourceAddress(rtVerb, w->slot);
 			if (ptr) {
+				VerbSlot *vs = &_vm->_verbs[w->slot];
 				byte buf[270];
 
 				_vm->convertMessageToString(ptr, buf, sizeof(buf));
-				int id = -1;
-
-				if (vs->verbid >= 1 && vs->verbid <= 13) {
-					id = i;
-				} else if (vs->verbid == 32) {
-					id = 14;
-				} else if (vs->verbid == 100) {
-					id = 0;
-				} else if (vs->verbid >= 120 && vs->verbid <= 123) {
-					id = vs->verbid - 105;
-				} else {
-//					debug("Unknown verb: [%d] %s", vs->verbid, buf);
-				}
-
-				if (id != -1) {
-					Widget *w = &_widgets[id];
-					bool enabled = (vs->curmode == 1);
-
-					if (w->timer) {
-						w->timer--;
-						if (!w->timer)
-							w->dirty = true;
-					}
-
-					if (!w->visible || w->dirty || w->enabled != enabled) {
-						debug("Drawing button: %s", buf);
-						drawButton(id, buf, vs->curmode != 2, w->timer);
-						w->visible = true;
-						w->enabled = enabled;
-						w->dirty = false;
-					}
-				}
+				debug("Drawing button: %s", buf);
+				drawButton(i, buf, vs->curmode != 2, w->timer);
+				w->visible = true;
+				w->enabled = (vs->curmode == 1);
+				w->redraw = false;
 			}
 		}
 	}
@@ -489,7 +520,7 @@ void MacIndy3Gui::handleEvent(Common::Event &event) {
 
 		if (w->visible && w->enabled) {
 			if (x >= w->x && x < w->x + w->width && y >= w->y && y < w->y + w->height) {
-				w->dirty = true;
+				w->redraw = true;
 				w->timer = 15;
 			}
 		}
@@ -550,9 +581,10 @@ void MacIndy3Gui::clear() {
 }
 
 void MacIndy3Gui::fill(Common::Rect r) {
+	int pitch = _macScreen->pitch;
+
 	if (_vm->_renderMode == Common::kRenderMacintoshBW) {
 		byte *row = (byte *)_macScreen->getBasePtr(r.left, r.top);
-		int pitch = _macScreen->pitch;
 
 		for (int y = r.top; y < r.bottom; y++) {
 			byte *ptr = row;
@@ -563,6 +595,8 @@ void MacIndy3Gui::fill(Common::Rect r) {
 		}
 	} else
 		_macScreen->fillRect(r, 7);
+
+	_system->copyRectToScreen(_macScreen->getBasePtr(r.left, r.top), pitch, r.left, r.top, r.width(), r.height());
 }
 
 void MacIndy3Gui::drawButton(int n, byte *text, bool enabled, bool pressed) {
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index ed830012ddd..d5b96145a00 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -51,6 +51,7 @@ private:
 	bool _visible = false;
 
 	struct Widget {
+		int slot;
 		int x;
 		int y;
 		int width;
@@ -58,7 +59,8 @@ private:
 		int timer;
 		bool visible;
 		bool enabled;
-		bool dirty;
+		bool redraw;
+		bool kill;
 	};
 
 	Widget _widgets[21];


Commit: fde1fc8717721d93e53ee637ed0dea21f192907c
    https://github.com/scummvm/scummvm/commit/fde1fc8717721d93e53ee637ed0dea21f192907c
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Hide the Indy 3 Mac GUI when there's nothing to show in it

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 7b272dc63b5..9a4d6766cf3 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -438,6 +438,8 @@ void MacIndy3Gui::update() {
 		w->kill = true;
 	}
 
+	bool keepGuiAlive = false;
+
 	// REMEMBER: Once we've mapped all the verb ids to their buttons,
 	// we may be able to revoke the friendship with ScummEngine, and
 	// optimize this loop quite a bit.
@@ -469,6 +471,7 @@ void MacIndy3Gui::update() {
 				if (!w->visible || w->redraw || w->enabled != enabled)
 					w->redraw = true;
 				w->kill = false;
+				keepGuiAlive = true;
 			}
 		}
 	}
@@ -506,6 +509,9 @@ void MacIndy3Gui::update() {
 			}
 		}
 	}
+
+	if (!keepGuiAlive)
+		hide();
 }
 
 void MacIndy3Gui::handleEvent(Common::Event &event) {
@@ -528,7 +534,6 @@ void MacIndy3Gui::handleEvent(Common::Event &event) {
 }
 
 void MacIndy3Gui::show() {
-	debug("SHOW");
 	if (_visible)
 		return;
 
@@ -537,7 +542,6 @@ void MacIndy3Gui::show() {
 }
 
 void MacIndy3Gui::hide() {
-	debug("HIDE");
 	if (!_visible)
 		return;
 


Commit: eeac1b24ef17ed75d297de190c6f6ad2eec10288
    https://github.com/scummvm/scummvm/commit/eeac1b24ef17ed75d297de190c6f6ad2eec10288
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Indy 3 Mac responds to verbs now

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


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 9a4d6766cf3..ddb81d47fdf 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -372,18 +372,18 @@ MacIndy3Gui::MacIndy3Gui(OSystem *system, ScummEngine *vm) :
 	// I think 101-106 are inventory items.
 
 	initWidget( 0,  67, 292, 348, 18); // 100: Sentence line
-	initWidget( 1, 137, 312,  68, 18); // 1: Open
-	initWidget( 2, 137, 332,  68, 18); // 2: Close
+	initWidget( 1,  67, 312,  68, 18); // 6: Push
+	initWidget( 2,  67, 332,  68, 18); // 7: Pull
 	initWidget( 3,  67, 352,  68, 18); // 3: Give
-	initWidget( 4, 277, 332,  68, 18); // 4: Turn on
-	initWidget( 5, 277, 352,  68, 18); // 5: Turn off
-	initWidget( 6,  67, 312,  68, 18); // 6: Push
-	initWidget( 7,  67, 332,  68, 18); // 7: Pull
-	initWidget( 8, 277, 312,  68, 18); // 8: Use
-	initWidget( 9, 137, 352,  68, 18); // 9: Look at
-	initWidget(10, 207, 312,  68, 18); // 10: Walk to
-	initWidget(11, 207, 332,  68, 18); // 11: Pick up
-	initWidget(12, 207, 352,  68, 18); // 12: What is
+	initWidget( 4, 137, 312,  68, 18); // 1: Open
+	initWidget( 5, 137, 332,  68, 18); // 2: Close
+	initWidget( 6, 137, 352,  68, 18); // 9: Look at
+	initWidget( 7, 207, 312,  68, 18); // 10: Walk to
+	initWidget( 8, 207, 332,  68, 18); // 11: Pick up
+	initWidget( 9, 207, 352,  68, 18); // 12: What is
+	initWidget(10, 277, 312,  68, 18); // 8: Use
+	initWidget(11, 277, 332,  68, 18); // 4: Turn on
+	initWidget(12, 277, 352,  68, 18); // 5: Turn off
 	initWidget(13, 347, 312,  68, 18); // 13: Talk
 	initWidget(14, 347, 332,  68, 18); // 32: Travel
 	initWidget(15,  67, 292, 507, 18); // 120: Conversation 1
@@ -395,6 +395,8 @@ MacIndy3Gui::MacIndy3Gui(OSystem *system, ScummEngine *vm) :
 }
 
 MacIndy3Gui::~MacIndy3Gui() {
+	for (int i = 0; i < ARRAYSIZE(_widgets); i++)
+		free(_widgets[i].text);
 }
 
 void MacIndy3Gui::initWidget(int n, int x, int y, int width, int height) {
@@ -405,6 +407,7 @@ void MacIndy3Gui::initWidget(int n, int x, int y, int width, int height) {
 	w->y = y;
 	w->width = width;
 	w->height = height;
+	w->text = nullptr;
 	w->timer = 0;
 	w->visible = false;
 	w->enabled = false;
@@ -468,6 +471,16 @@ void MacIndy3Gui::update() {
 
 				w->slot = i;
 
+				const byte *ptr = _vm->getResourceAddress(rtVerb, w->slot);
+				byte buf[270];
+
+				_vm->convertMessageToString(ptr, buf, sizeof(buf));
+				if (w->text == nullptr || strcmp((char *)w->text, (char *)buf) != 0) {
+					free(w->text);
+					w->text = (byte *)scumm_strdup((const char *)buf);
+					w->redraw = true;
+				}
+
 				if (!w->visible || w->redraw || w->enabled != enabled)
 					w->redraw = true;
 				w->kill = false;
@@ -495,18 +508,12 @@ void MacIndy3Gui::update() {
 			continue;
 
 		if (w->redraw) {
-			const byte *ptr = _vm->getResourceAddress(rtVerb, w->slot);
-			if (ptr) {
-				VerbSlot *vs = &_vm->_verbs[w->slot];
-				byte buf[270];
-
-				_vm->convertMessageToString(ptr, buf, sizeof(buf));
-				debug("Drawing button: %s", buf);
-				drawButton(i, buf, vs->curmode != 2, w->timer);
-				w->visible = true;
-				w->enabled = (vs->curmode == 1);
-				w->redraw = false;
-			}
+			VerbSlot *vs = &_vm->_verbs[w->slot];
+			debug("Drawing button %d: (%d) %s", i, vs->verbid, w->text);
+			drawButton(i, w->text, vs->curmode != 2, w->timer);
+			w->visible = true;
+			w->enabled = (vs->curmode == 1);
+			w->redraw = false;
 		}
 	}
 
@@ -528,6 +535,11 @@ void MacIndy3Gui::handleEvent(Common::Event &event) {
 			if (x >= w->x && x < w->x + w->width && y >= w->y && y < w->y + w->height) {
 				w->redraw = true;
 				w->timer = 15;
+
+				if (w->slot != -1) {
+					VerbSlot *vs = &_vm->_verbs[w->slot];
+					_vm->runInputScript(kVerbClickArea, vs->verbid, 1);
+				}
 			}
 		}
 	}
@@ -649,24 +661,26 @@ void MacIndy3Gui::drawButton(int n, byte *text, bool enabled, bool pressed) {
 	//
 	// This gives us pixel perfect rendering for the English verbs.
 
-	int stringWidth = 0;
-	for (int i = 0; text[i]; i++)
-		stringWidth += _fonts[2]->getCharWidth(text[i]);
+	if (text) {
+		int stringWidth = 0;
+		for (int i = 0; text[i]; i++)
+			stringWidth += _fonts[2]->getCharWidth(text[i]);
 
-	int textX = (x + (width - 1 - stringWidth) / 2) - 1;
-	int textY = y + 2;
-	int color = enabled ? 15 : 0;
+		int textX = (x + (width - 1 - stringWidth) / 2) - 1;
+		int textY = y + 2;
+		int color = enabled ? 15 : 0;
 
-	if (pressed) {
-		textX++;
-		textY++;
-	}
+		if (pressed) {
+			textX++;
+			textY++;
+		}
 
-	for (int i = 0; text[i]; i++) {
-		if (enabled)
-			_fonts[2]->drawChar(_macScreen, text[i], textX, textY, 0);
-		_fonts[1]->drawChar(_macScreen, text[i], textX + 1, textY, color);
-		textX += _fonts[2]->getCharWidth(text[i]);
+		for (int i = 0; text[i]; i++) {
+			if (enabled)
+				_fonts[2]->drawChar(_macScreen, text[i], textX, textY, 0);
+			_fonts[1]->drawChar(_macScreen, text[i], textX + 1, textY, color);
+			textX += _fonts[2]->getCharWidth(text[i]);
+		}
 	}
 
 	_system->copyRectToScreen(_macScreen->getBasePtr(x, y), _macScreen->pitch, x, y, width, height);
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index d5b96145a00..64c51d8c4f7 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -56,6 +56,7 @@ private:
 		int y;
 		int width;
 		int height;
+		byte *text;
 		int timer;
 		bool visible;
 		bool enabled;
diff --git a/engines/scumm/verbs.cpp b/engines/scumm/verbs.cpp
index 075c36afe8c..b7c9fbb682d 100644
--- a/engines/scumm/verbs.cpp
+++ b/engines/scumm/verbs.cpp
@@ -638,6 +638,9 @@ void ScummEngine::checkExecVerbs() {
 			// Verb was clicked
 			runInputScript(kVerbClickArea, _verbs[over].verbid, code);
 		} else {
+			if (zone->number != kMainVirtScreen && _macIndy3Gui && _macIndy3Gui->isActive())
+				return;
+
 			// Scene was clicked
 			runInputScript((zone->number == kMainVirtScreen) ? kSceneClickArea : kVerbClickArea, 0, code);
 		}


Commit: 654090043bf2a9cb6390f1ca153dfb99e1e23c16
    https://github.com/scummvm/scummvm/commit/654090043bf2a9cb6390f1ca153dfb99e1e23c16
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Fix glitches in Indy 3 Mac GUI

Apparently verb slot 0 is special.

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index ddb81d47fdf..d3f361a48f4 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -447,7 +447,7 @@ void MacIndy3Gui::update() {
 	// we may be able to revoke the friendship with ScummEngine, and
 	// optimize this loop quite a bit.
 
-	for (int i = 0; i < _vm->_numVerbs; i++) {
+	for (int i = 1; i < _vm->_numVerbs; i++) {
 		VerbSlot *vs = &_vm->_verbs[i];
 
 		if (!vs->saveid && vs->curmode && vs->verbid) {
@@ -493,6 +493,8 @@ void MacIndy3Gui::update() {
 		Widget *w = &_widgets[i];
 		if (w->kill && w->visible) {
 			fill(Common::Rect(w->x, w->y, w->x + w->width, w->y + w->height));
+			free(w->text);
+			w->text = nullptr;
 			w->slot = -1;
 			w->timer = 0;
 			w->enabled = false;


Commit: ead11c29e743177e5c6f0ae2b31d964d81ecd981
    https://github.com/scummvm/scummvm/commit/ead11c29e743177e5c6f0ae2b31d964d81ecd981
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Try to fix Indy 3 Mac GUI after loading

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


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index d3f361a48f4..4c3e42ba83d 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -359,7 +359,7 @@ Common::KeyState ScummEngine::mac_showOldStyleBannerAndPause(const char *msg, in
 }
 
 MacIndy3Gui::MacIndy3Gui(OSystem *system, ScummEngine *vm) :
-	_system(system), _vm(vm), _macScreen(vm->_macScreen) {
+	_system(system), _vm(vm), _macScreen(vm->_macScreen), _visible(false) {
 	Graphics::MacFontManager *mfm = _vm->_macFontManager;
 
 	_fonts[0] = mfm->getFont(Graphics::MacFont(Graphics::kMacFontGeneva, 9));
@@ -402,12 +402,22 @@ MacIndy3Gui::~MacIndy3Gui() {
 void MacIndy3Gui::initWidget(int n, int x, int y, int width, int height) {
 	Widget *w = &_widgets[n];
 
-	w->slot = -1;
 	w->x = x;
 	w->y = y;
 	w->width = width;
 	w->height = height;
 	w->text = nullptr;
+
+	resetWidget(n);
+}
+
+void MacIndy3Gui::resetWidget(int n) {
+	Widget *w = &_widgets[n];
+
+	free(w->text);
+
+	w->text = nullptr;
+	w->slot = -1;
 	w->timer = 0;
 	w->visible = false;
 	w->enabled = false;
@@ -420,6 +430,13 @@ bool MacIndy3Gui::isActive() {
 	return verbScript == 4 || verbScript == 18;
 }
 
+void MacIndy3Gui::resetAfterLoad() {
+	_visible = false;
+
+	for (int i = 0; i < ARRAYSIZE(_widgets); i++)
+		resetWidget(i);
+}
+
 void MacIndy3Gui::update() {
 	if (!isActive()) {
 		if (_visible)
@@ -561,6 +578,9 @@ void MacIndy3Gui::hide() {
 
 	_visible = false;
 
+	for (int i = 0; i < ARRAYSIZE(_widgets); i++)
+		resetWidget(i);
+
 	_macScreen->fillRect(Common::Rect(0, 288, 640, 400), 0);
 	_system->copyRectToScreen(_macScreen->getBasePtr(0, 288), _macScreen->pitch, 0, 288, 640, 112);
 }
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 64c51d8c4f7..06bf54aeff6 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -39,6 +39,7 @@ public:
 	~MacIndy3Gui();
 
 	bool isActive();
+	void resetAfterLoad();
 	void update();
 	void handleEvent(Common::Event &event);
 
@@ -67,6 +68,7 @@ private:
 	Widget _widgets[21];
 
 	void initWidget(int n, int x, int y, int w, int h);
+	void resetWidget(int n);
 	void clear();
 	void show();
 	void hide();
diff --git a/engines/scumm/saveload.cpp b/engines/scumm/saveload.cpp
index e6c98be983b..8c3f15b3cfd 100644
--- a/engines/scumm/saveload.cpp
+++ b/engines/scumm/saveload.cpp
@@ -27,6 +27,7 @@
 
 #include "scumm/actor.h"
 #include "scumm/charset.h"
+#include "scumm/gfx_mac.h"
 #include "scumm/imuse_digi/dimuse_engine.h"
 #include "scumm/imuse/imuse.h"
 #include "scumm/players/player_towns.h"
@@ -865,6 +866,9 @@ bool ScummEngine::loadState(int slot, bool compat, Common::String &filename) {
 		_macScreen->fillRect(Common::Rect(_macScreen->w, _macScreen->h), 0);
 	clearTextSurface();
 
+	if (_macIndy3Gui)
+		_macIndy3Gui->resetAfterLoad();
+
 	_lastCodePtr = nullptr;
 	_drawObjectQueNr = 0;
 	_verbMouseOver = 0;


Commit: 560f49d9cfd0b9a595dd48094efac90efcdc9733
    https://github.com/scummvm/scummvm/commit/560f49d9cfd0b9a595dd48094efac90efcdc9733
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Fix some Indy 3 Mac bugs, and add two more button 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 4c3e42ba83d..0a2f80b4738 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -385,13 +385,15 @@ MacIndy3Gui::MacIndy3Gui(OSystem *system, ScummEngine *vm) :
 	initWidget(11, 277, 332,  68, 18); // 4: Turn on
 	initWidget(12, 277, 352,  68, 18); // 5: Turn off
 	initWidget(13, 347, 312,  68, 18); // 13: Talk
-	initWidget(14, 347, 332,  68, 18); // 32: Travel
-	initWidget(15,  67, 292, 507, 18); // 120: Conversation 1
-	initWidget(16,  67, 312, 507, 18); // 121: Conversation 2
-	initWidget(17,  67, 332, 507, 18); // 122: Conversation 3
-	initWidget(18,  67, 352, 507, 18); // 123: Conversation 4
-	initWidget(19,  67, 352, 151, 18); // Conversation 5
-	initWidget(20, 423, 352, 151, 18); // Conversation 6
+	initWidget(14,  97, 312, 121, 18); // 14: Never mind.
+	initWidget(15, 347, 332,  68, 18); // 32: Travel
+	initWidget(16, 324, 312,  91, 18); // 119: Take this:
+	initWidget(17,  67, 292, 507, 18); // 120: Conversation 1
+	initWidget(18,  67, 312, 507, 18); // 121: Conversation 2
+	initWidget(19,  67, 332, 507, 18); // 122: Conversation 3
+	initWidget(20,  67, 352, 507, 18); // 123: Conversation 4
+	initWidget(21,  67, 352, 151, 18); // Conversation 5
+	initWidget(22, 423, 352, 151, 18); // Conversation 6
 }
 
 MacIndy3Gui::~MacIndy3Gui() {
@@ -470,16 +472,19 @@ void MacIndy3Gui::update() {
 		if (!vs->saveid && vs->curmode && vs->verbid) {
 			int id = -1;
 
-			if (vs->verbid >= 1 && vs->verbid <= 13) {
-				id = i;
-			} else if (vs->verbid == 32) {
-				id = 14;
-			} else if (vs->verbid == 100) {
+			if (vs->verbid == 100) {
 				id = 0;
-			} else if (vs->verbid >= 120 && vs->verbid <= 123) {
-				id = vs->verbid - 105;
+			} else if (vs->verbid >= 1 && vs->verbid <= 14) {
+				id = vs->verbid;
+			} else if (vs->verbid == 32) {
+				id = 15;
+			} else if (vs->verbid >= 119 && vs->verbid <= 125) {
+				id = vs->verbid - 103;
 			} else {
-//				debug("Unknown verb: %d", vs->verbid);
+				const byte *ptr = _vm->getResourceAddress(rtVerb, i);
+				byte buf[270];
+				_vm->convertMessageToString(ptr, buf, sizeof(buf));
+//				debug("Unknown verb: %d %s", vs->verbid, buf);
 			}
 
 			if (id != -1) {
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 06bf54aeff6..b17de6d9037 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -65,7 +65,7 @@ private:
 		bool kill;
 	};
 
-	Widget _widgets[21];
+	Widget _widgets[23];
 
 	void initWidget(int n, int x, int y, int w, int h);
 	void resetWidget(int n);


Commit: 1357be25f4db30ca48f842751dd40669f783c020
    https://github.com/scummvm/scummvm/commit/1357be25f4db30ca48f842751dd40669f783c020
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Migrate Indy 3 Mac widget positions to Common::Rect

It's easier to use width/height when initializing them, but after that
rectangles are more helpful.

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 0a2f80b4738..bb43c5c5fe1 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -404,10 +404,10 @@ MacIndy3Gui::~MacIndy3Gui() {
 void MacIndy3Gui::initWidget(int n, int x, int y, int width, int height) {
 	Widget *w = &_widgets[n];
 
-	w->x = x;
-	w->y = y;
-	w->width = width;
-	w->height = height;
+	w->bounds.left = x;
+	w->bounds.top = y;
+	w->bounds.right = x + width;
+	w->bounds.bottom = y + height;
 	w->text = nullptr;
 
 	resetWidget(n);
@@ -484,7 +484,7 @@ void MacIndy3Gui::update() {
 				const byte *ptr = _vm->getResourceAddress(rtVerb, i);
 				byte buf[270];
 				_vm->convertMessageToString(ptr, buf, sizeof(buf));
-//				debug("Unknown verb: %d %s", vs->verbid, buf);
+				debug("Unknown verb: %d %s", vs->verbid, buf);
 			}
 
 			if (id != -1) {
@@ -513,8 +513,9 @@ void MacIndy3Gui::update() {
 
 	for (int i = 0; i < ARRAYSIZE(_widgets); i++) {
 		Widget *w = &_widgets[i];
+
 		if (w->kill && w->visible) {
-			fill(Common::Rect(w->x, w->y, w->x + w->width, w->y + w->height));
+			fill(w->bounds);
 			free(w->text);
 			w->text = nullptr;
 			w->slot = -1;
@@ -556,7 +557,7 @@ void MacIndy3Gui::handleEvent(Common::Event &event) {
 		Widget *w = &_widgets[i];
 
 		if (w->visible && w->enabled) {
-			if (x >= w->x && x < w->x + w->width && y >= w->y && y < w->y + w->height) {
+			if (w->bounds.contains(x, y)) {
 				w->redraw = true;
 				w->timer = 15;
 
@@ -643,24 +644,21 @@ void MacIndy3Gui::fill(Common::Rect r) {
 }
 
 void MacIndy3Gui::drawButton(int n, byte *text, bool enabled, bool pressed) {
-	int x = _widgets[n].x;
-	int y = _widgets[n].y;
-	int width = _widgets[n].width;
-	int height = _widgets[n].height;
+	Common::Rect r = _widgets[n].bounds;
 
-	fill(Common::Rect(x, y, x + width, y + height));
+	fill(r);
 
 	if (!pressed) {
-		_macScreen->hLine(x + 1, y, x + width - 3, 0);
-		_macScreen->hLine(x + 1, y + height - 2, x + width - 3, 0);
-		_macScreen->vLine(x, y + 1, y + height - 3, 0);
-		_macScreen->vLine(x + width - 2, y + 1, y + height - 3, 0);
+		_macScreen->hLine(r.left + 1, r.top, r.right - 3, 0);
+		_macScreen->hLine(r.left + 1, r.bottom - 2, r.right - 3, 0);
+		_macScreen->vLine(r.left, r.top + 1, r.bottom - 3, 0);
+		_macScreen->vLine(r.right - 2, r.top + 1, r.bottom - 3, 0);
 
-		_macScreen->hLine(x + 2, y + height - 1, x + width - 1, 0);
-		_macScreen->vLine(x + width - 1, y + 2, y + height - 2, 0);
+		_macScreen->hLine(r.left + 2, r.bottom - 1, r.right - 1, 0);
+		_macScreen->vLine(r.right - 1, r.top + 2, r.bottom - 2, 0);
 
-		_macScreen->hLine(x + 1, y + 1, x + width - 3, 15);
-		_macScreen->vLine(x + 1, y + 2, y + height - 3, 15);
+		_macScreen->hLine(r.left + 1, r.top + 1, r.right - 3, 15);
+		_macScreen->vLine(r.left + 1, r.top + 2, r.bottom - 3, 15);
 	} else {
 		// I have only been able to capture a screenshot of the pressed
 		// button in black and white, where the checkerboard background
@@ -673,13 +671,13 @@ void MacIndy3Gui::drawButton(int n, byte *text, bool enabled, bool pressed) {
 		// that the shadow is always drawn, and the rest of the button
 		// is just shifted down to the right. That would make the other
 		// two corners rounded.
-		_macScreen->hLine(x + 2, y + 1, x + width - 2, 0);
-		_macScreen->hLine(x + 2, y + height - 1, x + width - 1, 0);
-		_macScreen->vLine(x + 1, y + 2, y + height - 2, 0);
-		_macScreen->vLine(x + width - 1, y + 2, y + height - 2, 0);
+		_macScreen->hLine(r.left + 2, r.top + 1, r.right - 2, 0);
+		_macScreen->hLine(r.left + 2, r.bottom - 1, r.right - 1, 0);
+		_macScreen->vLine(r.left + 1, r.top + 2, r.bottom - 2, 0);
+		_macScreen->vLine(r.right - 1, r.top + 2, r.bottom - 2, 0);
 
-		_macScreen->hLine(x + 2, y + 2, x + width - 2, 15);
-		_macScreen->vLine(x + 2, y + 3, y + height - 2, 15);
+		_macScreen->hLine(r.left + 2, r.top + 2, r.right - 2, 15);
+		_macScreen->vLine(r.left + 2, r.top + 3, r.bottom - 2, 15);
 	}
 
 	// The text is drawn centered. Based on experimentation, I think the
@@ -693,24 +691,24 @@ void MacIndy3Gui::drawButton(int n, byte *text, bool enabled, bool pressed) {
 		for (int i = 0; text[i]; i++)
 			stringWidth += _fonts[2]->getCharWidth(text[i]);
 
-		int textX = (x + (width - 1 - stringWidth) / 2) - 1;
-		int textY = y + 2;
+		int x = (r.left + (r.width() - 1 - stringWidth) / 2) - 1;
+		int y = r.top + 2;
 		int color = enabled ? 15 : 0;
 
 		if (pressed) {
-			textX++;
-			textY++;
+			x++;
+			y++;
 		}
 
 		for (int i = 0; text[i]; i++) {
 			if (enabled)
-				_fonts[2]->drawChar(_macScreen, text[i], textX, textY, 0);
-			_fonts[1]->drawChar(_macScreen, text[i], textX + 1, textY, color);
-			textX += _fonts[2]->getCharWidth(text[i]);
+				_fonts[2]->drawChar(_macScreen, text[i], x, y, 0);
+			_fonts[1]->drawChar(_macScreen, text[i], x + 1, y, color);
+			x += _fonts[2]->getCharWidth(text[i]);
 		}
 	}
 
-	_system->copyRectToScreen(_macScreen->getBasePtr(x, y), _macScreen->pitch, x, y, width, height);
+	_system->copyRectToScreen(_macScreen->getBasePtr(r.left, r.top), _macScreen->pitch, r.left, r.top, r.width(), r.height());
 }
 
 // Desired behavior:
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index b17de6d9037..3916f4ea38e 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -53,10 +53,7 @@ private:
 
 	struct Widget {
 		int slot;
-		int x;
-		int y;
-		int width;
-		int height;
+		Common::Rect bounds;
 		byte *text;
 		int timer;
 		bool visible;


Commit: 95015600d115ffd6332fc989a781fa8e7f5ad87c
    https://github.com/scummvm/scummvm/commit/95015600d115ffd6332fc989a781fa8e7f5ad87c
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Fix order of Mac Indy 3 verb buttons

I had only updated the comments, not the rest of it. Oops.

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index bb43c5c5fe1..4ec9d9b9ac1 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -372,18 +372,18 @@ MacIndy3Gui::MacIndy3Gui(OSystem *system, ScummEngine *vm) :
 	// I think 101-106 are inventory items.
 
 	initWidget( 0,  67, 292, 348, 18); // 100: Sentence line
-	initWidget( 1,  67, 312,  68, 18); // 6: Push
-	initWidget( 2,  67, 332,  68, 18); // 7: Pull
+	initWidget( 1, 137, 312,  68, 18); // 1: Open
+	initWidget( 2, 137, 332,  68, 18); // 2: Close
 	initWidget( 3,  67, 352,  68, 18); // 3: Give
-	initWidget( 4, 137, 312,  68, 18); // 1: Open
-	initWidget( 5, 137, 332,  68, 18); // 2: Close
-	initWidget( 6, 137, 352,  68, 18); // 9: Look at
-	initWidget( 7, 207, 312,  68, 18); // 10: Walk to
-	initWidget( 8, 207, 332,  68, 18); // 11: Pick up
-	initWidget( 9, 207, 352,  68, 18); // 12: What is
-	initWidget(10, 277, 312,  68, 18); // 8: Use
-	initWidget(11, 277, 332,  68, 18); // 4: Turn on
-	initWidget(12, 277, 352,  68, 18); // 5: Turn off
+	initWidget( 4, 277, 332,  68, 18); // 4: Turn on
+	initWidget( 5, 277, 352,  68, 18); // 5: Turn off
+	initWidget( 6,  67, 312,  68, 18); // 6: Push
+	initWidget( 7,  67, 332,  68, 18); // 7: Pull
+	initWidget( 8, 277, 312,  68, 18); // 8: Use
+	initWidget( 9, 137, 352,  68, 18); // 9: Look at
+	initWidget(10, 207, 312,  68, 18); // 10: Walk to
+	initWidget(11, 207, 332,  68, 18); // 11: Pick up
+	initWidget(12, 207, 352,  68, 18); // 12: What is
 	initWidget(13, 347, 312,  68, 18); // 13: Talk
 	initWidget(14,  97, 312, 121, 18); // 14: Never mind.
 	initWidget(15, 347, 332,  68, 18); // 32: Travel


Commit: 1875d193497f6ad8f608ef96ca25b9a2256a5c91
    https://github.com/scummvm/scummvm/commit/1875d193497f6ad8f608ef96ca25b9a2256a5c91
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Remove untrue Indy 3 GUI comment

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 4ec9d9b9ac1..abf2bbbcbbb 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -462,10 +462,6 @@ void MacIndy3Gui::update() {
 
 	bool keepGuiAlive = false;
 
-	// REMEMBER: Once we've mapped all the verb ids to their buttons,
-	// we may be able to revoke the friendship with ScummEngine, and
-	// optimize this loop quite a bit.
-
 	for (int i = 1; i < _vm->_numVerbs; i++) {
 		VerbSlot *vs = &_vm->_verbs[i];
 


Commit: 702ec52d96e40b9a9bf70c348403c34b799bf96c
    https://github.com/scummvm/scummvm/commit/702ec52d96e40b9a9bf70c348403c34b799bf96c
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Lots of Indy 3 Mac GUI rewriting

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


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index abf2bbbcbbb..6cbfe270911 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -358,6 +358,8 @@ Common::KeyState ScummEngine::mac_showOldStyleBannerAndPause(const char *msg, in
 	return ks;
 }
 
+// Verb GUI for Indiana Jones and the Last Crusade
+
 MacIndy3Gui::MacIndy3Gui(OSystem *system, ScummEngine *vm) :
 	_system(system), _vm(vm), _macScreen(vm->_macScreen), _visible(false) {
 	Graphics::MacFontManager *mfm = _vm->_macFontManager;
@@ -365,35 +367,33 @@ MacIndy3Gui::MacIndy3Gui(OSystem *system, ScummEngine *vm) :
 	_fonts[0] = mfm->getFont(Graphics::MacFont(Graphics::kMacFontGeneva, 9));
 	_fonts[1] = mfm->getFont(Graphics::MacFont(Graphics::kMacFontGeneva, 9, Graphics::kMacFontBold));
 	_fonts[2] = mfm->getFont(Graphics::MacFont(Graphics::kMacFontGeneva, 9, Graphics::kMacFontBold | Graphics::kMacFontOutline | Graphics::kMacFontCondense));
-	// This list has been arranged so that most of the standard verbs
-	// have the same index as their verb number in the game. The rest
-	// have been arranged tastefully.
-	//
-	// I think 101-106 are inventory items.
-
-	initWidget( 0,  67, 292, 348, 18); // 100: Sentence line
-	initWidget( 1, 137, 312,  68, 18); // 1: Open
-	initWidget( 2, 137, 332,  68, 18); // 2: Close
-	initWidget( 3,  67, 352,  68, 18); // 3: Give
-	initWidget( 4, 277, 332,  68, 18); // 4: Turn on
-	initWidget( 5, 277, 352,  68, 18); // 5: Turn off
-	initWidget( 6,  67, 312,  68, 18); // 6: Push
-	initWidget( 7,  67, 332,  68, 18); // 7: Pull
-	initWidget( 8, 277, 312,  68, 18); // 8: Use
-	initWidget( 9, 137, 352,  68, 18); // 9: Look at
-	initWidget(10, 207, 312,  68, 18); // 10: Walk to
-	initWidget(11, 207, 332,  68, 18); // 11: Pick up
-	initWidget(12, 207, 352,  68, 18); // 12: What is
-	initWidget(13, 347, 312,  68, 18); // 13: Talk
-	initWidget(14,  97, 312, 121, 18); // 14: Never mind.
-	initWidget(15, 347, 332,  68, 18); // 32: Travel
-	initWidget(16, 324, 312,  91, 18); // 119: Take this:
-	initWidget(17,  67, 292, 507, 18); // 120: Conversation 1
-	initWidget(18,  67, 312, 507, 18); // 121: Conversation 2
-	initWidget(19,  67, 332, 507, 18); // 122: Conversation 3
-	initWidget(20,  67, 352, 507, 18); // 123: Conversation 4
-	initWidget(21,  67, 352, 151, 18); // Conversation 5
-	initWidget(22, 423, 352, 151, 18); // Conversation 6
+
+	initWidget( 0,   1, 137, 312,  68, 18); // Open
+	initWidget( 1,   2, 137, 332,  68, 18); // Close
+	initWidget( 2,   3,  67, 352,  68, 18); // Give
+	initWidget( 3,   4, 277, 332,  68, 18); // Turn on
+	initWidget( 4,   5, 277, 352,  68, 18); // Turn off
+	initWidget( 5,   6,  67, 312,  68, 18); // Push
+	initWidget( 6,   7,  67, 332,  68, 18); // Pull
+	initWidget( 7,   8, 277, 312,  68, 18); // Use
+	initWidget( 8,   9, 137, 352,  68, 18); // Look at
+	initWidget( 9,  10, 207, 312,  68, 18); // Walk to
+	initWidget(10,  11, 207, 332,  68, 18); // Pick up
+	initWidget(11,  12, 207, 352,  68, 18); // What is
+	initWidget(12,  13, 347, 312,  68, 18); // Talk
+	initWidget(13,  14,  97, 312, 121, 18); // Never mind.
+	initWidget(14,  32, 347, 332,  68, 18); // Travel
+	initWidget(15,  33, 347, 352,  68, 18); // To Indy
+	initWidget(16,  34, 347, 352,  68, 18); // To Henry
+	initWidget(17, 100,  67, 292, 348, 18); // Sentence line
+	initWidget(18, 101, 417, 292, 157, 78); // Inventory widget
+	initWidget(19, 119, 324, 312,  91, 18); // Take this:
+	initWidget(20, 120,  67, 292, 507, 18); // Conversation 1
+	initWidget(21, 121,  67, 312, 507, 18); // Conversation 2
+	initWidget(22, 122,  67, 332, 507, 18); // Conversation 3
+	initWidget(23, 123,  67, 352, 507, 18); // Conversation 4
+	initWidget(24, 124,  67, 352, 151, 18); // Conversation 5
+	initWidget(25, 125, 423, 352, 151, 18); // Conversation 6
 }
 
 MacIndy3Gui::~MacIndy3Gui() {
@@ -401,25 +401,21 @@ MacIndy3Gui::~MacIndy3Gui() {
 		free(_widgets[i].text);
 }
 
-void MacIndy3Gui::initWidget(int n, int x, int y, int width, int height) {
+void MacIndy3Gui::initWidget(int n, int verbid, int x, int y, int width, int height) {
 	Widget *w = &_widgets[n];
 
+	w->verbid = verbid;
 	w->bounds.left = x;
 	w->bounds.top = y;
 	w->bounds.right = x + width;
 	w->bounds.bottom = y + height;
-	w->text = nullptr;
-
-	resetWidget(n);
 }
 
-void MacIndy3Gui::resetWidget(int n) {
-	Widget *w = &_widgets[n];
-
+void MacIndy3Gui::resetWidget(Widget *w) {
 	free(w->text);
 
+	w->verbslot = -1;
 	w->text = nullptr;
-	w->slot = -1;
 	w->timer = 0;
 	w->visible = false;
 	w->enabled = false;
@@ -436,7 +432,21 @@ void MacIndy3Gui::resetAfterLoad() {
 	_visible = false;
 
 	for (int i = 0; i < ARRAYSIZE(_widgets); i++)
-		resetWidget(i);
+		resetWidget(&_widgets[i]);
+
+	// In the DOS version, verb ID 102-106 were used for the visible
+	// inventory items, and 107-108 for inventory arrow buttons. In the
+	// Macintosh version, the entire inventory widget is verb ID 101.
+	//
+	// In old savegames, the DOS verb IDs may still be present, and have
+	// to be updated when loading them.
+
+	// TODO: Collect the inventory objects
+
+	for (int i = 0; i < _vm->_numVerbs; i++) {
+		if (_vm->_verbs[i].verbid >= 102 && _vm->_verbs[i].verbid <= 108)
+			_vm->killVerb(i);
+	}
 }
 
 void MacIndy3Gui::update() {
@@ -454,28 +464,33 @@ void MacIndy3Gui::update() {
 		Widget *w = &_widgets[i];
 		if (w->timer > 0) {
 			w->timer--;
-			if (w->timer == 0 && w->visible)
+			if (w->timer == 0 && w->visible) {
+				_vm->runInputScript(kVerbClickArea, w->verbid, 1);
 				w->redraw = true;
+			}
 		}
 		w->kill = true;
 	}
 
 	bool keepGuiAlive = false;
 
+	// Collect all active verbs
 	for (int i = 1; i < _vm->_numVerbs; i++) {
 		VerbSlot *vs = &_vm->_verbs[i];
 
 		if (!vs->saveid && vs->curmode && vs->verbid) {
 			int id = -1;
 
-			if (vs->verbid == 100) {
-				id = 0;
-			} else if (vs->verbid >= 1 && vs->verbid <= 14) {
-				id = vs->verbid;
-			} else if (vs->verbid == 32) {
-				id = 15;
+			if (vs->verbid >= 1 && vs->verbid <= 14) {
+				id = vs->verbid - 1;
+			} else if (vs->verbid >= 32 && vs->verbid <= 34) {
+				id = vs->verbid - 18;
+			} else if (vs->verbid == 100) {
+				id = 17;
+			} else if (vs->verbid == 101) {
+				id = 18;
 			} else if (vs->verbid >= 119 && vs->verbid <= 125) {
-				id = vs->verbid - 103;
+				id = vs->verbid - 100;
 			} else {
 				const byte *ptr = _vm->getResourceAddress(rtVerb, i);
 				byte buf[270];
@@ -487,9 +502,16 @@ void MacIndy3Gui::update() {
 				Widget *w = &_widgets[id];
 				bool enabled = (vs->curmode == 1);
 
-				w->slot = i;
+				assert(w->verbid == vs->verbid);
+
+				if (w->enabled != enabled)
+					w->redraw = true;
+				w->verbslot = i;
+				w->verbid = vs->verbid;
+				w->enabled = enabled;
+				w->kill = false;
 
-				const byte *ptr = _vm->getResourceAddress(rtVerb, w->slot);
+				const byte *ptr = _vm->getResourceAddress(rtVerb, w->verbslot);
 				byte buf[270];
 
 				_vm->convertMessageToString(ptr, buf, sizeof(buf));
@@ -499,42 +521,34 @@ void MacIndy3Gui::update() {
 					w->redraw = true;
 				}
 
-				if (!w->visible || w->redraw || w->enabled != enabled)
-					w->redraw = true;
-				w->kill = false;
 				keepGuiAlive = true;
 			}
 		}
 	}
 
+	// Erase all inactive buttons. Since buttons are overlapping, we have
+	// to do this first.
+
 	for (int i = 0; i < ARRAYSIZE(_widgets); i++) {
 		Widget *w = &_widgets[i];
 
 		if (w->kill && w->visible) {
-			fill(w->bounds);
-			free(w->text);
-			w->text = nullptr;
-			w->slot = -1;
-			w->timer = 0;
-			w->enabled = false;
-			w->visible = false;
-			w->redraw = false;
+			undrawWidget(w);
+			resetWidget(w);
 		}
 	}
 
+	// Draw all active buttons
+
 	for (int i = 0; i < ARRAYSIZE(_widgets); i++) {
 		Widget *w = &_widgets[i];
 
-		if (w->slot == -1)
+		if (w->verbslot == -1)
 			continue;
 
 		if (w->redraw) {
-			VerbSlot *vs = &_vm->_verbs[w->slot];
-			debug("Drawing button %d: (%d) %s", i, vs->verbid, w->text);
-			drawButton(i, w->text, vs->curmode != 2, w->timer);
-			w->visible = true;
-			w->enabled = (vs->curmode == 1);
-			w->redraw = false;
+			debug("Drawing button %d: (%d) %s", i, w->verbid, w->text);
+			drawWidget(w);
 		}
 	}
 
@@ -552,20 +566,25 @@ void MacIndy3Gui::handleEvent(Common::Event &event) {
 	for (int i = 0; i < ARRAYSIZE(_widgets); i++) {
 		Widget *w = &_widgets[i];
 
-		if (w->visible && w->enabled) {
-			if (w->bounds.contains(x, y)) {
-				w->redraw = true;
-				w->timer = 15;
-
-				if (w->slot != -1) {
-					VerbSlot *vs = &_vm->_verbs[w->slot];
-					_vm->runInputScript(kVerbClickArea, vs->verbid, 1);
-				}
-			}
+		if (w->verbid && w->enabled && w->bounds.contains(x, y)) {
+			// The input script is invoked when the button animation
+			// times out. Speed-runners will probably hate this, but
+			// I think it looks better this way.
+			//
+			// Besides, wouldn't real speed runners use keyboard
+			// shortcuts? (TODO: Check if they're really different
+			// than in the DOS version.)
+			w->redraw = true;
+			w->timer = 15;
+			return;
 		}
 	}
 }
 
+void MacIndy3Gui::updateInventory() {
+	debug("TODO: Update inventory");
+}
+
 void MacIndy3Gui::show() {
 	if (_visible)
 		return;
@@ -581,7 +600,7 @@ void MacIndy3Gui::hide() {
 	_visible = false;
 
 	for (int i = 0; i < ARRAYSIZE(_widgets); i++)
-		resetWidget(i);
+		resetWidget(&_widgets[i]);
 
 	_macScreen->fillRect(Common::Rect(0, 288, 640, 400), 0);
 	_system->copyRectToScreen(_macScreen->getBasePtr(0, 288), _macScreen->pitch, 0, 288, 640, 112);
@@ -617,44 +636,38 @@ void MacIndy3Gui::clear() {
 		}
 	}
 
-	_system->copyRectToScreen(_macScreen->getBasePtr(0, 288), _macScreen->pitch, 0, 288, 640, 112);
+	copyRectToScreen(Common::Rect(0, 288, 640, 400));
 }
 
-void MacIndy3Gui::fill(Common::Rect r) {
-	int pitch = _macScreen->pitch;
-
-	if (_vm->_renderMode == Common::kRenderMacintoshBW) {
-		byte *row = (byte *)_macScreen->getBasePtr(r.left, r.top);
+void MacIndy3Gui::drawWidget(Widget *w) {
+	// Clear the area used by the widget
+	fill(w->bounds);
+
+	switch (w->verbid) {
+	case 101:
+		drawInventoryWidget(w);
+		break;
+	default:
+		drawButton(w);
+		break;
+	}
 
-		for (int y = r.top; y < r.bottom; y++) {
-			byte *ptr = row;
-			for (int x = r.left; x < r.right; x++) {
-				*ptr++ = ((x + y) & 1) ? 15 : 0;
-			}
-			row += pitch;
-		}
-	} else
-		_macScreen->fillRect(r, 7);
+	w->visible = true;
+	w->redraw = false;
 
-	_system->copyRectToScreen(_macScreen->getBasePtr(r.left, r.top), pitch, r.left, r.top, r.width(), r.height());
+	copyRectToScreen(w->bounds);
 }
 
-void MacIndy3Gui::drawButton(int n, byte *text, bool enabled, bool pressed) {
-	Common::Rect r = _widgets[n].bounds;
-
-	fill(r);
+void MacIndy3Gui::undrawWidget(Widget *w) {
+	fill(w->bounds);
+	copyRectToScreen(w->bounds);
+}
 
-	if (!pressed) {
-		_macScreen->hLine(r.left + 1, r.top, r.right - 3, 0);
-		_macScreen->hLine(r.left + 1, r.bottom - 2, r.right - 3, 0);
-		_macScreen->vLine(r.left, r.top + 1, r.bottom - 3, 0);
-		_macScreen->vLine(r.right - 2, r.top + 1, r.bottom - 3, 0);
+void MacIndy3Gui::drawButton(Widget *w) {
+	Common::Rect r = w->bounds;
 
-		_macScreen->hLine(r.left + 2, r.bottom - 1, r.right - 1, 0);
-		_macScreen->vLine(r.right - 1, r.top + 2, r.bottom - 2, 0);
-
-		_macScreen->hLine(r.left + 1, r.top + 1, r.right - 3, 15);
-		_macScreen->vLine(r.left + 1, r.top + 2, r.bottom - 3, 15);
+	if (w->timer == 0) {
+		drawShadowBox(r);
 	} else {
 		// I have only been able to capture a screenshot of the pressed
 		// button in black and white, where the checkerboard background
@@ -682,29 +695,27 @@ void MacIndy3Gui::drawButton(int n, byte *text, bool enabled, bool pressed) {
 	//
 	// This gives us pixel perfect rendering for the English verbs.
 
-	if (text) {
+	if (w->text) {
 		int stringWidth = 0;
-		for (int i = 0; text[i]; i++)
-			stringWidth += _fonts[2]->getCharWidth(text[i]);
+		for (int i = 0; w->text[i]; i++)
+			stringWidth += _fonts[2]->getCharWidth(w->text[i]);
 
 		int x = (r.left + (r.width() - 1 - stringWidth) / 2) - 1;
 		int y = r.top + 2;
-		int color = enabled ? 15 : 0;
+		int color = w->enabled ? 15 : 0;
 
-		if (pressed) {
+		if (w->timer) {
 			x++;
 			y++;
 		}
 
-		for (int i = 0; text[i]; i++) {
-			if (enabled)
-				_fonts[2]->drawChar(_macScreen, text[i], x, y, 0);
-			_fonts[1]->drawChar(_macScreen, text[i], x + 1, y, color);
-			x += _fonts[2]->getCharWidth(text[i]);
+		for (int i = 0; w->text[i]; i++) {
+			if (w->enabled)
+				_fonts[2]->drawChar(_macScreen, w->text[i], x, y, 0);
+			_fonts[1]->drawChar(_macScreen, w->text[i], x + 1, y, color);
+			x += _fonts[2]->getCharWidth(w->text[i]);
 		}
 	}
-
-	_system->copyRectToScreen(_macScreen->getBasePtr(r.left, r.top), _macScreen->pitch, r.left, r.top, r.width(), r.height());
 }
 
 // Desired behavior:
@@ -730,46 +741,22 @@ void MacIndy3Gui::drawButton(int n, byte *text, bool enabled, bool pressed) {
 // Dragging the handle is not possible. Clicking on the handle is probably
 // indistinguishable from clicking above or below.
 
-void MacIndy3Gui::drawInventoryWidget() {
-	fill(Common::Rect(417, 292, 574, 370));
-
-	// Main outline and shadow
-	_macScreen->hLine(418, 292, 571, 0);
-	_macScreen->hLine(418, 368, 571, 0);
-	_macScreen->vLine(417, 293, 367, 0);
-	_macScreen->vLine(572, 293, 367, 0);
-
-	_macScreen->hLine(419, 369, 573, 0);
-	_macScreen->vLine(573, 294, 368, 0);
-
-	_macScreen->hLine(418, 293, 571, 15);
-	_macScreen->vLine(418, 294, 367, 15);
+void MacIndy3Gui::drawInventoryWidget(Widget *w) {
+	Common::Rect r = w->bounds;
 
-	// Inner frame
-	_macScreen->hLine(421, 296, 551, 0);
-	_macScreen->hLine(421, 297, 551, 0);
-	_macScreen->hLine(421, 365, 551, 0);
-	_macScreen->vLine(421, 298, 364, 0);
-	_macScreen->vLine(422, 298, 364, 0);
-	_macScreen->vLine(551, 298, 364, 0);
+	drawShadowBox(r);
+	drawShadowFrame(Common::Rect(r.left + 4, r.top + 4, r.right - 22, r.bottom - 4), 0, 15);
 
-	_macScreen->fillRect(Common::Rect(423, 298, 551, 365), 15);
-
-	// Scrollbar, outer frame
-	_macScreen->hLine(554, 296, 569, 0);
-	_macScreen->hLine(554, 365, 569, 0);
-	_macScreen->vLine(554, 297, 364, 0);
-	_macScreen->vLine(569, 297, 364, 0);
+	// TODO: Draw inventory text
 
-	drawInventoryScrollbar();
+	// Scrollbar
 
-	_macScreen->hLine(555, 297, 568, 15);
-	_macScreen->vLine(555, 298, 310, 15);
-	_macScreen->hLine(555, 351, 568, 15);
-	_macScreen->vLine(555, 352, 364, 15);
+	drawShadowFrame(Common::Rect(r.right - 20, r.top + 4, r.right - 4, r.top + 20), 15, 255);
+	drawShadowFrame(Common::Rect(r.right - 20, r.top + 19, r.right - 4, r.bottom - 19), 0, 255);
+	drawShadowFrame(Common::Rect(r.right - 20, r.bottom - 20, r.right - 4, r.bottom - 4), 15, 255);
 
-	drawInventoryArrowUp(false);
-	drawInventoryArrowDown(false);
+	drawInventoryArrow(r.right - 17, r.top + 7, false, false);
+	drawInventoryArrow(r.right - 17, r.bottom - 16, false, true);
 }
 
 void MacIndy3Gui::drawInventoryArrow(int arrowX, int arrowY, bool highlighted, bool flipped) {
@@ -818,24 +805,6 @@ void MacIndy3Gui::drawInventoryArrow(int arrowX, int arrowY, bool highlighted, b
 	} while (y != y1);
 }
 
-void MacIndy3Gui::drawInventoryArrowUp(bool highlighted) {
-	drawInventoryArrow(557, 299, highlighted, false);
-}
-
-void MacIndy3Gui::drawInventoryArrowDown(bool highlighted) {
-	drawInventoryArrow(557, 354, highlighted, true);
-}
-
-void MacIndy3Gui::drawInventoryScrollbar() {
-	_macScreen->hLine(555, 311, 568, 0);
-	_macScreen->hLine(555, 350, 568, 0);
-
-	_macScreen->hLine(555, 312, 568, 0);
-	_macScreen->vLine(555, 313, 349, 0);
-
-	fill(Common::Rect(556, 313, 569, 350));
-}
-
 void MacIndy3Gui::drawInventoryText(int slot, char *text, bool highlighted) {
 	int slotX = 423;
 	int slotY = 298 + slot * 11;
@@ -871,4 +840,57 @@ void MacIndy3Gui::drawInventoryText(int slot, char *text, bool highlighted) {
 	}
 }
 
+void MacIndy3Gui::fill(Common::Rect r) {
+	int pitch = _macScreen->pitch;
+
+	if (_vm->_renderMode == Common::kRenderMacintoshBW) {
+		byte *row = (byte *)_macScreen->getBasePtr(r.left, r.top);
+
+		for (int y = r.top; y < r.bottom; y++) {
+			byte *ptr = row;
+			for (int x = r.left; x < r.right; x++) {
+				*ptr++ = ((x + y) & 1) ? 15 : 0;
+			}
+			row += pitch;
+		}
+	} else
+		_macScreen->fillRect(r, 7);
+}
+
+void MacIndy3Gui::drawShadowBox(Common::Rect r) {
+	_macScreen->hLine(r.left + 1, r.top, r.right - 3, 0);
+	_macScreen->hLine(r.left + 1, r.bottom - 2, r.right - 3, 0);
+	_macScreen->vLine(r.left, r.top + 1, r.bottom - 3, 0);
+	_macScreen->vLine(r.right - 2, r.top + 1, r.bottom - 3, 0);
+
+	_macScreen->hLine(r.left + 2, r.bottom - 1, r.right - 1, 0);
+	_macScreen->vLine(r.right - 1, r.top + 2, r.bottom - 2, 0);
+
+	_macScreen->hLine(r.left + 1, r.top + 1, r.right - 3, 15);
+	_macScreen->vLine(r.left + 1, r.top + 2, r.bottom - 3, 15);
+}
+
+void MacIndy3Gui::drawShadowFrame(Common::Rect r, byte shadowColor, byte fillColor) {
+	_macScreen->hLine(r.left, r.top, r.right - 1, 0);
+	_macScreen->hLine(r.left, r.bottom - 1, r.right - 1, 0);
+	_macScreen->vLine(r.left, r.top + 1, r.bottom - 2, 0);
+	_macScreen->vLine(r.right - 1, r.top + 1, r.bottom - 2, 0);
+
+	_macScreen->hLine(r.left + 1, r.top + 1, r.right - 2, shadowColor);
+	_macScreen->vLine(r.left + 1, r.top + 2, r.bottom - 2, shadowColor);
+
+	if (fillColor != 255) {
+		Common::Rect fillRect(r.left + 2, r.top + 2, r.right - 1, r.bottom - 1);
+
+		if (fillColor == 0)
+			fill(fillRect);
+		else
+			_macScreen->fillRect(fillRect, fillColor);
+	}
+}
+
+void MacIndy3Gui::copyRectToScreen(Common::Rect r) {
+	_system->copyRectToScreen(_macScreen->getBasePtr(r.left, r.top), _macScreen->pitch, r.left, r.top, r.width(), r.height());
+}
+
 } // End of namespace Scumm
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 3916f4ea38e..9917b506efb 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -42,6 +42,7 @@ public:
 	void resetAfterLoad();
 	void update();
 	void handleEvent(Common::Event &event);
+	void updateInventory();
 
 private:
 	OSystem *_system;
@@ -52,31 +53,37 @@ private:
 	bool _visible = false;
 
 	struct Widget {
-		int slot;
 		Common::Rect bounds;
-		byte *text;
-		int timer;
-		bool visible;
-		bool enabled;
-		bool redraw;
-		bool kill;
+		int verbid = 0;
+		int verbslot = -1;
+		byte *text = nullptr;
+		int timer = 0;
+		bool visible = false;
+		bool enabled = false;
+		bool redraw = false;
+		bool kill = false;
 	};
 
-	Widget _widgets[23];
+	Widget _widgets[26];
 
-	void initWidget(int n, int x, int y, int w, int h);
-	void resetWidget(int n);
+	void initWidget(int n, int verbid, int x, int y, int w, int h);
+	void resetWidget(Widget *w);
 	void clear();
 	void show();
 	void hide();
-	void fill(Common::Rect r);
-	void drawButton(int n, byte *text, bool enabled, bool pressed);
-	void drawInventoryWidget();
+	void drawWidget(Widget *w);
+	void undrawWidget(Widget *w);
+	void drawButton(Widget *w);
+	void drawInventoryWidget(Widget *w);
 	void drawInventoryArrow(int arrowX, int arrowY, bool highlighted, bool flipped);
-	void drawInventoryArrowUp(bool highlight);
-	void drawInventoryArrowDown(bool highlight);
-	void drawInventoryScrollbar();
 	void drawInventoryText(int slot, char *text, bool highlighted);
+
+	// Primitives
+	void fill(Common::Rect r);
+	void drawShadowBox(Common::Rect r);
+	void drawShadowFrame(Common::Rect r, byte shadowColor, byte fillColor);
+
+	void copyRectToScreen(Common::Rect r);
 };
 
 } // End of namespace Scumm
diff --git a/engines/scumm/script.cpp b/engines/scumm/script.cpp
index 332f7f232f0..a846fd11a87 100644
--- a/engines/scumm/script.cpp
+++ b/engines/scumm/script.cpp
@@ -866,78 +866,11 @@ void ScummEngine::stopObjectCode() {
 
 void ScummEngine::runInventoryScript(int i) {
 	if (VAR(VAR_INVENTORY_SCRIPT)) {
-		if (_game.id == GID_INDY3 && _game.platform == Common::kPlatformMacintosh) {
-			inventoryScriptIndy3Mac();
-		} else {
-			int args[NUM_SCRIPT_LOCAL];
-			memset(args, 0, sizeof(args));
-			args[0] = i;
-			runScript(VAR(VAR_INVENTORY_SCRIPT), 0, 0, args);
-		}
-	}
-}
-
-void ScummEngine::inventoryScriptIndy3Mac() {
-	int slot;
-
-	// VAR(67) controls the scroll offset of the inventory in Indy3 for Macintosh.
-	// The inventory consists of two columns with three items visible in each,
-	// so a maximum of six items are visible at once.
-
-	// The scroll offset must be non-negative and if there are six or less items
-	// in the inventory, the inventory is fixed in the top position.
-	const int invCount = getInventoryCount(VAR(VAR_EGO));
-	if (VAR(67) < 0 || invCount <= 6) {
-		VAR(67) = 0;
-	}
-
-	// If there are more than six items in the inventory, clamp the scroll position
-	// to be at most invCount-6, rounded up to the next even integer.
-	bool scrolledToBottom = false;
-	if (invCount > 6 && VAR(67) >= invCount - 6) {
-		VAR(67) = invCount - 6;
-		// Odd number of inventory items? -> increment VAR(67) to make it even
-		if (invCount & 1) {
-			VAR(67)++;
-		}
-		scrolledToBottom = true;
-	}
-
-	// Now update var 83 till 89 to contain the inventory IDs of the
-	// corresponding inventory slots.
-	// Also setup fake verbs for the inventory
-	byte tmp[6] = { 0xFF, 0x06, 0x52, 0x00, 0x00, 0x00 };
-	for (int j = 1; j < 7; j++) {
-		int tmpA = (VAR(67) + j);
-		int tmpB = findInventory(VAR(VAR_EGO), tmpA);
-		VAR(82 + j) = tmpB;
-
-		// Setup fake verb
-		tmp[2] = 0x52 + j;
-		slot = getVerbSlot(100 + j, 0);
-		loadPtrToResource(rtVerb, slot, tmp);
-
-		VerbSlot *vs = &_verbs[slot];
-		vs->type = kTextVerbType;
-		vs->imgindex = 0;
-		vs->curmode = 1;
-		drawVerb(slot, 0);
+		int args[NUM_SCRIPT_LOCAL];
+		memset(args, 0, sizeof(args));
+		args[0] = i;
+		runScript(VAR(VAR_INVENTORY_SCRIPT), 0, 0, args);
 	}
-
-	// Enable up arrow if there are more than six items and we are not already
-	// scrolled all the way up.
-	slot = getVerbSlot(107, 0);
-	_verbs[slot].curmode = (invCount > 6 && VAR(67)) ? 1 : 0;
-	drawVerb(slot, 0);
-
-	// Enable down arrow if there are more than six items and we are not already
-	// scrolled all the way down.
-	slot = getVerbSlot(108, 0);
-	_verbs[slot].curmode = (invCount > 6 && !scrolledToBottom) ? 1 : 0;
-	drawVerb(slot, 0);
-
-	// Redraw!
-	verbMouseOver(0);
 }
 
 void ScummEngine::freezeScripts(int flag) {
@@ -1482,21 +1415,6 @@ void ScummEngine::runInputScript(int clickArea, int val, int mode) {
 
 	// Macintosh version of indy3ega used different interface, so adjust values.
 	if (_game.id == GID_INDY3 && _game.platform == Common::kPlatformMacintosh) {
-		if (clickArea == kVerbClickArea && (val >= 101 && val <= 108)) {
-			if (val == 107) {
-				VAR(67) -= 2;
-				inventoryScriptIndy3Mac();
-				return;
-			} else if (val == 108) {
-				VAR(67) += 2;
-				inventoryScriptIndy3Mac();
-				return;
-			} else {
-				args[0] = kInventoryClickArea;
-				args[1] = VAR(82 + (val - 100));
-			}
-		}
-
 		// Clicks are handled differently in Indy3 mac: param 2 of the
 		// input script is set to 0 for normal clicks, and to 1 for double clicks.
 		// The EGA DOS version of Loom also checks that the second click happens
diff --git a/engines/scumm/script_v5.cpp b/engines/scumm/script_v5.cpp
index c38d67bafd5..9bd81dc037c 100644
--- a/engines/scumm/script_v5.cpp
+++ b/engines/scumm/script_v5.cpp
@@ -21,6 +21,7 @@
 
 #include "scumm/actor.h"
 #include "scumm/charset.h"
+#include "scumm/gfx_mac.h"
 #include "scumm/object.h"
 #include "scumm/resource.h"
 #include "scumm/scumm_v3.h"
@@ -2589,26 +2590,6 @@ void ScummEngine_v5::o5_setVarRange() {
 		setResult(b);
 		_resultVarNumber++;
 	} while (--a);
-
-	// Macintosh version of indy3ega used different interface, so adjust values.
-	if (_game.id == GID_INDY3 && _game.platform == Common::kPlatformMacintosh) {
-#if 0
-		VAR(68) = 0;
-		VAR(69) = 0;
-		VAR(70) = 168;
-		VAR(71) = 0;
-		VAR(72) = 168;
-		VAR(73) = 0;
-		VAR(74) = 168;
-		VAR(75) = 0;
-		VAR(76) = 176;
-		VAR(77) = 176;
-		VAR(78) = 184;
-		VAR(79) = 184;
-		VAR(80) = 192;
-		VAR(81) = 192;
-#endif
-	}
 }
 
 void ScummEngine_v5::o5_startMusic() {
@@ -2958,25 +2939,11 @@ void ScummEngine_v5::o5_verbOps() {
 			vs->origLeft = vs->curRect.left;
 			break;
 		case 6:		// SO_VERB_ON
-			// It seems that the Mac version of Indiana Jones and
-			// the Last Crusade treats the entire inventory as a
-			// single verb, or at least that's my guess as far as
-			// script 12 is concerned. In the 256-color DOS
-			// version (I don't have the EGA version), the script
-			// enables all inventory verbs, and possibly the
-			// inventory arrows. Well, that's what the hard-coded
-			// inventory script does, so this should be fine.
-			//
-			// This fixes a problem where if you offer an object
-			// to someone and then press "Never mind", the next
-			// time you try you see only one inventory object.
-			//
-			// I don't know if it has to be limited to this
-			// particular script, but that's what I'll do for now.
-			if (_game.id == GID_INDY3 && _game.platform == Common::kPlatformMacintosh && verb == 101 && vm.slot[_currentScript].number == 12) {
-				inventoryScriptIndy3Mac();
-			} else
-				vs->curmode = 1;
+			vs->curmode = 1;
+
+			if (_macIndy3Gui && vs->verbid == 101) {
+				_macIndy3Gui->updateInventory();
+			}
 			break;
 		case 7:		// SO_VERB_OFF
 			vs->curmode = 0;
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index 0d31bd52098..e09afbdba91 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -990,7 +990,6 @@ protected:
 	void executeScript();
 	void updateScriptPtr();
 	virtual void runInventoryScript(int i);
-	void inventoryScriptIndy3Mac();
 	virtual void checkAndRunSentenceScript();
 	void runExitScript();
 	void runEntryScript();


Commit: b8d5096d7c2caad30162a81be3a632f97d833911
    https://github.com/scummvm/scummvm/commit/b8d5096d7c2caad30162a81be3a632f97d833911
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Some more Indy 3 Mac inventory work

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


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 6cbfe270911..02f6212bfce 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -582,7 +582,23 @@ void MacIndy3Gui::handleEvent(Common::Event &event) {
 }
 
 void MacIndy3Gui::updateInventory() {
-	debug("TODO: Update inventory");
+	// The Mac inventory script simply turns on the inventory widget and
+	// sets its text to the name of the object in variable 83. However, I
+	// can't find anywhere where this variable is set, so maybe it's set by
+	// the engine itself?
+	//
+	// Rather than guessing, just brute force it.
+
+	int owner = _vm->VAR(_vm->VAR_EGO);
+	int slot = 0;
+	for (int i = 0; i < _vm->_numInventory; i++) {
+		int obj = _vm->_inventory[i];
+		if (obj && _vm->getOwner(obj) == owner) {
+			const byte *name = _vm->getObjOrActorName(obj);
+			drawInventoryText(slot, name, false);
+			slot++;
+		}
+	}
 }
 
 void MacIndy3Gui::show() {
@@ -805,7 +821,7 @@ void MacIndy3Gui::drawInventoryArrow(int arrowX, int arrowY, bool highlighted, b
 	} while (y != y1);
 }
 
-void MacIndy3Gui::drawInventoryText(int slot, char *text, bool highlighted) {
+void MacIndy3Gui::drawInventoryText(int slot, const byte *text, bool highlighted) {
 	int slotX = 423;
 	int slotY = 298 + slot * 11;
 
@@ -835,9 +851,13 @@ void MacIndy3Gui::drawInventoryText(int slot, char *text, bool highlighted) {
 	int x = slotX + 4;
 
 	for (int i = 0; text[i]; i++) {
-		_fonts[0]->drawChar(_macScreen, text[i], x, y, fg);
-		x += _fonts[0]->getCharWidth(text[i]);
+		if (text[i] != '@') {
+			_fonts[0]->drawChar(_macScreen, text[i], x, y, fg);
+			x += _fonts[0]->getCharWidth(text[i]);
+		}
 	}
+
+	copyRectToScreen(Common::Rect(slotX, slotY, slotX + width, slotY + height));
 }
 
 void MacIndy3Gui::fill(Common::Rect r) {
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 9917b506efb..836b7aad462 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -76,7 +76,7 @@ private:
 	void drawButton(Widget *w);
 	void drawInventoryWidget(Widget *w);
 	void drawInventoryArrow(int arrowX, int arrowY, bool highlighted, bool flipped);
-	void drawInventoryText(int slot, char *text, bool highlighted);
+	void drawInventoryText(int slot, const byte *text, bool highlighted);
 
 	// Primitives
 	void fill(Common::Rect r);
diff --git a/engines/scumm/verbs.cpp b/engines/scumm/verbs.cpp
index b7c9fbb682d..6d4d397ff72 100644
--- a/engines/scumm/verbs.cpp
+++ b/engines/scumm/verbs.cpp
@@ -1082,6 +1082,9 @@ void ScummEngine::drawVerb(int verb, int mode) {
 	VerbSlot *vs;
 	bool tmp;
 
+	if (_macIndy3Gui && _macIndy3Gui->isActive())
+		return;
+
 	if (!verb)
 		return;
 
@@ -1158,6 +1161,9 @@ void ScummEngine::drawVerb(int verb, int mode) {
 }
 
 void ScummEngine::restoreVerbBG(int verb) {
+	if (_macIndy3Gui && _macIndy3Gui->isActive())
+		return;
+
 	VerbSlot *vs;
 
 	vs = &_verbs[verb];


Commit: 127c585362dbb13a8ef2181914c1837427311bb4
    https://github.com/scummvm/scummvm/commit/127c585362dbb13a8ef2181914c1837427311bb4
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: More work on Indy 3 Mac GUI

It's now possible to travel all the way to Venice, though there is a
glitch on the travel map there.

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


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 02f6212bfce..381cf2dfe99 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -358,7 +358,28 @@ Common::KeyState ScummEngine::mac_showOldStyleBannerAndPause(const char *msg, in
 	return ks;
 }
 
-// Verb GUI for Indiana Jones and the Last Crusade
+/*
+ * The infamous verb GUI for the Macintosh version of Indiana Jones and the
+ * Last Crusade.
+ *
+ * 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
+ * haven't been able to find in any of the early scripts of the game.
+ *
+ * But the design goal here is to keep things as simple as possible, even if
+ * that means using brute force. So the GUI figures out which verbs are active
+ * by scanning all the verbs, and which objects are in the inventory by
+ * scanning all objects.
+ *
+ * We used to coerce the Mac verb GUI into something that looked like the
+ * GUI from all other versions. This used a number of variables and hard-coded
+ * verbs. The only thing still used of all this is variable 67, to keep track
+ * of the inventory scroll position. The fake verbs are removed when loading
+ * old savegames, but the variables are assumed to be harmless.
+ */
+
+#define BUTTON_TIMER			15
+#define INVENTORY_SLOT_TIMER	7
 
 MacIndy3Gui::MacIndy3Gui(OSystem *system, ScummEngine *vm) :
 	_system(system), _vm(vm), _macScreen(vm->_macScreen), _visible(false) {
@@ -368,6 +389,9 @@ MacIndy3Gui::MacIndy3Gui(OSystem *system, ScummEngine *vm) :
 	_fonts[1] = mfm->getFont(Graphics::MacFont(Graphics::kMacFontGeneva, 9, Graphics::kMacFontBold));
 	_fonts[2] = mfm->getFont(Graphics::MacFont(Graphics::kMacFontGeneva, 9, Graphics::kMacFontBold | Graphics::kMacFontOutline | Graphics::kMacFontCondense));
 
+	// There is one widget for every verb in the game. Verbs include the
+    // inventory widget and conversation options.
+
 	initWidget( 0,   1, 137, 312,  68, 18); // Open
 	initWidget( 1,   2, 137, 332,  68, 18); // Close
 	initWidget( 2,   3,  67, 352,  68, 18); // Give
@@ -385,15 +409,39 @@ MacIndy3Gui::MacIndy3Gui(OSystem *system, ScummEngine *vm) :
 	initWidget(14,  32, 347, 332,  68, 18); // Travel
 	initWidget(15,  33, 347, 352,  68, 18); // To Indy
 	initWidget(16,  34, 347, 352,  68, 18); // To Henry
-	initWidget(17, 100,  67, 292, 348, 18); // Sentence line
-	initWidget(18, 101, 417, 292, 157, 78); // Inventory widget
-	initWidget(19, 119, 324, 312,  91, 18); // Take this:
-	initWidget(20, 120,  67, 292, 507, 18); // Conversation 1
-	initWidget(21, 121,  67, 312, 507, 18); // Conversation 2
-	initWidget(22, 122,  67, 332, 507, 18); // Conversation 3
-	initWidget(23, 123,  67, 352, 507, 18); // Conversation 4
-	initWidget(24, 124,  67, 352, 151, 18); // Conversation 5
-	initWidget(25, 125, 423, 352, 151, 18); // Conversation 6
+	initWidget(17,  90,  67, 292, 507, 18); // Travel option 1
+	initWidget(18,  91,  67, 312, 507, 18); // Travel option 2
+	initWidget(19,  92,  67, 332, 507, 18); // Travel option 3
+	initWidget(20, 100,  67, 292, 348, 18); // Sentence line
+	initWidget(21, 101, 417, 292, 157, 78); // Inventory widget
+	initWidget(22, 119, 324, 312,  91, 18); // Take this:
+	initWidget(23, 120,  67, 292, 507, 18); // Conversation 1
+	initWidget(24, 121,  67, 312, 507, 18); // Conversation 2
+	initWidget(25, 122,  67, 332, 507, 18); // Conversation 3
+	initWidget(26, 123,  67, 352, 507, 18); // Conversation 4
+	initWidget(27, 124,  67, 352, 151, 18); // Conversation 5
+	initWidget(28, 125, 423, 352, 151, 18); // Conversation 6
+
+	// Inventory widget sub-widgets.
+
+	// No matter how many objects you are carrying, only six inventory slots
+	// are visible.
+
+	Widget *inventoryWidget = &_widgets[21];
+	assert(inventoryWidget->verbid == 101);
+
+	int x = inventoryWidget->bounds.left + 6;
+	int y = inventoryWidget->bounds.top + 6;
+
+	// There is only space in the inventory widget for each slot to be 11
+	// pixels tall. But when a slot is highlighted, the highlight is 12 pixels
+	// tall. It's assumed that this will never collide with the text in the
+	// next slot.
+
+	for (int i = 0; i < ARRAYSIZE(_inventorySlots); i++) {
+		initInventorySlot(i, x, y, 128, 12);
+		y += 11;
+	}
 }
 
 MacIndy3Gui::~MacIndy3Gui() {
@@ -423,9 +471,32 @@ void MacIndy3Gui::resetWidget(Widget *w) {
 	w->kill = false;
 }
 
+void MacIndy3Gui::initInventorySlot(int n, int x, int y, int width, int height) {
+	InventorySlot *s = &_inventorySlots[n];
+
+	s->slot = n;
+	s->bounds.left = x;
+	s->bounds.top = y;
+	s->bounds.right = x + width;
+	s->bounds.bottom = y + height;
+}
+
+void MacIndy3Gui::resetInventorySlot(InventorySlot *s) {
+	free(s->name);
+	s->name = nullptr;
+	s->obj = -1;
+	s->timer = 0;
+	s->redraw = false;
+}
+
 bool MacIndy3Gui::isActive() {
+	// Verb script 4 is the regular verb gui.
+	// Verb script 18 is for conversations.
+	// Verb script 200 is for Venice.
+	// Verb script 201 is for travelling from the US.
+	// Verb script 205 is for travelling from Venice.
 	int verbScript = _vm->VAR(_vm->VAR_VERB_SCRIPT);
-	return verbScript == 4 || verbScript == 18;
+	return verbScript == 4 || verbScript == 18 || verbScript == 200 || verbScript == 201 || verbScript == 205;
 }
 
 void MacIndy3Gui::resetAfterLoad() {
@@ -434,19 +505,31 @@ void MacIndy3Gui::resetAfterLoad() {
 	for (int i = 0; i < ARRAYSIZE(_widgets); i++)
 		resetWidget(&_widgets[i]);
 
+	for (int i = 0; i < ARRAYSIZE(_inventorySlots); i++)
+		resetInventorySlot(&_inventorySlots[i]);
+
 	// In the DOS version, verb ID 102-106 were used for the visible
 	// inventory items, and 107-108 for inventory arrow buttons. In the
 	// Macintosh version, the entire inventory widget is verb ID 101.
 	//
 	// In old savegames, the DOS verb IDs may still be present, and have
-	// to be updated when loading them.
+	// to be removed.
 
-	// TODO: Collect the inventory objects
+	bool oldSavegame = false;
 
 	for (int i = 0; i < _vm->_numVerbs; i++) {
-		if (_vm->_verbs[i].verbid >= 102 && _vm->_verbs[i].verbid <= 108)
+		if (_vm->_verbs[i].verbid >= 102 && _vm->_verbs[i].verbid <= 108) {
 			_vm->killVerb(i);
+			oldSavegame = true;
+		}
 	}
+
+	// The old GUI adjusted the offset by 2 when scrolling the inventory.
+
+	if (oldSavegame)
+		_vm->VAR(67) /= 2;
+
+	_inventoryOffset = _vm->VAR(67);
 }
 
 void MacIndy3Gui::update() {
@@ -459,7 +542,27 @@ void MacIndy3Gui::update() {
 	if (!_visible)
 		show();
 
-	// Decrease all timers
+	updateTimers();
+
+	bool keepGuiAlive = false;
+	bool inventoryIsActive = false;
+
+	updateButtons(keepGuiAlive, inventoryIsActive);
+
+	if (!keepGuiAlive) {
+		hide();
+		return;
+	}
+
+	drawButtons();
+
+	if (inventoryIsActive) {
+		updateInventorySlots();
+		drawInventorySlots();
+	}
+}
+
+void MacIndy3Gui::updateTimers() {
 	for (int i = 0; i < ARRAYSIZE(_widgets); i++) {
 		Widget *w = &_widgets[i];
 		if (w->timer > 0) {
@@ -472,9 +575,22 @@ void MacIndy3Gui::update() {
 		w->kill = true;
 	}
 
-	bool keepGuiAlive = false;
+	for (int i = 0; i < ARRAYSIZE(_inventorySlots); i++) {
+		InventorySlot *s = &_inventorySlots[i];
+		if (s->timer > 0) {
+			s->timer--;
+			if (s->timer == 0) {
+				s->redraw = true;
+				_vm->runInputScript(kInventoryClickArea, s->obj, 1);
+			}
+		}
+	}
+}
+
+void MacIndy3Gui::updateButtons(bool &keepGuiAlive, bool &inventoryIsActive) {
+	// Collect all active verbs. Verb slot 0 is special, apparently, so we
+	// don't look at that one.
 
-	// Collect all active verbs
 	for (int i = 1; i < _vm->_numVerbs; i++) {
 		VerbSlot *vs = &_vm->_verbs[i];
 
@@ -485,12 +601,15 @@ void MacIndy3Gui::update() {
 				id = vs->verbid - 1;
 			} else if (vs->verbid >= 32 && vs->verbid <= 34) {
 				id = vs->verbid - 18;
+			} else if (vs->verbid >= 90 && vs->verbid <= 92) {
+				id = vs->verbid - 73;
 			} else if (vs->verbid == 100) {
-				id = 17;
+				id = 20;
 			} else if (vs->verbid == 101) {
-				id = 18;
+				id = 21;
+				inventoryIsActive = true;
 			} else if (vs->verbid >= 119 && vs->verbid <= 125) {
-				id = vs->verbid - 100;
+				id = vs->verbid - 97;
 			} else {
 				const byte *ptr = _vm->getResourceAddress(rtVerb, i);
 				byte buf[270];
@@ -518,6 +637,7 @@ void MacIndy3Gui::update() {
 				if (w->text == nullptr || strcmp((char *)w->text, (char *)buf) != 0) {
 					free(w->text);
 					w->text = (byte *)scumm_strdup((const char *)buf);
+					w->timer = 0;
 					w->redraw = true;
 				}
 
@@ -525,9 +645,76 @@ void MacIndy3Gui::update() {
 			}
 		}
 	}
+}
+
+void MacIndy3Gui::updateInventorySlots() {
+	int owner = _vm->VAR(_vm->VAR_EGO);
+
+	int invCount = _vm->getInventoryCount(owner);
+	int invOffset = _vm->VAR(67);
+
+	// The scroll offset must be non-negative and if there are six or less
+	// items in the inventory, the inventory is fixed in the top position.
 
-	// Erase all inactive buttons. Since buttons are overlapping, we have
-	// to do this first.
+	if (invOffset < 0 || invCount <= 6)
+		invOffset = 0;
+
+	// If there are more than six items in the inventory, clamp the scroll
+	// offset to be at most invCount - 6.
+
+	if (invCount > 6 && invOffset >= invCount - 6)
+		invOffset = invCount - 6;
+
+	_vm->VAR(67) = invOffset;
+
+	int invSlot = 0;
+	invCount = 0;
+
+	for (int i = 0; i < _vm->_numInventory && invSlot < ARRAYSIZE(_inventorySlots); i++) {
+		int obj = _vm->_inventory[i];
+		if (obj && _vm->getOwner(obj) == owner) {
+			if (++invCount >= invOffset) {
+				InventorySlot *s = &_inventorySlots[invSlot];
+				const byte *name = _vm->getObjOrActorName(obj);
+
+				if (name) {
+					if (!s->name || strcmp((char *)s->name, (const char *)name) != 0) {
+						free(s->name);
+						s->name = (byte *)scumm_strdup((const char *)name);
+						s->timer = 0;
+						s->redraw = true;
+					}
+				} else {
+					if (s->name) {
+						free(s->name);
+						s->name = nullptr;
+						s->timer = 0;
+						s->redraw = true;
+					}
+				}
+
+				s->obj = obj;
+				invSlot++;
+			}
+		}
+	}
+
+	for (int i = invSlot; i < ARRAYSIZE(_inventorySlots); i++) {
+		InventorySlot *s = &_inventorySlots[i];
+
+		if (s->name) {
+			free(s->name);
+			s->name = nullptr;
+			s->obj = -1;
+			s->timer = 0;
+			s->redraw = true;
+		}
+	}
+}
+
+void MacIndy3Gui::drawButtons() {
+	// Erase all inactive buttons. Since active buttons may overlap ones
+	// that are now inactive, we have to do this first.
 
 	for (int i = 0; i < ARRAYSIZE(_widgets); i++) {
 		Widget *w = &_widgets[i];
@@ -543,23 +730,39 @@ void MacIndy3Gui::update() {
 	for (int i = 0; i < ARRAYSIZE(_widgets); i++) {
 		Widget *w = &_widgets[i];
 
-		if (w->verbslot == -1)
-			continue;
-
-		if (w->redraw) {
+		if (w->verbslot != -1 && w->redraw) {
 			debug("Drawing button %d: (%d) %s", i, w->verbid, w->text);
 			drawWidget(w);
 		}
 	}
+}
 
-	if (!keepGuiAlive)
-		hide();
+void MacIndy3Gui::drawInventorySlots() {
+	// Since the inventory slots overlap, draw the highlighted ones (and there
+	// should really only be one at a time) first.
+
+	for (int i = 0; i < ARRAYSIZE(_inventorySlots); i++) {
+		InventorySlot *s = &_inventorySlots[i];
+
+		if (s->redraw && s->timer == 0)
+			drawInventorySlot(s);
+	}
+
+	for (int i = 0; i < ARRAYSIZE(_inventorySlots); i++) {
+		InventorySlot *s = &_inventorySlots[i];
+
+		if (s->redraw)
+			drawInventorySlot(s);
+	}
 }
 
 void MacIndy3Gui::handleEvent(Common::Event &event) {
 	if (event.type != Common::EVENT_LBUTTONDOWN)
 		return;
 
+	if (_vm->_userPut <= 0)
+		return;
+
 	int x = event.mouse.x;
 	int y = event.mouse.y;
 
@@ -567,36 +770,24 @@ void MacIndy3Gui::handleEvent(Common::Event &event) {
 		Widget *w = &_widgets[i];
 
 		if (w->verbid && w->enabled && w->bounds.contains(x, y)) {
-			// The input script is invoked when the button animation
-			// times out. Speed-runners will probably hate this, but
-			// I think it looks better this way.
-			//
-			// Besides, wouldn't real speed runners use keyboard
-			// shortcuts? (TODO: Check if they're really different
-			// than in the DOS version.)
-			w->redraw = true;
-			w->timer = 15;
-			return;
-		}
-	}
-}
-
-void MacIndy3Gui::updateInventory() {
-	// The Mac inventory script simply turns on the inventory widget and
-	// sets its text to the name of the object in variable 83. However, I
-	// can't find anywhere where this variable is set, so maybe it's set by
-	// the engine itself?
-	//
-	// Rather than guessing, just brute force it.
-
-	int owner = _vm->VAR(_vm->VAR_EGO);
-	int slot = 0;
-	for (int i = 0; i < _vm->_numInventory; i++) {
-		int obj = _vm->_inventory[i];
-		if (obj && _vm->getOwner(obj) == owner) {
-			const byte *name = _vm->getObjOrActorName(obj);
-			drawInventoryText(slot, name, false);
-			slot++;
+			if (w->verbid != 101) {
+				// The input script is invoked when the button animation
+				// times out. Speed-runners will probably hate this, but
+				// I think it looks better this way.
+				w->redraw = true;
+				w->timer = BUTTON_TIMER;
+				return;
+			} else {
+				for (int j = 0; j < ARRAYSIZE(_inventorySlots); j++) {
+					InventorySlot *s = &_inventorySlots[j];
+
+					if (s->obj != -1 && s->bounds.contains(x, y)) {
+						s->redraw = true;
+						s->timer = INVENTORY_SLOT_TIMER;
+						return;
+					}
+				}
+			}
 		}
 	}
 }
@@ -758,13 +949,14 @@ void MacIndy3Gui::drawButton(Widget *w) {
 // indistinguishable from clicking above or below.
 
 void MacIndy3Gui::drawInventoryWidget(Widget *w) {
+	for (int i = 0; i < ARRAYSIZE(_inventorySlots); i++)
+		resetInventorySlot(&_inventorySlots[i]);
+
 	Common::Rect r = w->bounds;
 
 	drawShadowBox(r);
 	drawShadowFrame(Common::Rect(r.left + 4, r.top + 4, r.right - 22, r.bottom - 4), 0, 15);
 
-	// TODO: Draw inventory text
-
 	// Scrollbar
 
 	drawShadowFrame(Common::Rect(r.right - 20, r.top + 4, r.right - 4, r.top + 20), 15, 255);
@@ -821,13 +1013,11 @@ void MacIndy3Gui::drawInventoryArrow(int arrowX, int arrowY, bool highlighted, b
 	} while (y != y1);
 }
 
-void MacIndy3Gui::drawInventoryText(int slot, const byte *text, bool highlighted) {
-	int slotX = 423;
-	int slotY = 298 + slot * 11;
-
+void MacIndy3Gui::drawInventorySlot(InventorySlot *s) {
+	debug("Drawing inventory slot: [%d] %s", s->slot, s->name);
 	int fg, bg;
 
-	if (highlighted) {
+	if (s->timer) {
 		fg = 15;
 		bg = 0;
 	} else {
@@ -835,29 +1025,22 @@ void MacIndy3Gui::drawInventoryText(int slot, const byte *text, bool highlighted
 		bg = 15;
 	}
 
-#if 0
-	// The inventory slots overlap slightly, so we have to clear the entire
-	// area, then highlight the at most single one that's highlighted.
-	_macScreen->fillRect(Common::Rect(423, 298, 551, 365), 15);
-#endif
-
-	int height = 12;
-	int width = 128;
+	_macScreen->fillRect(s->bounds, bg);
 
-	if (highlighted)
-		_macScreen->fillRect(Common::Rect(slotX, slotY, slotX + width, slotY + height), bg);
+	if (s->name) {
+		int y = s->bounds.top - 1;
+		int x = s->bounds.left + 4;
 
-	int y = slotY - 1;
-	int x = slotX + 4;
-
-	for (int i = 0; text[i]; i++) {
-		if (text[i] != '@') {
-			_fonts[0]->drawChar(_macScreen, text[i], x, y, fg);
-			x += _fonts[0]->getCharWidth(text[i]);
+		for (int i = 0; s->name[i]; i++) {
+			if (s->name[i] != '@') {
+				_fonts[0]->drawChar(_macScreen, s->name[i], x, y, fg);
+				x += _fonts[0]->getCharWidth(s->name[i]);
+			}
 		}
 	}
 
-	copyRectToScreen(Common::Rect(slotX, slotY, slotX + width, slotY + height));
+	s->redraw = false;
+	copyRectToScreen(s->bounds);
 }
 
 void MacIndy3Gui::fill(Common::Rect r) {
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 836b7aad462..378a274341d 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -40,14 +40,15 @@ public:
 
 	bool isActive();
 	void resetAfterLoad();
+
 	void update();
+
 	void handleEvent(Common::Event &event);
-	void updateInventory();
 
 private:
-	OSystem *_system;
-	ScummEngine *_vm;
-	Graphics::Surface *_macScreen;
+	OSystem *_system = nullptr;
+	ScummEngine *_vm = nullptr;
+	Graphics::Surface *_macScreen = nullptr;
 	const Graphics::Font *_fonts[3];
 
 	bool _visible = false;
@@ -64,19 +65,42 @@ private:
 		bool kill = false;
 	};
 
-	Widget _widgets[26];
+	Widget _widgets[29];
+
+	struct InventorySlot {
+		Common::Rect bounds;
+		byte *name = nullptr;
+		int slot = -1;
+		int obj = -1;
+		int timer = 0;
+		bool redraw = false;
+	};
+
+	InventorySlot _inventorySlots[6];
+	int _inventoryOffset = 0;
 
-	void initWidget(int n, int verbid, int x, int y, int w, int h);
+	void initWidget(int n, int verbid, int x, int y, int width, int height);
 	void resetWidget(Widget *w);
+	void initInventorySlot(int n, int x, int y, int width, int height);
+	void resetInventorySlot(InventorySlot *s);
+
 	void clear();
 	void show();
 	void hide();
+
+	void updateTimers();
+	void updateButtons(bool &keepGuiAlive, bool &inventoryIsActive);
+	void updateInventorySlots();
+
+	void drawButtons();
+	void drawInventorySlots();
+
 	void drawWidget(Widget *w);
 	void undrawWidget(Widget *w);
 	void drawButton(Widget *w);
 	void drawInventoryWidget(Widget *w);
 	void drawInventoryArrow(int arrowX, int arrowY, bool highlighted, bool flipped);
-	void drawInventoryText(int slot, const byte *text, bool highlighted);
+	void drawInventorySlot(InventorySlot *slot);
 
 	// Primitives
 	void fill(Common::Rect r);
diff --git a/engines/scumm/script.cpp b/engines/scumm/script.cpp
index a846fd11a87..6bd0b8ed348 100644
--- a/engines/scumm/script.cpp
+++ b/engines/scumm/script.cpp
@@ -740,6 +740,8 @@ void ScummEngine::writeVar(uint var, int value) {
 			}
 		}
 
+		if (var == VAR_VERB_SCRIPT)
+			debug("VAR_VERB_SCRIPT = %d", value);
 		_scummVars[var] = value;
 
 		// Unlike the PC version, the Macintosh version of Loom appears
diff --git a/engines/scumm/script_v5.cpp b/engines/scumm/script_v5.cpp
index 9bd81dc037c..f24739ad031 100644
--- a/engines/scumm/script_v5.cpp
+++ b/engines/scumm/script_v5.cpp
@@ -21,7 +21,6 @@
 
 #include "scumm/actor.h"
 #include "scumm/charset.h"
-#include "scumm/gfx_mac.h"
 #include "scumm/object.h"
 #include "scumm/resource.h"
 #include "scumm/scumm_v3.h"
@@ -2940,10 +2939,6 @@ void ScummEngine_v5::o5_verbOps() {
 			break;
 		case 6:		// SO_VERB_ON
 			vs->curmode = 1;
-
-			if (_macIndy3Gui && vs->verbid == 101) {
-				_macIndy3Gui->updateInventory();
-			}
 			break;
 		case 7:		// SO_VERB_OFF
 			vs->curmode = 0;


Commit: 98229e51551d21e9c971f9c2a532b6b1cbbf18e1
    https://github.com/scummvm/scummvm/commit/98229e51551d21e9c971f9c2a532b6b1cbbf18e1
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Hopefully fix Indy 3 Mac glitch on travel map

The GUI should now know better when to back off and not try to clear
what's no longer the Verb screen.

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


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 381cf2dfe99..f74e1d3ecf7 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -490,13 +490,20 @@ void MacIndy3Gui::resetInventorySlot(InventorySlot *s) {
 }
 
 bool MacIndy3Gui::isActive() {
+	if (_vm->_virtscr[kVerbVirtScreen].topline != 144)
+		return false;
+
 	// Verb script 4 is the regular verb gui.
 	// Verb script 18 is for conversations.
 	// Verb script 200 is for Venice.
 	// Verb script 201 is for travelling from the US.
 	// Verb script 205 is for travelling from Venice.
 	int verbScript = _vm->VAR(_vm->VAR_VERB_SCRIPT);
-	return verbScript == 4 || verbScript == 18 || verbScript == 200 || verbScript == 201 || verbScript == 205;
+	if (verbScript == 4 || verbScript == 18 || verbScript == 200 || verbScript == 201 || verbScript == 205)
+		return true;
+
+	debug("VAR_VERB_SCRIPT = %d", verbScript);
+	return false;
 }
 
 void MacIndy3Gui::resetAfterLoad() {
@@ -539,9 +546,6 @@ void MacIndy3Gui::update() {
 		return;
 	}
 
-	if (!_visible)
-		show();
-
 	updateTimers();
 
 	bool keepGuiAlive = false;
@@ -550,10 +554,14 @@ void MacIndy3Gui::update() {
 	updateButtons(keepGuiAlive, inventoryIsActive);
 
 	if (!keepGuiAlive) {
-		hide();
+		// We haven't drawn anything yet, so just silently hide it.
+		_visible = false;
 		return;
 	}
 
+	if (!_visible)
+		show();
+
 	drawButtons();
 
 	if (inventoryIsActive) {
@@ -760,7 +768,7 @@ void MacIndy3Gui::handleEvent(Common::Event &event) {
 	if (event.type != Common::EVENT_LBUTTONDOWN)
 		return;
 
-	if (_vm->_userPut <= 0)
+	if (!isActive() || _vm->_userPut <= 0)
 		return;
 
 	int x = event.mouse.x;
@@ -793,9 +801,11 @@ void MacIndy3Gui::handleEvent(Common::Event &event) {
 }
 
 void MacIndy3Gui::show() {
-	if (_visible)
+	if (_visible || !isActive())
 		return;
 
+	debug("SHOW");
+
 	clear();
 	_visible = true;
 }
@@ -809,6 +819,11 @@ void MacIndy3Gui::hide() {
 	for (int i = 0; i < ARRAYSIZE(_widgets); i++)
 		resetWidget(&_widgets[i]);
 
+	if (_vm->_virtscr[kVerbVirtScreen].topline != 144)
+		return;
+
+	debug("HIDE");
+
 	_macScreen->fillRect(Common::Rect(0, 288, 640, 400), 0);
 	_system->copyRectToScreen(_macScreen->getBasePtr(0, 288), _macScreen->pitch, 0, 288, 640, 112);
 }
diff --git a/engines/scumm/script.cpp b/engines/scumm/script.cpp
index 6bd0b8ed348..a846fd11a87 100644
--- a/engines/scumm/script.cpp
+++ b/engines/scumm/script.cpp
@@ -740,8 +740,6 @@ void ScummEngine::writeVar(uint var, int value) {
 			}
 		}
 
-		if (var == VAR_VERB_SCRIPT)
-			debug("VAR_VERB_SCRIPT = %d", value);
 		_scummVars[var] = value;
 
 		// Unlike the PC version, the Macintosh version of Loom appears


Commit: 3bf0b424cdf49dfce72a7a51d26dae93602ae8fd
    https://github.com/scummvm/scummvm/commit/3bf0b424cdf49dfce72a7a51d26dae93602ae8fd
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Some more Indy 3 Mac GUI cleanup

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index f74e1d3ecf7..1fc8f6c1492 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -493,16 +493,27 @@ bool MacIndy3Gui::isActive() {
 	if (_vm->_virtscr[kVerbVirtScreen].topline != 144)
 		return false;
 
-	// Verb script 4 is the regular verb gui.
-	// Verb script 18 is for conversations.
-	// Verb script 200 is for Venice.
-	// Verb script 201 is for travelling from the US.
-	// Verb script 205 is for travelling from Venice.
+	// Known verb scripts
+	//
+	//   4 - Regular verb GUI
+	//  18 - Conversations
+	// 200 - Venice, outdoors
+	// 201 - Travel to Henry's or Venice
+	// 205 - Travel from Venice
+	//
+	//  19 - Boxing
+
 	int verbScript = _vm->VAR(_vm->VAR_VERB_SCRIPT);
 	if (verbScript == 4 || verbScript == 18 || verbScript == 200 || verbScript == 201 || verbScript == 205)
 		return true;
 
-	debug("VAR_VERB_SCRIPT = %d", verbScript);
+	static int lastWarnVerbScript = -1;
+
+	if (verbScript != lastWarnVerbScript) {
+		debug("VAR_VERB_SCRIPT = %d", verbScript);
+		lastWarnVerbScript = verbScript;
+	}
+
 	return false;
 }
 
@@ -555,6 +566,9 @@ void MacIndy3Gui::update() {
 
 	if (!keepGuiAlive) {
 		// We haven't drawn anything yet, so just silently hide it.
+		for (int i = 0; i < ARRAYSIZE(_widgets); i++)
+			resetWidget(&_widgets[i]);
+
 		_visible = false;
 		return;
 	}


Commit: a14de55ddade08425e74ff07a7e36d7a5acac041
    https://github.com/scummvm/scummvm/commit/a14de55ddade08425e74ff07a7e36d7a5acac041
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: More Indy 3 Mac GUI fixes

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 1fc8f6c1492..16092433d5a 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -507,13 +507,6 @@ bool MacIndy3Gui::isActive() {
 	if (verbScript == 4 || verbScript == 18 || verbScript == 200 || verbScript == 201 || verbScript == 205)
 		return true;
 
-	static int lastWarnVerbScript = -1;
-
-	if (verbScript != lastWarnVerbScript) {
-		debug("VAR_VERB_SCRIPT = %d", verbScript);
-		lastWarnVerbScript = verbScript;
-	}
-
 	return false;
 }
 
@@ -551,6 +544,13 @@ void MacIndy3Gui::resetAfterLoad() {
 }
 
 void MacIndy3Gui::update() {
+	static int lastVerbScript = -1;
+
+	if (_vm->VAR(_vm->VAR_VERB_SCRIPT) != lastVerbScript) {
+		debug("VAR_VERB_SCRIPT = %d", _vm->VAR(_vm->VAR_VERB_SCRIPT));
+		lastVerbScript = _vm->VAR(_vm->VAR_VERB_SCRIPT);
+	}
+
 	if (!isActive()) {
 		if (_visible)
 			hide();
@@ -565,11 +565,7 @@ void MacIndy3Gui::update() {
 	updateButtons(keepGuiAlive, inventoryIsActive);
 
 	if (!keepGuiAlive) {
-		// We haven't drawn anything yet, so just silently hide it.
-		for (int i = 0; i < ARRAYSIZE(_widgets); i++)
-			resetWidget(&_widgets[i]);
-
-		_visible = false;
+		hide();
 		return;
 	}
 


Commit: 62af89334841a465e5db8a8137c7a04e0992d97d
    https://github.com/scummvm/scummvm/commit/62af89334841a465e5db8a8137c7a04e0992d97d
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Major (incomplete) refactoring of Indy 3 Mac GUI

Widgets are now proper objects. I'm back to the point where verbs work
again, but not the inventory. Still, this is too scary not to commit now
that it's beginning to work again.

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


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 16092433d5a..01fe70a05eb 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -56,7 +56,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. All
 	// other drawing to that area is suspended.
-	if (vs->number == kVerbVirtScreen && _macIndy3Gui->isActive())
+	if (vs->number == kVerbVirtScreen && _macIndy3Gui->isVisible())
 		return;
 
 	const byte *pixels = vs->getPixels(x, top);
@@ -381,6 +381,310 @@ Common::KeyState ScummEngine::mac_showOldStyleBannerAndPause(const char *msg, in
 #define BUTTON_TIMER			15
 #define INVENTORY_SLOT_TIMER	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;
+}
+
+void MacIndy3Gui::Widget::reset() {
+	_timer = 0;
+	_enabled = false;
+	_redraw = false;
+}
+
+bool MacIndy3Gui::Widget::updateTimer() {
+	if (_timer == 0)
+		return false;
+	return --_timer == 0;
+}
+
+void MacIndy3Gui::Widget::copyRectToScreen(Common::Rect r) {
+	_gui->copyRectToScreen(r);
+}
+
+void MacIndy3Gui::Widget::fill(Common::Rect r) {
+	_gui->fill(r);
+}
+
+void MacIndy3Gui::Widget::drawBitmap(Common::Rect r, const uint16 *bitmap, byte color) {
+	_gui->drawBitmap(r, bitmap, color);
+}
+
+void MacIndy3Gui::Widget::drawShadowBox(Common::Rect r) {
+	_surface->hLine(r.left + 1, r.top, r.right - 3, 0);
+	_surface->hLine(r.left + 1, r.bottom - 2, r.right - 3, 0);
+	_surface->vLine(r.left, r.top + 1, r.bottom - 3, 0);
+	_surface->vLine(r.right - 2, r.top + 1, r.bottom - 3, 0);
+
+	_surface->hLine(r.left + 2, r.bottom - 1, r.right - 1, 0);
+	_surface->vLine(r.right - 1, r.top + 2, r.bottom - 2, 0);
+
+	_surface->hLine(r.left + 1, r.top + 1, r.right - 3, 15);
+	_surface->vLine(r.left + 1, r.top + 2, r.bottom - 3, 15);
+}
+
+void MacIndy3Gui::Widget::drawShadowFrame(Common::Rect r, byte shadowColor, byte fillColor) {
+	_surface->hLine(r.left, r.top, r.right - 1, 0);
+	_surface->hLine(r.left, r.bottom - 1, r.right - 1, 0);
+	_surface->vLine(r.left, r.top + 1, r.bottom - 2, 0);
+	_surface->vLine(r.right - 1, r.top + 1, r.bottom - 2, 0);
+
+	_surface->hLine(r.left + 1, r.top + 1, r.right - 2, shadowColor);
+	_surface->vLine(r.left + 1, r.top + 2, r.bottom - 2, shadowColor);
+
+	if (fillColor != 255) {
+		Common::Rect fillRect(r.left + 2, r.top + 2, r.right - 1, r.bottom - 1);
+
+		if (fillColor == 0)
+			fill(fillRect);
+		else
+			_surface->fillRect(fillRect, fillColor);
+	}
+}
+
+MacIndy3Gui::GuiWidget::GuiWidget(int verbid, int x, int y, int width, int height) : MacIndy3Gui::Widget(x, y, width, height) {
+	_verbid = verbid;
+}
+
+void MacIndy3Gui::GuiWidget::reset() {
+	MacIndy3Gui::Widget::reset();
+	_verbslot = -1;
+	_visible = false;
+	_kill = false;
+}
+
+void MacIndy3Gui::GuiWidget::updateVerb(int verbslot) {
+	VerbSlot *vs = &_vm->_verbs[verbslot];
+	bool enabled = (vs->curmode == 1);
+
+	if (_enabled != enabled)
+		_redraw = true;
+
+	_verbslot = verbslot;
+	_verbid = vs->verbid;
+	_enabled = enabled;
+	_kill = false;
+}
+
+void MacIndy3Gui::GuiWidget::draw() {
+	// Clear the area used by the widget
+	fill(_bounds);
+	_visible = true;
+	_redraw = false;
+}
+
+void MacIndy3Gui::GuiWidget::undraw() {
+	fill(_bounds);
+	copyRectToScreen(_bounds);
+}
+
+MacIndy3Gui::Button::Button(int verbid, int x, int y, int width, int height) : MacIndy3Gui::GuiWidget(verbid, x, y, width, height) {
+}
+
+MacIndy3Gui::Button::~Button() {
+	free(_text);
+}
+
+void MacIndy3Gui::Button::reset() {
+	MacIndy3Gui::GuiWidget::reset();
+	free(_text);
+	_text = nullptr;
+}
+
+bool MacIndy3Gui::Button::handleEvent(Common::Event &event) {
+	if (event.type != Common::EVENT_LBUTTONDOWN)
+		return false;
+
+	int x = event.mouse.x;
+	int y = event.mouse.y;
+
+	if (_verbid && _enabled && _bounds.contains(x, y)) {
+		_redraw = true;
+		_timer = 15;
+		return true;
+	}
+
+	return false;
+}
+
+bool MacIndy3Gui::Button::updateTimer() {
+	bool ret = GuiWidget::updateTimer();
+
+	if (ret) {
+		if (_visible) {
+			_vm->runInputScript(kVerbClickArea, _verbid, 1);
+			_redraw = true;
+		}
+	}
+
+	return ret;
+}
+
+void MacIndy3Gui::Button::updateVerb(int verbslot) {
+	MacIndy3Gui::GuiWidget::updateVerb(verbslot);
+
+	const byte *ptr = _vm->getResourceAddress(rtVerb, verbslot);
+	byte buf[270];
+
+	_vm->convertMessageToString(ptr, buf, sizeof(buf));
+	if (!_text || strcmp((char *)_text, (char *)buf) != 0) {
+		free(_text);
+		_text = (byte *)scumm_strdup((const char *)buf);
+		_timer = 0;
+		_redraw = true;
+	}
+}
+
+void MacIndy3Gui::Button::draw() {
+	MacIndy3Gui::GuiWidget::draw();
+
+	if (_timer == 0) {
+		drawShadowBox(_bounds);
+	} else {
+		// I have only been able to capture a screenshot of the pressed
+		// button in black and white, where the checkerboard background
+		// makes it hard to see exactly which pixels should be drawn.
+		// Basilisk II runs it too fast, and I haven't gotten Mini vMac
+		// to run it in 16-color mode.
+		//
+		// All I can say for certain is that the upper left corner is
+		// rounded while the lower right is not. I'm going to assume
+		// that the shadow is always drawn, and the rest of the button
+		// is just shifted down to the right. That would make the other
+		// two corners rounded.
+		_surface->hLine(_bounds.left + 2, _bounds.top + 1, _bounds.right - 2, 0);
+		_surface->hLine(_bounds.left + 2, _bounds.bottom - 1, _bounds.right - 1, 0);
+		_surface->vLine(_bounds.left + 1, _bounds.top + 2, _bounds.bottom - 2, 0);
+		_surface->vLine(_bounds.right - 1, _bounds.top + 2, _bounds.bottom - 2, 0);
+
+		_surface->hLine(_bounds.left + 2, _bounds.top + 2, _bounds.right - 2, 15);
+		_surface->vLine(_bounds.left + 2, _bounds.top + 3, _bounds.bottom - 2, 15);
+	}
+
+	// The text is drawn centered. Based on experimentation, I think the
+	// width is always based on the outlined font, and the button shadow is
+	// not counted as part of the button width.
+	//
+	// This gives us pixel perfect rendering for the English verbs.
+
+	if (_text) {
+		const Graphics::Font *boldFont = _gui->getFont(1);
+		const Graphics::Font *outlineFont = _gui->getFont(2);
+
+		int stringWidth = 0;
+		for (int i = 0; _text[i]; i++)
+			stringWidth += outlineFont->getCharWidth(_text[i]);
+
+		int x = (_bounds.left + (_bounds.width() - 1 - stringWidth) / 2) - 1;
+		int y = _bounds.top + 2;
+		int color = _enabled ? 15 : 0;
+
+		if (_timer) {
+			x++;
+			y++;
+		}
+
+		for (int i = 0; _text[i]; i++) {
+			if (_enabled)
+				outlineFont->drawChar(_surface, _text[i], x, y, 0);
+			boldFont->drawChar(_surface, _text[i], x + 1, y, color);
+			x += boldFont->getCharWidth(_text[i]);
+		}
+	}
+
+	copyRectToScreen(_bounds);
+}
+
+// Desired behavior:
+//
+// The drag handle of the scrollbar never changes size. It's only drawn when
+// there are enough inventory items to scroll.
+//
+// The size of the scroll handle is fixed, not scaled to indicate the number of
+// objects in your inventory.
+//
+// The exact positions of the scroll handle are not yet known. Probably just a
+// simple calculation.
+//
+// Clicking on an arrow scrolls up or down by one row. Clicking and holding
+// scrolls the inventory. The time between scrolling is constant, i.e. the
+// first delay is not different. The delay seems to depend on the speed of the
+// Mac, so pick something that feels right.
+//
+// Clicking above or below the handle scrolls up or down by - probably - one
+// page. I've never seen the inventory full enough for this to mean anything
+// else than scrolling to the top or bottom.
+//
+// Dragging the handle is not possible. Clicking on the handle is probably
+// indistinguishable from clicking above or below.
+
+MacIndy3Gui::InventoryWidget::InventoryWidget(int verbid, int x, int y, int width, int height) : MacIndy3Gui::GuiWidget(verbid, x, y, width, height) {
+}
+
+bool MacIndy3Gui::InventoryWidget::handleEvent(Common::Event &event) {
+	return false;
+}
+
+void MacIndy3Gui::InventoryWidget::draw() {
+	MacIndy3Gui::GuiWidget::draw();
+
+#if 0
+	for (int i = 0; i < ARRAYSIZE(_slots); i++)
+		_slots[i]->reset();
+#endif
+
+	drawShadowBox(_bounds);
+	drawShadowFrame(Common::Rect(_bounds.left + 4, _bounds.top + 4, _bounds.right - 22, _bounds.bottom - 4), 0, 15);
+
+#if 0
+	// Scrollbar
+	drawInventoryScrollButton(_inventoryScrollButtons[0]);
+	drawInventoryScrollButton(_inventoryScrollButtons[1]);
+#endif
+
+	drawShadowFrame(Common::Rect(_bounds.right - 20, _bounds.top + 19, _bounds.right - 4, _bounds.bottom - 19), 0, 255);
+
+	copyRectToScreen(_bounds);
+}
+
+MacIndy3Gui::InventorySlot::InventorySlot(int slot, int x, int y, int width, int height) : MacIndy3Gui::Widget(x, y, width, height) {
+	_slot = slot;
+}
+
+void MacIndy3Gui::InventorySlot::reset() {
+	Widget::reset();
+	free(_name);
+	_name = nullptr;
+	_obj = -1;
+}
+
+bool MacIndy3Gui::InventorySlot::updateTimer() {
+	bool ret = Widget::updateTimer();
+
+	if (ret) {
+		_vm->runInputScript(kInventoryClickArea, _obj, 1);
+		_redraw = true;
+	}
+
+	return ret;
+}
+
+void MacIndy3Gui::InventorySlot::draw() {
+}
+
+MacIndy3Gui::InventoryScrollButton::InventoryScrollButton(int x, int y, int width, int height, ScrollDirection direction) : MacIndy3Gui::Widget(x, y, width, height) {
+	_direction = direction;
+}
+
+void MacIndy3Gui::InventoryScrollButton::draw() {
+}
+
 MacIndy3Gui::MacIndy3Gui(OSystem *system, ScummEngine *vm) :
 	_system(system), _vm(vm), _macScreen(vm->_macScreen), _visible(false) {
 	Graphics::MacFontManager *mfm = _vm->_macFontManager;
@@ -390,110 +694,99 @@ MacIndy3Gui::MacIndy3Gui(OSystem *system, ScummEngine *vm) :
 	_fonts[2] = mfm->getFont(Graphics::MacFont(Graphics::kMacFontGeneva, 9, Graphics::kMacFontBold | Graphics::kMacFontOutline | Graphics::kMacFontCondense));
 
 	// There is one widget for every verb in the game. Verbs include the
-    // inventory widget and conversation options.
-
-	initWidget( 0,   1, 137, 312,  68, 18); // Open
-	initWidget( 1,   2, 137, 332,  68, 18); // Close
-	initWidget( 2,   3,  67, 352,  68, 18); // Give
-	initWidget( 3,   4, 277, 332,  68, 18); // Turn on
-	initWidget( 4,   5, 277, 352,  68, 18); // Turn off
-	initWidget( 5,   6,  67, 312,  68, 18); // Push
-	initWidget( 6,   7,  67, 332,  68, 18); // Pull
-	initWidget( 7,   8, 277, 312,  68, 18); // Use
-	initWidget( 8,   9, 137, 352,  68, 18); // Look at
-	initWidget( 9,  10, 207, 312,  68, 18); // Walk to
-	initWidget(10,  11, 207, 332,  68, 18); // Pick up
-	initWidget(11,  12, 207, 352,  68, 18); // What is
-	initWidget(12,  13, 347, 312,  68, 18); // Talk
-	initWidget(13,  14,  97, 312, 121, 18); // Never mind.
-	initWidget(14,  32, 347, 332,  68, 18); // Travel
-	initWidget(15,  33, 347, 352,  68, 18); // To Indy
-	initWidget(16,  34, 347, 352,  68, 18); // To Henry
-	initWidget(17,  90,  67, 292, 507, 18); // Travel option 1
-	initWidget(18,  91,  67, 312, 507, 18); // Travel option 2
-	initWidget(19,  92,  67, 332, 507, 18); // Travel option 3
-	initWidget(20, 100,  67, 292, 348, 18); // Sentence line
-	initWidget(21, 101, 417, 292, 157, 78); // Inventory widget
-	initWidget(22, 119, 324, 312,  91, 18); // Take this:
-	initWidget(23, 120,  67, 292, 507, 18); // Conversation 1
-	initWidget(24, 121,  67, 312, 507, 18); // Conversation 2
-	initWidget(25, 122,  67, 332, 507, 18); // Conversation 3
-	initWidget(26, 123,  67, 352, 507, 18); // Conversation 4
-	initWidget(27, 124,  67, 352, 151, 18); // Conversation 5
-	initWidget(28, 125, 423, 352, 151, 18); // Conversation 6
-
+	// inventory widget and conversation options.
+
+	Widget::_vm = _vm;
+	Widget::_surface = _macScreen;
+	Widget::_gui = this;
+
+	_widgets[0]  = new Button(  1, 137, 312,  68, 18); // Open
+	_widgets[1]  = new Button(  2, 137, 332,  68, 18); // Close
+	_widgets[2]  = new Button(  3,  67, 352,  68, 18); // Give
+	_widgets[3]  = new Button(  4, 277, 332,  68, 18); // Turn on
+	_widgets[4]  = new Button(  5, 277, 352,  68, 18); // Turn off
+	_widgets[5]  = new Button(  6,  67, 312,  68, 18); // Push
+	_widgets[6]  = new Button(  7,  67, 332,  68, 18); // Pull
+	_widgets[7]  = new Button(  8, 277, 312,  68, 18); // Use
+	_widgets[8]  = new Button(  9, 137, 352,  68, 18); // Look at
+	_widgets[9]  = new Button( 10, 207, 312,  68, 18); // Walk to
+	_widgets[10] = new Button( 11, 207, 332,  68, 18); // Pick up
+	_widgets[11] = new Button( 12, 207, 352,  68, 18); // What is
+	_widgets[12] = new Button( 13, 347, 312,  68, 18); // Talk
+	_widgets[13] = new Button( 14,  97, 312, 121, 18); // Never mind.
+	_widgets[14] = new Button( 32, 347, 332,  68, 18); // Travel
+	_widgets[15] = new Button( 33, 347, 352,  68, 18); // To Indy
+	_widgets[16] = new Button( 34, 347, 352,  68, 18); // To Henry
+	_widgets[17] = new Button( 90,  67, 292, 507, 18); // Travel 1
+	_widgets[18] = new Button( 91,  67, 312, 507, 18); // Travel 2
+	_widgets[19] = new Button( 92,  67, 332, 507, 18); // Travel 3
+	_widgets[20] = new Button(100,  67, 292, 348, 18); // Sentence
+	_widgets[21] = new InventoryWidget(101, 417, 292, 157, 78);
+	_widgets[22] = new Button(119, 324, 312,  91, 18); // Take this:
+	_widgets[23] = new Button(120,  67, 292, 507, 18); // Converse 1
+	_widgets[24] = new Button(121,  67, 312, 507, 18); // Converse 2
+	_widgets[25] = new Button(122,  67, 332, 507, 18); // Converse 3
+	_widgets[26] = new Button(123,  67, 352, 507, 18); // Converse 4
+	_widgets[27] = new Button(124,  67, 352, 151, 18); // Converse 5
+	_widgets[28] = new Button(125, 423, 352, 151, 18); // Converse 6
+
+#if 0
 	// Inventory widget sub-widgets.
 
 	// No matter how many objects you are carrying, only six inventory slots
 	// are visible.
 
-	Widget *inventoryWidget = &_widgets[21];
-	assert(inventoryWidget->verbid == 101);
+	Widget *w = _widgets[21];
+	assert(w->_verbid == 101);
 
-	int x = inventoryWidget->bounds.left + 6;
-	int y = inventoryWidget->bounds.top + 6;
+	int x = w->_bounds.left + 6;
+	int y = w->_bounds.top + 6;
 
-	// There is only space in the inventory widget for each slot to be 11
-	// pixels tall. But when a slot is highlighted, the highlight is 12 pixels
-	// tall. It's assumed that this will never collide with the text in the
-	// next slot.
+	// Each slot is 12 pixels tall (as seen when they are highlighted), which
+	// means they have to overlap slightly to fit. It is assumed that this will
+	// never interfere with the text drawing.
 
 	for (int i = 0; i < ARRAYSIZE(_inventorySlots); i++) {
-		initInventorySlot(i, x, y, 128, 12);
+		_inventorySlots[i] = new InventorySlot(i, x, y, 128, 12);
+		_inventorySlots[i]->setGui(this);
+		_inventorySlots[i]->setSurface(_vm->_macScreen);
 		y += 11;
 	}
-}
 
-MacIndy3Gui::~MacIndy3Gui() {
-	for (int i = 0; i < ARRAYSIZE(_widgets); i++)
-		free(_widgets[i].text);
-}
-
-void MacIndy3Gui::initWidget(int n, int verbid, int x, int y, int width, int height) {
-	Widget *w = &_widgets[n];
-
-	w->verbid = verbid;
-	w->bounds.left = x;
-	w->bounds.top = y;
-	w->bounds.right = x + width;
-	w->bounds.bottom = y + height;
-}
-
-void MacIndy3Gui::resetWidget(Widget *w) {
-	free(w->text);
+	// Scrollbar arrow buttons
+	x = w->_bounds.left + 137;
+	y = w->_bounds.top + 4;
 
-	w->verbslot = -1;
-	w->text = nullptr;
-	w->timer = 0;
-	w->visible = false;
-	w->enabled = false;
-	w->redraw = false;
-	w->kill = false;
-}
+	_inventoryScrollButtons[0] = new InventoryScrollButton(x, w->_bounds.top + 4, 16, 16, kScrollUp);
+	_inventoryScrollButtons[1] = new InventoryScrollButton(x, w->_bounds.bottom - 20, 16, 16, kScrollDown);
 
-void MacIndy3Gui::initInventorySlot(int n, int x, int y, int width, int height) {
-	InventorySlot *s = &_inventorySlots[n];
-
-	s->slot = n;
-	s->bounds.left = x;
-	s->bounds.top = y;
-	s->bounds.right = x + width;
-	s->bounds.bottom = y + height;
+	for (int i = 0; i < ARRAYSIZE(_inventoryScrollButtons); i++) {
+		_inventoryScrollButtons[i]->setGui(this);
+		_inventoryScrollButtons[i]->setSurface(_vm->_macScreen);
+	}
+#endif
 }
 
-void MacIndy3Gui::resetInventorySlot(InventorySlot *s) {
-	free(s->name);
-	s->name = nullptr;
-	s->obj = -1;
-	s->timer = 0;
-	s->redraw = false;
+MacIndy3Gui::~MacIndy3Gui() {
+	for (int i = 0; i < ARRAYSIZE(_widgets); i++)
+		delete _widgets[i];
+#if 0
+	for (int i = 0; i < ARRAYSIZE(_inventorySlots); i++)
+		delete _inventorySlots[i];
+	for (int i = 0; i < ARRAYSIZE(_inventoryScrollButtons); i++)
+		delete _inventoryScrollButtons[i];
+#endif
 }
 
 bool MacIndy3Gui::isActive() {
-	if (_vm->_virtscr[kVerbVirtScreen].topline != 144)
+	// The GUI is only allowed if the verb area has the expected size. There
+	// are scenes (e.g. the flight path to Venice) where it's not.
+
+	VirtScreen *vs = &_vm->_virtscr[kVerbVirtScreen];
+	if (vs->topline != 144 || vs->h != 56)
 		return false;
 
-	// Known verb scripts
+	// The GUI is only allowed for certain verb scripts:
 	//
 	//   4 - Regular verb GUI
 	//  18 - Conversations
@@ -501,11 +794,17 @@ bool MacIndy3Gui::isActive() {
 	// 201 - Travel to Henry's or Venice
 	// 205 - Travel from Venice
 	//
+	// Other known verb scripts where the GUI is not allowed:
+	//
 	//  19 - Boxing
 
 	int verbScript = _vm->VAR(_vm->VAR_VERB_SCRIPT);
-	if (verbScript == 4 || verbScript == 18 || verbScript == 200 || verbScript == 201 || verbScript == 205)
-		return true;
+	int guiVerbScripts[] = { 4, 18, 200, 201, 205 };
+
+	for (int i = 0; i < ARRAYSIZE(guiVerbScripts); i++) {
+		if (verbScript == guiVerbScripts[i])
+			return true;
+	}
 
 	return false;
 }
@@ -514,10 +813,12 @@ void MacIndy3Gui::resetAfterLoad() {
 	_visible = false;
 
 	for (int i = 0; i < ARRAYSIZE(_widgets); i++)
-		resetWidget(&_widgets[i]);
+		_widgets[i]->reset();
 
+#if 0
 	for (int i = 0; i < ARRAYSIZE(_inventorySlots); i++)
-		resetInventorySlot(&_inventorySlots[i]);
+		_inventorySlots[i]->reset();
+#endif
 
 	// In the DOS version, verb ID 102-106 were used for the visible
 	// inventory items, and 107-108 for inventory arrow buttons. In the
@@ -540,7 +841,9 @@ void MacIndy3Gui::resetAfterLoad() {
 	if (oldSavegame)
 		_vm->VAR(67) /= 2;
 
+#if 0
 	_inventoryOffset = _vm->VAR(67);
+#endif
 }
 
 void MacIndy3Gui::update() {
@@ -552,60 +855,95 @@ void MacIndy3Gui::update() {
 	}
 
 	if (!isActive()) {
-		if (_visible)
+		if (isVisible())
 			hide();
 		return;
 	}
 
-	updateTimers();
+	for (int i = 0; i < ARRAYSIZE(_widgets); i++) {
+		_widgets[i]->updateTimer();
+		_widgets[i]->_kill = true;
+	}
 
 	bool keepGuiAlive = false;
-	bool inventoryIsActive = false;
+	bool hasInventory = false;
 
-	updateButtons(keepGuiAlive, inventoryIsActive);
+	updateWidgets(keepGuiAlive, hasInventory);
 
 	if (!keepGuiAlive) {
 		hide();
 		return;
 	}
 
-	if (!_visible)
+	if (!isVisible())
 		show();
 
+	for (int i = 0; i < ARRAYSIZE(_widgets); i++) {
+		GuiWidget *w = _widgets[i];
+		if (w->_kill && w->_visible) {
+			w->undraw();
+			w->reset();
+		}
+	}
+
+	for (int i = 0; i < ARRAYSIZE(_widgets); i++) {
+		GuiWidget *w = _widgets[i];
+		if (w->_verbslot != -1 && w->_redraw) {
+			debug("Drawing button %d: (%d) %s", i, w->_verbid, w->name());
+			w->draw();
+		}
+	}
+
+#if 0
 	drawButtons();
+#endif
 
-	if (inventoryIsActive) {
+#if 0
+	if (hasInventory) {
 		updateInventorySlots();
 		drawInventorySlots();
+		fillInventoryArrows();
 	}
+#endif
 }
 
+#if 0
 void MacIndy3Gui::updateTimers() {
 	for (int i = 0; i < ARRAYSIZE(_widgets); i++) {
-		Widget *w = &_widgets[i];
-		if (w->timer > 0) {
-			w->timer--;
-			if (w->timer == 0 && w->visible) {
-				_vm->runInputScript(kVerbClickArea, w->verbid, 1);
-				w->redraw = true;
+		Widget *w = _widgets[i];
+		if (w->_timer > 0) {
+			w->_timer--;
+			if (w->_timer == 0 && w->_visible) {
+				_vm->runInputScript(kVerbClickArea, w->_verbid, 1);
+				w->_redraw = true;
 			}
 		}
-		w->kill = true;
+		w->_kill = true;
 	}
 
 	for (int i = 0; i < ARRAYSIZE(_inventorySlots); i++) {
-		InventorySlot *s = &_inventorySlots[i];
-		if (s->timer > 0) {
-			s->timer--;
-			if (s->timer == 0) {
-				s->redraw = true;
-				_vm->runInputScript(kInventoryClickArea, s->obj, 1);
+		InventorySlot *s = _inventorySlots[i];
+		if (s->_timer > 0) {
+			s->_timer--;
+			if (s->_timer == 0) {
+				s->_redraw = true;
+				_vm->runInputScript(kInventoryClickArea, s->_obj, 1);
 			}
 		}
 	}
+
+	for (int i = 0; i < ARRAYSIZE(_inventoryScrollButtons); i++) {
+		InventoryScrollButton *b = _inventoryScrollButtons[i];
+		if (b->_timer > 0) {
+			b->_timer--;
+			if (b->_timer == 0)
+				b->_redraw = true;
+		}
+	}
 }
+#endif
 
-void MacIndy3Gui::updateButtons(bool &keepGuiAlive, bool &inventoryIsActive) {
+void MacIndy3Gui::updateWidgets(bool &keepGuiAlive, bool &hasInventory) {
 	// Collect all active verbs. Verb slot 0 is special, apparently, so we
 	// don't look at that one.
 
@@ -625,7 +963,7 @@ void MacIndy3Gui::updateButtons(bool &keepGuiAlive, bool &inventoryIsActive) {
 				id = 20;
 			} else if (vs->verbid == 101) {
 				id = 21;
-				inventoryIsActive = true;
+				hasInventory = true;
 			} else if (vs->verbid >= 119 && vs->verbid <= 125) {
 				id = vs->verbid - 97;
 			} else {
@@ -636,35 +974,14 @@ void MacIndy3Gui::updateButtons(bool &keepGuiAlive, bool &inventoryIsActive) {
 			}
 
 			if (id != -1) {
-				Widget *w = &_widgets[id];
-				bool enabled = (vs->curmode == 1);
-
-				assert(w->verbid == vs->verbid);
-
-				if (w->enabled != enabled)
-					w->redraw = true;
-				w->verbslot = i;
-				w->verbid = vs->verbid;
-				w->enabled = enabled;
-				w->kill = false;
-
-				const byte *ptr = _vm->getResourceAddress(rtVerb, w->verbslot);
-				byte buf[270];
-
-				_vm->convertMessageToString(ptr, buf, sizeof(buf));
-				if (w->text == nullptr || strcmp((char *)w->text, (char *)buf) != 0) {
-					free(w->text);
-					w->text = (byte *)scumm_strdup((const char *)buf);
-					w->timer = 0;
-					w->redraw = true;
-				}
-
+				_widgets[id]->updateVerb(i);
 				keepGuiAlive = true;
 			}
 		}
 	}
 }
 
+#if 0
 void MacIndy3Gui::updateInventorySlots() {
 	int owner = _vm->VAR(_vm->VAR_EGO);
 
@@ -692,126 +1009,145 @@ void MacIndy3Gui::updateInventorySlots() {
 		int obj = _vm->_inventory[i];
 		if (obj && _vm->getOwner(obj) == owner) {
 			if (++invCount >= invOffset) {
-				InventorySlot *s = &_inventorySlots[invSlot];
+				InventorySlot *s = _inventorySlots[invSlot];
 				const byte *name = _vm->getObjOrActorName(obj);
 
 				if (name) {
-					if (!s->name || strcmp((char *)s->name, (const char *)name) != 0) {
-						free(s->name);
-						s->name = (byte *)scumm_strdup((const char *)name);
-						s->timer = 0;
-						s->redraw = true;
+					if (!s->_name || strcmp((char *)s->_name, (const char *)name) != 0) {
+						free(s->_name);
+						s->_name = (byte *)scumm_strdup((const char *)name);
+						s->_timer = 0;
+						s->_redraw = true;
 					}
 				} else {
-					if (s->name) {
-						free(s->name);
-						s->name = nullptr;
-						s->timer = 0;
-						s->redraw = true;
+					if (s->_name) {
+						free(s->_name);
+						s->_name = nullptr;
+						s->_timer = 0;
+						s->_redraw = true;
 					}
 				}
 
-				s->obj = obj;
+				s->_obj = obj;
 				invSlot++;
 			}
 		}
 	}
 
 	for (int i = invSlot; i < ARRAYSIZE(_inventorySlots); i++) {
-		InventorySlot *s = &_inventorySlots[i];
-
-		if (s->name) {
-			free(s->name);
-			s->name = nullptr;
-			s->obj = -1;
-			s->timer = 0;
-			s->redraw = true;
+		InventorySlot *s = _inventorySlots[i];
+
+		if (s->_name) {
+			free(s->_name);
+			s->_name = nullptr;
+			s->_obj = -1;
+			s->_timer = 0;
+			s->_redraw = true;
 		}
 	}
 }
 
+#if 0
 void MacIndy3Gui::drawButtons() {
 	// Erase all inactive buttons. Since active buttons may overlap ones
 	// that are now inactive, we have to do this first.
 
 	for (int i = 0; i < ARRAYSIZE(_widgets); i++) {
-		Widget *w = &_widgets[i];
+		Widget *w = _widgets[i];
 
-		if (w->kill && w->visible) {
-			undrawWidget(w);
-			resetWidget(w);
+		if (w->_kill && w->_visible) {
+			w->undraw();
+			w->reset();
 		}
 	}
 
 	// Draw all active buttons
 
 	for (int i = 0; i < ARRAYSIZE(_widgets); i++) {
-		Widget *w = &_widgets[i];
+		Widget *w = _widgets[i];
 
-		if (w->verbslot != -1 && w->redraw) {
-			debug("Drawing button %d: (%d) %s", i, w->verbid, w->text);
-			drawWidget(w);
+		if (w->_verbslot != -1 && w->_redraw) {
+			debug("Drawing button %d: (%d) %s", i, w->_verbid, w->name());
+			w->draw();
 		}
 	}
 }
+#endif
 
 void MacIndy3Gui::drawInventorySlots() {
 	// Since the inventory slots overlap, draw the highlighted ones (and there
 	// should really only be one at a time) first.
 
 	for (int i = 0; i < ARRAYSIZE(_inventorySlots); i++) {
-		InventorySlot *s = &_inventorySlots[i];
+		InventorySlot *s = _inventorySlots[i];
 
-		if (s->redraw && s->timer == 0)
+		if (s->_redraw && s->_timer == 0)
 			drawInventorySlot(s);
 	}
 
 	for (int i = 0; i < ARRAYSIZE(_inventorySlots); i++) {
-		InventorySlot *s = &_inventorySlots[i];
+		InventorySlot *s = _inventorySlots[i];
 
-		if (s->redraw)
+		if (s->_redraw)
 			drawInventorySlot(s);
 	}
 }
 
-void MacIndy3Gui::handleEvent(Common::Event &event) {
-	if (event.type != Common::EVENT_LBUTTONDOWN)
-		return;
+void MacIndy3Gui::fillInventoryArrows() {
+	for (int i = 0; i < ARRAYSIZE(_inventoryScrollButtons); i++) {
+		InventoryScrollButton *b = _inventoryScrollButtons[i];
 
-	if (!isActive() || _vm->_userPut <= 0)
-		return;
+		if (b->_redraw)
+			fillInventoryArrow(b);
+	}
+}
+#endif
 
-	int x = event.mouse.x;
-	int y = event.mouse.y;
+void MacIndy3Gui::handleEvent(Common::Event &event) {
+	if (!isVisible() || _vm->_userPut <= 0)
+		return;
 
 	for (int i = 0; i < ARRAYSIZE(_widgets); i++) {
-		Widget *w = &_widgets[i];
+		if (_widgets[i]->handleEvent(event))
+			break;
+	}
 
-		if (w->verbid && w->enabled && w->bounds.contains(x, y)) {
-			if (w->verbid != 101) {
+#if 0
+			if (w->_verbid != 101) {
 				// The input script is invoked when the button animation
 				// times out. Speed-runners will probably hate this, but
 				// I think it looks better this way.
-				w->redraw = true;
-				w->timer = BUTTON_TIMER;
+				w->_redraw = true;
+				w->_timer = BUTTON_TIMER;
 				return;
 			} else {
 				for (int j = 0; j < ARRAYSIZE(_inventorySlots); j++) {
-					InventorySlot *s = &_inventorySlots[j];
+					InventorySlot *s = _inventorySlots[j];
+
+					if (s->_obj != -1 && s->_bounds.contains(x, y)) {
+						s->_redraw = true;
+						s->_timer = INVENTORY_SLOT_TIMER;
+						return;
+					}
+				}
 
-					if (s->obj != -1 && s->bounds.contains(x, y)) {
-						s->redraw = true;
-						s->timer = INVENTORY_SLOT_TIMER;
+				for (int j = 0; j < ARRAYSIZE(_inventoryScrollButtons); j++) {
+					InventoryScrollButton *b = _inventoryScrollButtons[j];
+
+					if (b->_enabled && b->_bounds.contains(x, y)) {
+						b->_redraw = true;
+						b->_timer = BUTTON_TIMER;
 						return;
 					}
 				}
 			}
 		}
 	}
+#endif
 }
 
 void MacIndy3Gui::show() {
-	if (_visible || !isActive())
+	if (isVisible())
 		return;
 
 	debug("SHOW");
@@ -821,13 +1157,13 @@ void MacIndy3Gui::show() {
 }
 
 void MacIndy3Gui::hide() {
-	if (!_visible)
+	if (!isVisible())
 		return;
 
 	_visible = false;
 
 	for (int i = 0; i < ARRAYSIZE(_widgets); i++)
-		resetWidget(&_widgets[i]);
+		_widgets[i]->reset();
 
 	if (_vm->_virtscr[kVerbVirtScreen].topline != 144)
 		return;
@@ -840,209 +1176,55 @@ void MacIndy3Gui::hide() {
 
 void MacIndy3Gui::clear() {
 	_macScreen->fillRect(Common::Rect(0, 288, 640, 289), 0);
-	fill(Common::Rect(0, 290, 640, 373));
 	_macScreen->fillRect(Common::Rect(0, 373, 640, 400), 0);
 
-	const byte corner[] = {
-		1, 1, 1, 1,
-		1, 1, 0, 0,
-		1, 0, 0, 0,
-		1, 0, 0, 0
-	};
-
-	byte *ul = (byte *)_macScreen->getBasePtr(0, 290);
-	byte *ur = (byte *)_macScreen->getBasePtr(639, 290);
-	byte *ll = (byte *)_macScreen->getBasePtr(0, 372);
-	byte *lr = (byte *)_macScreen->getBasePtr(639, 372);
-
-	int pitch = _macScreen->pitch;
-
-	for (int y = 0; y < 4; y++) {
-		for (int x = 0; x < 4; x++) {
-			if (corner[y * 4 + x]) {
-				*(ul + y * pitch + x) = 0;
-				*(ur + y * pitch - x) = 0;
-				*(ll - y * pitch + x) = 0;
-				*(lr - y * pitch - x) = 0;
-			}
-		}
-	}
+	fill(Common::Rect(0, 290, 640, 373));
+	drawBitmap(Common::Rect(  0, 290,   4, 294), _ulCorner, 0);
+	drawBitmap(Common::Rect(636, 290, 640, 294), _urCorner, 0);
+	drawBitmap(Common::Rect(  0, 369,   4, 373), _llCorner, 0);
+	drawBitmap(Common::Rect(636, 369, 640, 373), _lrCorner, 0);
 
 	copyRectToScreen(Common::Rect(0, 288, 640, 400));
 }
 
-void MacIndy3Gui::drawWidget(Widget *w) {
-	// Clear the area used by the widget
-	fill(w->bounds);
-
-	switch (w->verbid) {
-	case 101:
-		drawInventoryWidget(w);
-		break;
-	default:
-		drawButton(w);
-		break;
-	}
-
-	w->visible = true;
-	w->redraw = false;
-
-	copyRectToScreen(w->bounds);
-}
-
-void MacIndy3Gui::undrawWidget(Widget *w) {
-	fill(w->bounds);
-	copyRectToScreen(w->bounds);
-}
-
-void MacIndy3Gui::drawButton(Widget *w) {
-	Common::Rect r = w->bounds;
+#if 0
+void MacIndy3Gui::drawInventoryScrollButton(InventoryScrollButton *b) {
+	Common::Rect r = b->_bounds;
+	uint16 *outline;
+	uint16 *arrow;
 
-	if (w->timer == 0) {
-		drawShadowBox(r);
+	if (b->_direction == kScrollUp) {
+		outline = _upArrowOutline;
+		arrow = _upArrow;
 	} else {
-		// I have only been able to capture a screenshot of the pressed
-		// button in black and white, where the checkerboard background
-		// makes it hard to see exactly which pixels should be drawn.
-		// Basilisk II runs it too fast, and I haven't gotten Mini vMac
-		// to run it in 16-color mode.
-		//
-		// All I can say for certain is that the upper left corner is
-		// rounded while the lower right is not. I'm going to assume
-		// that the shadow is always drawn, and the rest of the button
-		// is just shifted down to the right. That would make the other
-		// two corners rounded.
-		_macScreen->hLine(r.left + 2, r.top + 1, r.right - 2, 0);
-		_macScreen->hLine(r.left + 2, r.bottom - 1, r.right - 1, 0);
-		_macScreen->vLine(r.left + 1, r.top + 2, r.bottom - 2, 0);
-		_macScreen->vLine(r.right - 1, r.top + 2, r.bottom - 2, 0);
-
-		_macScreen->hLine(r.left + 2, r.top + 2, r.right - 2, 15);
-		_macScreen->vLine(r.left + 2, r.top + 3, r.bottom - 2, 15);
-	}
-
-	// The text is drawn centered. Based on experimentation, I think the
-	// width is always based on the outlined font, and the button shadow is
-	// not counted as part of the button width.
-	//
-	// This gives us pixel perfect rendering for the English verbs.
-
-	if (w->text) {
-		int stringWidth = 0;
-		for (int i = 0; w->text[i]; i++)
-			stringWidth += _fonts[2]->getCharWidth(w->text[i]);
-
-		int x = (r.left + (r.width() - 1 - stringWidth) / 2) - 1;
-		int y = r.top + 2;
-		int color = w->enabled ? 15 : 0;
-
-		if (w->timer) {
-			x++;
-			y++;
-		}
-
-		for (int i = 0; w->text[i]; i++) {
-			if (w->enabled)
-				_fonts[2]->drawChar(_macScreen, w->text[i], x, y, 0);
-			_fonts[1]->drawChar(_macScreen, w->text[i], x + 1, y, color);
-			x += _fonts[2]->getCharWidth(w->text[i]);
-		}
+		outline = _downArrowOutline;
+		arrow = _downArrow;
 	}
-}
-
-// Desired behavior:
-//
-// The drag handle of the scrollbar never changes size. It's only drawn when
-// there are enough inventory items to scroll.
-//
-// The size of the scroll handle is fixed, not scaled to indicate the number of
-// objects in your inventory.
-//
-// The exact positions of the scroll handle are not yet known. Probably just a
-// simple calculation.
-//
-// Clicking on an arrow scrolls up or down by one row. Clicking and holding
-// scrolls the inventory. The time between scrolling is constant, i.e. the
-// first delay is not different. The delay seems to depend on the speed of the
-// Mac, so pick something that feels right.
-//
-// Clicking above or below the handle scrolls up or down by - probably - one
-// page. I've never seen the inventory full enough for this to mean anything
-// else than scrolling to the top or bottom.
-//
-// Dragging the handle is not possible. Clicking on the handle is probably
-// indistinguishable from clicking above or below.
-
-void MacIndy3Gui::drawInventoryWidget(Widget *w) {
-	for (int i = 0; i < ARRAYSIZE(_inventorySlots); i++)
-		resetInventorySlot(&_inventorySlots[i]);
-
-	Common::Rect r = w->bounds;
-
-	drawShadowBox(r);
-	drawShadowFrame(Common::Rect(r.left + 4, r.top + 4, r.right - 22, r.bottom - 4), 0, 15);
-
-	// Scrollbar
 
-	drawShadowFrame(Common::Rect(r.right - 20, r.top + 4, r.right - 4, r.top + 20), 15, 255);
-	drawShadowFrame(Common::Rect(r.right - 20, r.top + 19, r.right - 4, r.bottom - 19), 0, 255);
-	drawShadowFrame(Common::Rect(r.right - 20, r.bottom - 20, r.right - 4, r.bottom - 4), 15, 255);
+	// Don't use fillInventoryArrow() here, because it will needlessly copy
+	// the arrow to the screen. We assume the arrow is always white.
 
-	drawInventoryArrow(r.right - 17, r.top + 7, false, false);
-	drawInventoryArrow(r.right - 17, r.bottom - 16, false, true);
+	drawShadowFrame(r, 15, 255);
+	drawBitmap(r, outline, 0);
+	drawBitmap(r, arrow, 15);
 }
 
-void MacIndy3Gui::drawInventoryArrow(int arrowX, int arrowY, bool highlighted, bool flipped) {
-	const byte arrow[110] = {
-		0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
-		0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0,
-		0, 0, 0, 1, 2, 2, 2, 1, 0, 0, 0,
-		0, 0, 1, 2, 2, 2, 2, 2, 1, 0, 0,
-		0, 1, 2, 2, 2, 2, 2, 2, 2, 1, 0,
-		1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1,
-		0, 0, 1, 2, 2, 2, 2, 2, 1, 0, 0,
-		0, 0, 1, 2, 2, 2, 2, 2, 1, 0, 0,
-		0, 0, 1, 2, 2, 2, 2, 2, 1, 0, 0,
-		0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0
-	};
-
-	byte palette[] = { 0, 15 };
-
-	if (highlighted)
-		palette[1] = 0;
-
-	int y0, y1, yd;
-
-	if (flipped) {
-		y0 = 9;
-		y1 = 0;
-		yd = -1;
-	} else {
-		y0 = 0;
-		y1 = 9;
-		yd = 1;
-	}
-
-	byte *ptr = (byte *)_macScreen->getBasePtr(arrowX, arrowY);
-	int pitch = _macScreen->pitch;
+void MacIndy3Gui::fillInventoryArrow(InventoryScrollButton *b) {
+	Common::Rect r = b->_bounds;
+	uint16 *bitmap = b->_direction == kScrollUp ? _upArrow : _downArrow;
+	byte color = b->_timer ? 0 : 15;
 
-	int y = y0 - yd;
-	do {
-		y += yd;
-		for (int x = 0; x < 11; x++) {
-			byte color = arrow[11 * y + x];
-			if (color)
-				ptr[x] = palette[color - 1];
-		}
-		ptr += pitch;
-	} while (y != y1);
+	drawBitmap(r, bitmap, color);
+	b->_redraw = false;
+	copyRectToScreen(r);
 }
 
 void MacIndy3Gui::drawInventorySlot(InventorySlot *s) {
-	debug("Drawing inventory slot: [%d] %s", s->slot, s->name);
+	debug("Drawing inventory slot: [%d] %s", s->_slot, s->_name);
+	Common::Rect r = s->_bounds;
 	int fg, bg;
 
-	if (s->timer) {
+	if (s->_timer) {
 		fg = 15;
 		bg = 0;
 	} else {
@@ -1050,75 +1232,62 @@ void MacIndy3Gui::drawInventorySlot(InventorySlot *s) {
 		bg = 15;
 	}
 
-	_macScreen->fillRect(s->bounds, bg);
+	_macScreen->fillRect(r, bg);
 
-	if (s->name) {
-		int y = s->bounds.top - 1;
-		int x = s->bounds.left + 4;
+	if (s->_name) {
+		int y = _bounds.top - 1;
+		int x = _bounds.left + 4;
 
-		for (int i = 0; s->name[i]; i++) {
-			if (s->name[i] != '@') {
-				_fonts[0]->drawChar(_macScreen, s->name[i], x, y, fg);
-				x += _fonts[0]->getCharWidth(s->name[i]);
+		for (int i = 0; s->_name[i]; i++) {
+			if (s->_name[i] != '@') {
+				_fonts[0]->drawChar(_macScreen, s->_name[i], x, y, fg);
+				x += _fonts[0]->getCharWidth(s->_name[i]);
 			}
 		}
 	}
 
-	s->redraw = false;
-	copyRectToScreen(s->bounds);
+	s->_redraw = false;
+	copyRectToScreen(r);
+}
+#endif
+
+void MacIndy3Gui::copyRectToScreen(Common::Rect r) {
+	_system->copyRectToScreen(_macScreen->getBasePtr(r.left, r.top), _macScreen->pitch, r.left, r.top, r.width(), r.height());
 }
 
 void MacIndy3Gui::fill(Common::Rect r) {
 	int pitch = _macScreen->pitch;
 
+	// Fill the screen with either gray of a checkerboard pattern.
+
 	if (_vm->_renderMode == Common::kRenderMacintoshBW) {
 		byte *row = (byte *)_macScreen->getBasePtr(r.left, r.top);
 
 		for (int y = r.top; y < r.bottom; y++) {
 			byte *ptr = row;
-			for (int x = r.left; x < r.right; x++) {
+			for (int x = r.left; x < r.right; x++)
 				*ptr++ = ((x + y) & 1) ? 15 : 0;
-			}
 			row += pitch;
 		}
 	} else
 		_macScreen->fillRect(r, 7);
 }
 
-void MacIndy3Gui::drawShadowBox(Common::Rect r) {
-	_macScreen->hLine(r.left + 1, r.top, r.right - 3, 0);
-	_macScreen->hLine(r.left + 1, r.bottom - 2, r.right - 3, 0);
-	_macScreen->vLine(r.left, r.top + 1, r.bottom - 3, 0);
-	_macScreen->vLine(r.right - 2, r.top + 1, r.bottom - 3, 0);
-
-	_macScreen->hLine(r.left + 2, r.bottom - 1, r.right - 1, 0);
-	_macScreen->vLine(r.right - 1, r.top + 2, r.bottom - 2, 0);
-
-	_macScreen->hLine(r.left + 1, r.top + 1, r.right - 3, 15);
-	_macScreen->vLine(r.left + 1, r.top + 2, r.bottom - 3, 15);
-}
-
-void MacIndy3Gui::drawShadowFrame(Common::Rect r, byte shadowColor, byte fillColor) {
-	_macScreen->hLine(r.left, r.top, r.right - 1, 0);
-	_macScreen->hLine(r.left, r.bottom - 1, r.right - 1, 0);
-	_macScreen->vLine(r.left, r.top + 1, r.bottom - 2, 0);
-	_macScreen->vLine(r.right - 1, r.top + 1, r.bottom - 2, 0);
-
-	_macScreen->hLine(r.left + 1, r.top + 1, r.right - 2, shadowColor);
-	_macScreen->vLine(r.left + 1, r.top + 2, r.bottom - 2, shadowColor);
+void MacIndy3Gui::drawBitmap(Common::Rect r, const uint16 *bitmap, byte color) {
+	byte *ptr = (byte *)_macScreen->getBasePtr(r.left, r.top);
+	int pitch = _macScreen->pitch;
 
-	if (fillColor != 255) {
-		Common::Rect fillRect(r.left + 2, r.top + 2, r.right - 1, r.bottom - 1);
+	assert(r.width() <= 16);
 
-		if (fillColor == 0)
-			fill(fillRect);
-		else
-			_macScreen->fillRect(fillRect, fillColor);
+	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;
 	}
 }
 
-void MacIndy3Gui::copyRectToScreen(Common::Rect r) {
-	_system->copyRectToScreen(_macScreen->getBasePtr(r.left, r.top), _macScreen->pitch, r.left, r.top, r.width(), r.height());
-}
-
 } // End of namespace Scumm
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 378a274341d..ec977d12e37 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -38,11 +38,22 @@ public:
 	MacIndy3Gui(OSystem *system, ScummEngine *vm);
 	~MacIndy3Gui();
 
-	bool isActive();
-	void resetAfterLoad();
+	// There is a distinction between the GUI being active and being
+	// visible. Active means that it's allowed to draw verbs. Visible
+	// means that it's actually drawn verbs. From the outside, only the
+	// visibility is relevant.
+	//
+	// One case where this makes a difference is when boxing with the
+	// coach. During the "10 minutes later" sign, the GUI is active but
+	// it's not drawing verbs, so the SCUMM engine is allowed to draw in
+	// the verb area to clear the power meters and text.
 
-	void update();
+	bool isVisible() { return _visible; }
+
+	const Graphics::Font *getFont(int n) { return _fonts[n]; }
 
+	void resetAfterLoad();
+	void update();
 	void handleEvent(Common::Event &event);
 
 private:
@@ -53,61 +64,179 @@ private:
 
 	bool _visible = false;
 
-	struct Widget {
-		Common::Rect bounds;
-		int verbid = 0;
-		int verbslot = -1;
-		byte *text = nullptr;
-		int timer = 0;
-		bool visible = false;
-		bool enabled = false;
-		bool redraw = false;
-		bool kill = false;
+	class Widget {
+	public:
+		static ScummEngine *_vm;
+		static MacIndy3Gui *_gui;
+		static Graphics::Surface *_surface;
+
+		Common::Rect _bounds;
+		int _timer = 0;
+		bool _enabled = false;
+		bool _redraw = false;
+
+		Widget(int x, int y, int width, int height);
+		virtual ~Widget() {}
+
+		void reset();
+
+		virtual void draw() = 0;
+		virtual void undraw() {}
+
+		virtual bool updateTimer();
+
+		// Primitives
+		void copyRectToScreen(Common::Rect r);
+		void fill(Common::Rect r);
+		void drawBitmap(Common::Rect r, const uint16 *bitmap, byte color);
+		void drawShadowBox(Common::Rect r);
+		void drawShadowFrame(Common::Rect r, byte shadowColor, byte fillColor);
 	};
 
-	Widget _widgets[29];
+	class GuiWidget : public Widget {
+	public:
+		int _verbid = 0;
+		int _verbslot = -1;
+		bool _visible = false;
+		bool _kill = false;
+
+		GuiWidget(int verbid, int x, int y, int width, int height);
+
+		virtual const char *name() = 0;
 
-	struct InventorySlot {
-		Common::Rect bounds;
-		byte *name = nullptr;
-		int slot = -1;
-		int obj = -1;
-		int timer = 0;
-		bool redraw = false;
+		void reset();
+
+		virtual bool handleEvent(Common::Event &event) = 0;
+		virtual void updateVerb(int verbslot);
+
+		void draw();
+		void undraw();
 	};
 
-	InventorySlot _inventorySlots[6];
-	int _inventoryOffset = 0;
+	class Button : public GuiWidget {
+	public:
+		byte *_text = nullptr;
+
+		Button(int verbid, int x, int y, int width, int height);
+		~Button();
+
+		const char *name() { return _text ? (const char *)_text : "(null)"; }
+		bool handleEvent(Common::Event &event);
+
+		void reset();
+		bool updateTimer();
+		void updateVerb(int verbslot);
+		void draw();
+	};
+
+	enum ScrollDirection {
+		kScrollUp,
+		kScrollDown
+	};
+
+	class InventoryScrollButton : public Widget {
+	private:
+		ScrollDirection _direction;
+
+	public:
+		InventoryScrollButton(int x, int y, int width, int height, ScrollDirection direction);
+
+		void draw();
+	};
+
+	class InventorySlot : public Widget {
+	private:
+		byte *_name = nullptr;
+		int _slot = -1;
+		int _obj = -1;
+
+	public:
+		InventorySlot(int slot, int x, int y, int width, int height);
+		void reset();
+		bool updateTimer();
+		void draw();
+	};
+
+	class InventoryWidget : public GuiWidget {
+	private:
+		InventorySlot *_slots[6];
+		InventoryScrollButton *_scrollButtons[2];
+
+	public:
+		InventoryWidget(int verbid, int x, int y, int width, int height);
+
+		const char *name() { return "Inventory"; }
+
+		bool handleEvent(Common::Event &event);
+
+		void draw();
+	};
+
+	GuiWidget *_widgets[29];
+
+	const uint16 _ulCorner[4] = { 0xF000, 0xC000, 0x8000, 0x8000 };
+	const uint16 _urCorner[4] = { 0xF000, 0x3000, 0x1000, 0x1000 };
+	const uint16 _llCorner[4] = { 0x8000, 0x8000, 0xC000, 0xF000 };
+	const uint16 _lrCorner[5] = { 0x1000, 0x1000, 0x3000, 0xF000 };
+
+	const uint16 _upArrowOutline[16] = {
+		0x0000, 0x0000, 0x0000, 0x0080,	0x0140, 0x0220, 0x0410, 0x0808,
+		0x1C1C, 0x0410, 0x0410, 0x0410, 0x07F0, 0x0000, 0x0000, 0x0000
+	};
+
+	uint16 _upArrow[16] = {
+		0x0000, 0x0000, 0x0000, 0x0000, 0x0080, 0x01C0, 0x03E0, 0x07F0,
+		0x03E0, 0x03E0, 0x03E0, 0x03E0, 0x0000, 0x0000, 0x0000, 0x0000
+	};
+
+	uint16 _downArrowOutline[16] = {
+		0x0000, 0x0000, 0x0000, 0x0000, 0x07F0, 0x0410, 0x0410, 0x0410,
+		0x1C1C, 0x0808, 0x0410, 0x0220, 0x0140, 0x0080, 0x0000, 0x0000
+	};
+
+	uint16 _downArrow[16] = {
+		0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x03E0, 0x03E0, 0x03E0,
+		0x03E0, 0x07F0,	0x03E0, 0x01C0, 0x0080, 0x0000, 0x0000, 0x0000
+	};
+
+	bool isActive();
+
+#if 0
+	void initWidget(Widget *w, int x, int y, int width, int height);
+	void resetBaseWidget(Widget *w);
 
 	void initWidget(int n, int verbid, int x, int y, int width, int height);
 	void resetWidget(Widget *w);
 	void initInventorySlot(int n, int x, int y, int width, int height);
 	void resetInventorySlot(InventorySlot *s);
+	void initInventoryScrollButton(int n, int x, int y, int width, int height, ScrollDirection direction);
+	void resetInventoryScrollButton(InventoryScrollButton *b);
+#endif
 
 	void clear();
 	void show();
 	void hide();
 
-	void updateTimers();
-	void updateButtons(bool &keepGuiAlive, bool &inventoryIsActive);
+	void updateWidgets(bool &keepGuiAlive, bool &hasInventory);
+#if 0
 	void updateInventorySlots();
 
 	void drawButtons();
 	void drawInventorySlots();
+	void fillInventoryArrows();
+#endif
 
-	void drawWidget(Widget *w);
-	void undrawWidget(Widget *w);
+#if 0
 	void drawButton(Widget *w);
 	void drawInventoryWidget(Widget *w);
-	void drawInventoryArrow(int arrowX, int arrowY, bool highlighted, bool flipped);
 	void drawInventorySlot(InventorySlot *slot);
-
-	// Primitives
-	void fill(Common::Rect r);
-	void drawShadowBox(Common::Rect r);
-	void drawShadowFrame(Common::Rect r, byte shadowColor, byte fillColor);
+	void drawInventoryScrollButton(InventoryScrollButton *b);
+	void fillInventoryArrow(InventoryScrollButton *b);
+#endif
 
 	void copyRectToScreen(Common::Rect r);
+	void fill(Common::Rect r);
+	void drawBitmap(Common::Rect r, const uint16 *bitmap, byte color);
 };
 
 } // End of namespace Scumm
diff --git a/engines/scumm/verbs.cpp b/engines/scumm/verbs.cpp
index 6d4d397ff72..7761715d854 100644
--- a/engines/scumm/verbs.cpp
+++ b/engines/scumm/verbs.cpp
@@ -638,9 +638,6 @@ void ScummEngine::checkExecVerbs() {
 			// Verb was clicked
 			runInputScript(kVerbClickArea, _verbs[over].verbid, code);
 		} else {
-			if (zone->number != kMainVirtScreen && _macIndy3Gui && _macIndy3Gui->isActive())
-				return;
-
 			// Scene was clicked
 			runInputScript((zone->number == kMainVirtScreen) ? kSceneClickArea : kVerbClickArea, 0, code);
 		}
@@ -964,7 +961,7 @@ void ScummEngine::verbMouseOver(int verb) {
 }
 
 int ScummEngine::findVerbAtPos(int x, int y) const {
-	if (_macIndy3Gui && _macIndy3Gui->isActive())
+	if (_macIndy3Gui)
 		return 0;
 
 	if (!_numVerbs)
@@ -1082,7 +1079,7 @@ void ScummEngine::drawVerb(int verb, int mode) {
 	VerbSlot *vs;
 	bool tmp;
 
-	if (_macIndy3Gui && _macIndy3Gui->isActive())
+	if (_macIndy3Gui)
 		return;
 
 	if (!verb)
@@ -1161,7 +1158,7 @@ void ScummEngine::drawVerb(int verb, int mode) {
 }
 
 void ScummEngine::restoreVerbBG(int verb) {
-	if (_macIndy3Gui && _macIndy3Gui->isActive())
+	if (_macIndy3Gui)
 		return;
 
 	VerbSlot *vs;


Commit: ef6e3bf4322e1a191f4a6bcd717514fc1423cc72
    https://github.com/scummvm/scummvm/commit/ef6e3bf4322e1a191f4a6bcd717514fc1423cc72
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Indy 3 Mac GUI restructuring done. Cleanup next.

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 01fe70a05eb..4a301ed8ca9 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -378,9 +378,6 @@ Common::KeyState ScummEngine::mac_showOldStyleBannerAndPause(const char *msg, in
  * old savegames, but the variables are assumed to be harmless.
  */
 
-#define BUTTON_TIMER			15
-#define INVENTORY_SLOT_TIMER	7
-
 ScummEngine *MacIndy3Gui::Widget::_vm = nullptr;
 Graphics::Surface *MacIndy3Gui::Widget::_surface = nullptr;
 MacIndy3Gui *MacIndy3Gui::Widget::_gui = nullptr;
@@ -448,22 +445,18 @@ void MacIndy3Gui::Widget::drawShadowFrame(Common::Rect r, byte shadowColor, byte
 	}
 }
 
-MacIndy3Gui::GuiWidget::GuiWidget(int verbid, int x, int y, int width, int height) : MacIndy3Gui::Widget(x, y, width, height) {
-	_verbid = verbid;
-}
-
-void MacIndy3Gui::GuiWidget::reset() {
+void MacIndy3Gui::VerbWidget::reset() {
 	MacIndy3Gui::Widget::reset();
 	_verbslot = -1;
 	_visible = false;
 	_kill = false;
 }
 
-void MacIndy3Gui::GuiWidget::updateVerb(int verbslot) {
+void MacIndy3Gui::VerbWidget::updateVerb(int verbslot) {
 	VerbSlot *vs = &_vm->_verbs[verbslot];
 	bool enabled = (vs->curmode == 1);
 
-	if (_enabled != enabled)
+	if (!_visible || _enabled != enabled)
 		_redraw = true;
 
 	_verbslot = verbslot;
@@ -472,29 +465,21 @@ void MacIndy3Gui::GuiWidget::updateVerb(int verbslot) {
 	_kill = false;
 }
 
-void MacIndy3Gui::GuiWidget::draw() {
+void MacIndy3Gui::VerbWidget::draw() {
 	// Clear the area used by the widget
 	fill(_bounds);
 	_visible = true;
 	_redraw = false;
 }
 
-void MacIndy3Gui::GuiWidget::undraw() {
+void MacIndy3Gui::VerbWidget::undraw() {
 	fill(_bounds);
 	copyRectToScreen(_bounds);
 }
 
-MacIndy3Gui::Button::Button(int verbid, int x, int y, int width, int height) : MacIndy3Gui::GuiWidget(verbid, x, y, width, height) {
-}
-
-MacIndy3Gui::Button::~Button() {
-	free(_text);
-}
-
 void MacIndy3Gui::Button::reset() {
-	MacIndy3Gui::GuiWidget::reset();
-	free(_text);
-	_text = nullptr;
+	MacIndy3Gui::VerbWidget::reset();
+	_text.clear();
 }
 
 bool MacIndy3Gui::Button::handleEvent(Common::Event &event) {
@@ -514,7 +499,7 @@ bool MacIndy3Gui::Button::handleEvent(Common::Event &event) {
 }
 
 bool MacIndy3Gui::Button::updateTimer() {
-	bool ret = GuiWidget::updateTimer();
+	bool ret = VerbWidget::updateTimer();
 
 	if (ret) {
 		if (_visible) {
@@ -527,22 +512,26 @@ bool MacIndy3Gui::Button::updateTimer() {
 }
 
 void MacIndy3Gui::Button::updateVerb(int verbslot) {
-	MacIndy3Gui::GuiWidget::updateVerb(verbslot);
+	MacIndy3Gui::VerbWidget::updateVerb(verbslot);
 
 	const byte *ptr = _vm->getResourceAddress(rtVerb, verbslot);
 	byte buf[270];
 
 	_vm->convertMessageToString(ptr, buf, sizeof(buf));
-	if (!_text || strcmp((char *)_text, (char *)buf) != 0) {
-		free(_text);
-		_text = (byte *)scumm_strdup((const char *)buf);
+	if (_text != (char *)buf) {
+		_text = (char *)buf;
 		_timer = 0;
 		_redraw = true;
 	}
 }
 
 void MacIndy3Gui::Button::draw() {
-	MacIndy3Gui::GuiWidget::draw();
+	if (!_redraw)
+		return;
+
+	debug("Drawing button [%d] %s", _verbid, _text.c_str());
+
+	MacIndy3Gui::VerbWidget::draw();
 
 	if (_timer == 0) {
 		drawShadowBox(_bounds);
@@ -573,12 +562,12 @@ void MacIndy3Gui::Button::draw() {
 	//
 	// This gives us pixel perfect rendering for the English verbs.
 
-	if (_text) {
+	if (!_text.empty()) {
 		const Graphics::Font *boldFont = _gui->getFont(1);
 		const Graphics::Font *outlineFont = _gui->getFont(2);
 
 		int stringWidth = 0;
-		for (int i = 0; _text[i]; i++)
+		for (uint i = 0; i < _text.size(); i++)
 			stringWidth += outlineFont->getCharWidth(_text[i]);
 
 		int x = (_bounds.left + (_bounds.width() - 1 - stringWidth) / 2) - 1;
@@ -590,10 +579,12 @@ void MacIndy3Gui::Button::draw() {
 			y++;
 		}
 
-		for (int i = 0; _text[i]; i++) {
-			if (_enabled)
-				outlineFont->drawChar(_surface, _text[i], x, y, 0);
-			boldFont->drawChar(_surface, _text[i], x + 1, y, color);
+		for (uint i = 0; i < _text.size() && x < _bounds.right; i++) {
+			if (x >= _bounds.left) {
+				if (_enabled)
+					outlineFont->drawChar(_surface, _text[i], x, y, 0);
+				boldFont->drawChar(_surface, _text[i], x + 1, y, color);
+			}
 			x += boldFont->getCharWidth(_text[i]);
 		}
 	}
@@ -624,47 +615,218 @@ void MacIndy3Gui::Button::draw() {
 // Dragging the handle is not possible. Clicking on the handle is probably
 // indistinguishable from clicking above or below.
 
-MacIndy3Gui::InventoryWidget::InventoryWidget(int verbid, int x, int y, int width, int height) : MacIndy3Gui::GuiWidget(verbid, x, y, width, height) {
+const uint16 MacIndy3Gui::Inventory::_upArrow[16] = {
+	0x0000, 0x0000, 0x0000, 0x0080,	0x0140, 0x0220, 0x0410, 0x0808,
+	0x1C1C, 0x0410, 0x0410, 0x0410,	0x07F0, 0x0000, 0x0000, 0x0000
+};
+
+const uint16 MacIndy3Gui::Inventory::_downArrow[16] = {
+	0x0000, 0x0000, 0x0000, 0x0000,	0x07F0, 0x0410, 0x0410, 0x0410,
+	0x1C1C, 0x0808, 0x0410, 0x0220,	0x0140, 0x0080, 0x0000, 0x0000
+};
+
+MacIndy3Gui::Inventory::Inventory(int x, int y, int width, int height) : MacIndy3Gui::VerbWidget(x, y, width, height) {
+	x = _bounds.left + 6;
+	y = _bounds.top + 6;
+
+	// There are always six slots, no matter how many objects you are
+	// carrying.
+	//
+	// Each slot is 12 pixels tall (as seen when they are highlighted),
+	// which means they have to overlap slightly to fit. It is assumed
+	// that this will never interfere with the text drawing.
+
+	for (int i = 0; i < ARRAYSIZE(_slots); i++) {
+		_slots[i] = new Slot(i, x, y, 128, 12);
+		y += 11;
+	}
+
+	x = _bounds.right - 20;
+	y = _bounds.top + 4;
+
+	_scrollButtons[0] = new ScrollButton(x, _bounds.top + 4, 16, 16, kScrollUp);
+	_scrollButtons[1] = new ScrollButton(x, _bounds.bottom - 20, 16, 16, kScrollDown);
+}
+
+MacIndy3Gui::Inventory::~Inventory() {
+	for (int i = 0; i < ARRAYSIZE(_slots); i++)
+		delete _slots[i];
+
+	for (int i = 0; i < ARRAYSIZE(_scrollButtons); i++)
+		delete _scrollButtons[i];
+}
+
+void MacIndy3Gui::Inventory::updateVerb(int verbslot) {
+	MacIndy3Gui::VerbWidget::updateVerb(verbslot);
+
+	int owner = _vm->VAR(_vm->VAR_EGO);
+
+	int invCount = _vm->getInventoryCount(owner);
+	int invOffset = _vm->VAR(67);
+
+	// The scroll offset must be non-negative and if there are six or less
+	// items in the inventory, the inventory is fixed in the top position.
+
+	if (invOffset < 0 || invCount <= 6)
+		invOffset = 0;
+
+	// If there are more than six items in the inventory, clamp the scroll
+	// offset to be at most invCount - 6.
+
+	if (invCount > 6 && invOffset >= invCount - 6)
+		invOffset = invCount - 6;
+
+	_vm->VAR(67) = invOffset;
+
+	int invSlot = 0;
+	invCount = 0;
+
+	for (int i = 0; i < _vm->_numInventory && invSlot < ARRAYSIZE(_slots); i++) {
+		int obj = _vm->_inventory[i];
+		if (obj && _vm->getOwner(obj) == owner) {
+			if (++invCount >= invOffset) {
+				Slot *s = _slots[invSlot];
+				const byte *name = _vm->getObjOrActorName(obj);
+
+				if (name) {
+					if (s->_name != (const char *)name) {
+						s->_name = (const char *)name;
+						s->_timer = 0;
+						s->_redraw = true;
+					}
+				} else {
+					if (!s->_name.empty()) {
+						s->_name.clear();
+						s->_timer = 0;
+						s->_redraw = true;
+					}
+				}
+
+				s->_obj = obj;
+				invSlot++;
+			}
+		}
+	}
+
+	for (int i = invSlot; i < ARRAYSIZE(_slots); i++) {
+		Slot *s = _slots[i];
+
+		if (!s->_name.empty()) {
+			s->_name.clear();
+			s->_obj = -1;
+			s->_timer = 0;
+			s->_redraw = true;
+		}
+	}
 }
 
-bool MacIndy3Gui::InventoryWidget::handleEvent(Common::Event &event) {
+bool MacIndy3Gui::Inventory::handleEvent(Common::Event &event) {
+	if (event.type != Common::EVENT_LBUTTONDOWN)
+		return false;
+
+	for (int i = 0; i < ARRAYSIZE(_slots); i++) {
+		if (_slots[i]->handleEvent(event))
+			return true;
+	}
+
+	for (int i = 0; i < ARRAYSIZE(_scrollButtons); i++) {
+		if (_scrollButtons[i]->handleEvent(event))
+			return true;
+	}
+
 	return false;
 }
 
-void MacIndy3Gui::InventoryWidget::draw() {
-	MacIndy3Gui::GuiWidget::draw();
+bool MacIndy3Gui::Inventory::updateTimer() {
+	VerbWidget::updateTimer();
 
-#if 0
-	for (int i = 0; i < ARRAYSIZE(_slots); i++)
-		_slots[i]->reset();
-#endif
+	for (int i = 0; i < ARRAYSIZE(_slots); i++) {
+		Slot *s = _slots[i];
 
-	drawShadowBox(_bounds);
-	drawShadowFrame(Common::Rect(_bounds.left + 4, _bounds.top + 4, _bounds.right - 22, _bounds.bottom - 4), 0, 15);
+		if (s->updateTimer()) {
+			s->_redraw = true;
+			_vm->runInputScript(kInventoryClickArea, s->_obj, 1);
+		}
+	}
 
-#if 0
-	// Scrollbar
-	drawInventoryScrollButton(_inventoryScrollButtons[0]);
-	drawInventoryScrollButton(_inventoryScrollButtons[1]);
-#endif
+	for (int i = 0; i < ARRAYSIZE(_scrollButtons); i++) {
+		ScrollButton *b = _scrollButtons[i];
 
-	drawShadowFrame(Common::Rect(_bounds.right - 20, _bounds.top + 19, _bounds.right - 4, _bounds.bottom - 19), 0, 255);
+		if (b->updateTimer()) {
+			b->_redraw = true;
+		}
+	}
 
-	copyRectToScreen(_bounds);
+	return false;
+}
+
+void MacIndy3Gui::Inventory::draw() {
+	if (_redraw) {
+		debug("Drawing button [%d] Inventory", _verbid);
+
+		MacIndy3Gui::VerbWidget::draw();
+
+		for (int i = 0; i < ARRAYSIZE(_slots); i++)
+			_slots[i]->reset();
+
+		drawShadowBox(_bounds);
+		drawShadowFrame(Common::Rect(_bounds.left + 4, _bounds.top + 4, _bounds.right - 22, _bounds.bottom - 4), 0, 15);
+		drawShadowFrame(Common::Rect(_bounds.right - 20, _bounds.top + 19, _bounds.right - 4, _bounds.bottom - 19), 0, 255);
+
+		for (int i = 0; i < ARRAYSIZE(_scrollButtons); i++) {
+			ScrollButton *s = _scrollButtons[i];
+			const uint16 *arrow = (s->_direction == kScrollUp) ? _upArrow : _downArrow;
+
+			drawShadowFrame(s->_bounds, 15, 255);
+			drawBitmap(s->_bounds, arrow, 0);
+			s->draw();
+			s->_redraw = true;
+		}
+
+		copyRectToScreen(_bounds);
+	}
+
+	// Since the inventory slots overlap, draw the highlighted ones (and
+	// there should really only be one at a time) last.
+
+	for (int i = 0; i < ARRAYSIZE(_slots); i++) {
+		if (!_slots[i]->_timer)
+			_slots[i]->draw();
+	}
+
+	for (int i = 0; i < ARRAYSIZE(_slots); i++) {
+		if (_slots[i]->_timer)
+			_slots[i]->draw();
+	}
+
+	for (int i = 0; i < ARRAYSIZE(_scrollButtons); i++)
+		_scrollButtons[i]->draw();
 }
 
-MacIndy3Gui::InventorySlot::InventorySlot(int slot, int x, int y, int width, int height) : MacIndy3Gui::Widget(x, y, width, height) {
+MacIndy3Gui::Inventory::Slot::Slot(int slot, int x, int y, int width, int height) : MacIndy3Gui::Widget(x, y, width, height) {
 	_slot = slot;
 }
 
-void MacIndy3Gui::InventorySlot::reset() {
+void MacIndy3Gui::Inventory::Slot::reset() {
 	Widget::reset();
-	free(_name);
-	_name = nullptr;
+	_name.clear();
 	_obj = -1;
 }
 
-bool MacIndy3Gui::InventorySlot::updateTimer() {
+bool MacIndy3Gui::Inventory::Slot::handleEvent(Common::Event &event) {
+	if (event.type != Common::EVENT_LBUTTONDOWN)
+		return false;
+
+	if (_obj != -1 && _bounds.contains(event.mouse.x, event.mouse.y)) {
+		_redraw = true;
+		_timer = 7;
+		return true;
+	}
+
+	return false;
+}
+
+bool MacIndy3Gui::Inventory::Slot::updateTimer() {
 	bool ret = Widget::updateTimer();
 
 	if (ret) {
@@ -675,14 +837,81 @@ bool MacIndy3Gui::InventorySlot::updateTimer() {
 	return ret;
 }
 
-void MacIndy3Gui::InventorySlot::draw() {
+void MacIndy3Gui::Inventory::Slot::draw() {
+	if (!_redraw)
+		return;
+
+	debug("Drawing inventory slot: [%d] %s", _slot, _name.c_str());
+	int fg, bg;
+
+	if (_timer) {
+		fg = 15;
+		bg = 0;
+	} else {
+		fg = 0;
+		bg = 15;
+	}
+
+	_surface->fillRect(_bounds, bg);
+
+	if (!_name.empty()) {
+		const Graphics::Font *font = _gui->getFont(0);
+
+		int y = _bounds.top - 1;
+		int x = _bounds.left + 4;
+
+		for (uint i = 0; i < _name.size(); i++) {
+			if (_name[i] != '@') {
+				font->drawChar(_surface, _name[i], x, y, fg);
+				x += font->getCharWidth(_name[i]);
+			}
+		}
+	}
+
+	_redraw = false;
+	copyRectToScreen(_bounds);
 }
 
-MacIndy3Gui::InventoryScrollButton::InventoryScrollButton(int x, int y, int width, int height, ScrollDirection direction) : MacIndy3Gui::Widget(x, y, width, height) {
+const uint16 MacIndy3Gui::Inventory::ScrollButton::_upArrow[16] = {
+	0x0000, 0x0000, 0x0000, 0x0000,	0x0080, 0x01C0, 0x03E0, 0x07F0,
+	0x03E0, 0x03E0, 0x03E0, 0x03E0,	0x0000, 0x0000, 0x0000, 0x0000
+};
+
+const uint16 MacIndy3Gui::Inventory::ScrollButton::_downArrow[16] = {
+	0x0000, 0x0000, 0x0000, 0x0000,	0x0000, 0x03E0, 0x03E0, 0x03E0,
+	0x03E0, 0x07F0,	0x03E0, 0x01C0,	0x0080, 0x0000, 0x0000, 0x0000
+};
+
+MacIndy3Gui::Inventory::ScrollButton::ScrollButton(int x, int y, int width, int height, ScrollDirection direction) : MacIndy3Gui::Widget(x, y, width, height) {
 	_direction = direction;
 }
 
-void MacIndy3Gui::InventoryScrollButton::draw() {
+bool MacIndy3Gui::Inventory::ScrollButton::handleEvent(Common::Event &event) {
+	if (event.type != Common::EVENT_LBUTTONDOWN)
+		return false;
+
+	if (_enabled && _bounds.contains(event.mouse.x, event.mouse.y)) {
+		_redraw = true;
+		_timer = 15;
+		return true;
+	}
+
+	return false;
+}
+
+void MacIndy3Gui::Inventory::ScrollButton::draw() {
+	if (!_redraw)
+		return;
+
+	debug("Drawing inventory arrow %d", _direction);
+
+	_redraw = false;
+
+	const uint16 *arrow = (_direction == kScrollUp) ? _upArrow : _downArrow;
+	byte color = _timer ? 0 : 15;
+
+	drawBitmap(_bounds, arrow, color);
+	copyRectToScreen(_bounds);
 }
 
 MacIndy3Gui::MacIndy3Gui(OSystem *system, ScummEngine *vm) :
@@ -700,82 +929,43 @@ MacIndy3Gui::MacIndy3Gui(OSystem *system, ScummEngine *vm) :
 	Widget::_surface = _macScreen;
 	Widget::_gui = this;
 
-	_widgets[0]  = new Button(  1, 137, 312,  68, 18); // Open
-	_widgets[1]  = new Button(  2, 137, 332,  68, 18); // Close
-	_widgets[2]  = new Button(  3,  67, 352,  68, 18); // Give
-	_widgets[3]  = new Button(  4, 277, 332,  68, 18); // Turn on
-	_widgets[4]  = new Button(  5, 277, 352,  68, 18); // Turn off
-	_widgets[5]  = new Button(  6,  67, 312,  68, 18); // Push
-	_widgets[6]  = new Button(  7,  67, 332,  68, 18); // Pull
-	_widgets[7]  = new Button(  8, 277, 312,  68, 18); // Use
-	_widgets[8]  = new Button(  9, 137, 352,  68, 18); // Look at
-	_widgets[9]  = new Button( 10, 207, 312,  68, 18); // Walk to
-	_widgets[10] = new Button( 11, 207, 332,  68, 18); // Pick up
-	_widgets[11] = new Button( 12, 207, 352,  68, 18); // What is
-	_widgets[12] = new Button( 13, 347, 312,  68, 18); // Talk
-	_widgets[13] = new Button( 14,  97, 312, 121, 18); // Never mind.
-	_widgets[14] = new Button( 32, 347, 332,  68, 18); // Travel
-	_widgets[15] = new Button( 33, 347, 352,  68, 18); // To Indy
-	_widgets[16] = new Button( 34, 347, 352,  68, 18); // To Henry
-	_widgets[17] = new Button( 90,  67, 292, 507, 18); // Travel 1
-	_widgets[18] = new Button( 91,  67, 312, 507, 18); // Travel 2
-	_widgets[19] = new Button( 92,  67, 332, 507, 18); // Travel 3
-	_widgets[20] = new Button(100,  67, 292, 348, 18); // Sentence
-	_widgets[21] = new InventoryWidget(101, 417, 292, 157, 78);
-	_widgets[22] = new Button(119, 324, 312,  91, 18); // Take this:
-	_widgets[23] = new Button(120,  67, 292, 507, 18); // Converse 1
-	_widgets[24] = new Button(121,  67, 312, 507, 18); // Converse 2
-	_widgets[25] = new Button(122,  67, 332, 507, 18); // Converse 3
-	_widgets[26] = new Button(123,  67, 352, 507, 18); // Converse 4
-	_widgets[27] = new Button(124,  67, 352, 151, 18); // Converse 5
-	_widgets[28] = new Button(125, 423, 352, 151, 18); // Converse 6
-
-#if 0
-	// Inventory widget sub-widgets.
-
-	// No matter how many objects you are carrying, only six inventory slots
-	// are visible.
-
-	Widget *w = _widgets[21];
-	assert(w->_verbid == 101);
-
-	int x = w->_bounds.left + 6;
-	int y = w->_bounds.top + 6;
-
-	// Each slot is 12 pixels tall (as seen when they are highlighted), which
-	// means they have to overlap slightly to fit. It is assumed that this will
-	// never interfere with the text drawing.
-
-	for (int i = 0; i < ARRAYSIZE(_inventorySlots); i++) {
-		_inventorySlots[i] = new InventorySlot(i, x, y, 128, 12);
-		_inventorySlots[i]->setGui(this);
-		_inventorySlots[i]->setSurface(_vm->_macScreen);
-		y += 11;
-	}
-
-	// Scrollbar arrow buttons
-	x = w->_bounds.left + 137;
-	y = w->_bounds.top + 4;
-
-	_inventoryScrollButtons[0] = new InventoryScrollButton(x, w->_bounds.top + 4, 16, 16, kScrollUp);
-	_inventoryScrollButtons[1] = new InventoryScrollButton(x, w->_bounds.bottom - 20, 16, 16, kScrollDown);
-
-	for (int i = 0; i < ARRAYSIZE(_inventoryScrollButtons); i++) {
-		_inventoryScrollButtons[i]->setGui(this);
-		_inventoryScrollButtons[i]->setSurface(_vm->_macScreen);
-	}
-#endif
+	_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
+	_widgets[  4] = new Button(277, 332,  68, 18); // Turn on
+	_widgets[  5] = new Button(277, 352,  68, 18); // Turn off
+	_widgets[  6] = new Button( 67, 312,  68, 18); // Push
+	_widgets[  7] = new Button( 67, 332,  68, 18); // Pull
+	_widgets[  8] = new Button(277, 312,  68, 18); // Use
+	_widgets[  9] = new Button(137, 352,  68, 18); // Look at
+	_widgets[ 10] = new Button(207, 312,  68, 18); // Walk to
+	_widgets[ 11] = new Button(207, 332,  68, 18); // Pick up
+	_widgets[ 12] = new Button(207, 352,  68, 18); // What is
+	_widgets[ 13] = new Button(347, 312,  68, 18); // Talk
+	_widgets[ 14] = new Button( 97, 312, 121, 18); // Never mind.
+	_widgets[ 32] = new Button(347, 332,  68, 18); // Travel
+	_widgets[ 33] = new Button(347, 352,  68, 18); // To Indy
+	_widgets[ 34] = new Button(347, 352,  68, 18); // To Henry
+	_widgets[ 90] = new Button( 67, 292, 507, 18); // Travel 1
+	_widgets[ 91] = new Button( 67, 312, 507, 18); // Travel 2
+	_widgets[ 92] = new Button( 67, 332, 507, 18); // Travel 3
+	_widgets[100] = new Button( 67, 292, 348, 18); // Sentence
+	_widgets[101] = new Inventory(417, 292, 157, 78);
+	_widgets[119] = new Button(324, 312,  91, 18); // Take this:
+	_widgets[120] = new Button( 67, 292, 507, 18); // Converse 1
+	_widgets[121] = new Button( 67, 312, 507, 18); // Converse 2
+	_widgets[122] = new Button( 67, 332, 507, 18); // Converse 3
+	_widgets[123] = new Button( 67, 352, 507, 18); // Converse 4
+	_widgets[124] = new Button( 67, 352, 151, 18); // Converse 5
+	_widgets[125] = new Button(423, 352, 151, 18); // Converse 6
+
+	for (Common::HashMap<int, VerbWidget *>::iterator i = _widgets.begin(); i != _widgets.end(); ++i)
+		i->_value->_verbid = i->_key;
 }
 
 MacIndy3Gui::~MacIndy3Gui() {
-	for (int i = 0; i < ARRAYSIZE(_widgets); i++)
-		delete _widgets[i];
-#if 0
-	for (int i = 0; i < ARRAYSIZE(_inventorySlots); i++)
-		delete _inventorySlots[i];
-	for (int i = 0; i < ARRAYSIZE(_inventoryScrollButtons); i++)
-		delete _inventoryScrollButtons[i];
-#endif
+	for (Common::HashMap<int, VerbWidget *>::iterator i = _widgets.begin(); i != _widgets.end(); ++i)
+		delete i->_value;
 }
 
 bool MacIndy3Gui::isActive() {
@@ -812,13 +1002,8 @@ bool MacIndy3Gui::isActive() {
 void MacIndy3Gui::resetAfterLoad() {
 	_visible = false;
 
-	for (int i = 0; i < ARRAYSIZE(_widgets); i++)
-		_widgets[i]->reset();
-
-#if 0
-	for (int i = 0; i < ARRAYSIZE(_inventorySlots); i++)
-		_inventorySlots[i]->reset();
-#endif
+	for (Common::HashMap<int, VerbWidget *>::iterator i = _widgets.begin(); i != _widgets.end(); ++i)
+		i->_value->reset();
 
 	// In the DOS version, verb ID 102-106 were used for the visible
 	// inventory items, and 107-108 for inventory arrow buttons. In the
@@ -860,90 +1045,18 @@ void MacIndy3Gui::update() {
 		return;
 	}
 
-	for (int i = 0; i < ARRAYSIZE(_widgets); i++) {
-		_widgets[i]->updateTimer();
-		_widgets[i]->_kill = true;
-	}
-
-	bool keepGuiAlive = false;
-	bool hasInventory = false;
-
-	updateWidgets(keepGuiAlive, hasInventory);
-
-	if (!keepGuiAlive) {
-		hide();
-		return;
-	}
-
-	if (!isVisible())
-		show();
-
-	for (int i = 0; i < ARRAYSIZE(_widgets); i++) {
-		GuiWidget *w = _widgets[i];
-		if (w->_kill && w->_visible) {
-			w->undraw();
-			w->reset();
-		}
-	}
-
-	for (int i = 0; i < ARRAYSIZE(_widgets); i++) {
-		GuiWidget *w = _widgets[i];
-		if (w->_verbslot != -1 && w->_redraw) {
-			debug("Drawing button %d: (%d) %s", i, w->_verbid, w->name());
-			w->draw();
-		}
-	}
-
-#if 0
-	drawButtons();
-#endif
+	// Tentatively mark the verb widgets for removal. Any widget that wants
+	// to stay has to say so.
 
-#if 0
-	if (hasInventory) {
-		updateInventorySlots();
-		drawInventorySlots();
-		fillInventoryArrows();
-	}
-#endif
-}
+	for (Common::HashMap<int, VerbWidget *>::iterator i = _widgets.begin(); i != _widgets.end(); ++i) {
+		VerbWidget *w = i->_value;
 
-#if 0
-void MacIndy3Gui::updateTimers() {
-	for (int i = 0; i < ARRAYSIZE(_widgets); i++) {
-		Widget *w = _widgets[i];
-		if (w->_timer > 0) {
-			w->_timer--;
-			if (w->_timer == 0 && w->_visible) {
-				_vm->runInputScript(kVerbClickArea, w->_verbid, 1);
-				w->_redraw = true;
-			}
-		}
-		w->_kill = true;
+		w->updateTimer();
+		w->threaten();
 	}
 
-	for (int i = 0; i < ARRAYSIZE(_inventorySlots); i++) {
-		InventorySlot *s = _inventorySlots[i];
-		if (s->_timer > 0) {
-			s->_timer--;
-			if (s->_timer == 0) {
-				s->_redraw = true;
-				_vm->runInputScript(kInventoryClickArea, s->_obj, 1);
-			}
-		}
-	}
-
-	for (int i = 0; i < ARRAYSIZE(_inventoryScrollButtons); i++) {
-		InventoryScrollButton *b = _inventoryScrollButtons[i];
-		if (b->_timer > 0) {
-			b->_timer--;
-			if (b->_timer == 0)
-				b->_redraw = true;
-		}
-	}
-}
-#endif
+	bool keepGuiAlive = false;
 
-void MacIndy3Gui::updateWidgets(bool &keepGuiAlive, bool &hasInventory) {
 	// Collect all active verbs. Verb slot 0 is special, apparently, so we
 	// don't look at that one.
 
@@ -951,109 +1064,30 @@ void MacIndy3Gui::updateWidgets(bool &keepGuiAlive, bool &hasInventory) {
 		VerbSlot *vs = &_vm->_verbs[i];
 
 		if (!vs->saveid && vs->curmode && vs->verbid) {
-			int id = -1;
-
-			if (vs->verbid >= 1 && vs->verbid <= 14) {
-				id = vs->verbid - 1;
-			} else if (vs->verbid >= 32 && vs->verbid <= 34) {
-				id = vs->verbid - 18;
-			} else if (vs->verbid >= 90 && vs->verbid <= 92) {
-				id = vs->verbid - 73;
-			} else if (vs->verbid == 100) {
-				id = 20;
-			} else if (vs->verbid == 101) {
-				id = 21;
-				hasInventory = true;
-			} else if (vs->verbid >= 119 && vs->verbid <= 125) {
-				id = vs->verbid - 97;
+			VerbWidget *w = _widgets.getValOrDefault(vs->verbid);
+
+			if (w) {
+				w->updateVerb(i);
+				keepGuiAlive = true;
 			} else {
 				const byte *ptr = _vm->getResourceAddress(rtVerb, i);
 				byte buf[270];
 				_vm->convertMessageToString(ptr, buf, sizeof(buf));
 				debug("Unknown verb: %d %s", vs->verbid, buf);
 			}
-
-			if (id != -1) {
-				_widgets[id]->updateVerb(i);
-				keepGuiAlive = true;
-			}
-		}
-	}
-}
-
-#if 0
-void MacIndy3Gui::updateInventorySlots() {
-	int owner = _vm->VAR(_vm->VAR_EGO);
-
-	int invCount = _vm->getInventoryCount(owner);
-	int invOffset = _vm->VAR(67);
-
-	// The scroll offset must be non-negative and if there are six or less
-	// items in the inventory, the inventory is fixed in the top position.
-
-	if (invOffset < 0 || invCount <= 6)
-		invOffset = 0;
-
-	// If there are more than six items in the inventory, clamp the scroll
-	// offset to be at most invCount - 6.
-
-	if (invCount > 6 && invOffset >= invCount - 6)
-		invOffset = invCount - 6;
-
-	_vm->VAR(67) = invOffset;
-
-	int invSlot = 0;
-	invCount = 0;
-
-	for (int i = 0; i < _vm->_numInventory && invSlot < ARRAYSIZE(_inventorySlots); i++) {
-		int obj = _vm->_inventory[i];
-		if (obj && _vm->getOwner(obj) == owner) {
-			if (++invCount >= invOffset) {
-				InventorySlot *s = _inventorySlots[invSlot];
-				const byte *name = _vm->getObjOrActorName(obj);
-
-				if (name) {
-					if (!s->_name || strcmp((char *)s->_name, (const char *)name) != 0) {
-						free(s->_name);
-						s->_name = (byte *)scumm_strdup((const char *)name);
-						s->_timer = 0;
-						s->_redraw = true;
-					}
-				} else {
-					if (s->_name) {
-						free(s->_name);
-						s->_name = nullptr;
-						s->_timer = 0;
-						s->_redraw = true;
-					}
-				}
-
-				s->_obj = obj;
-				invSlot++;
-			}
 		}
 	}
 
-	for (int i = invSlot; i < ARRAYSIZE(_inventorySlots); i++) {
-		InventorySlot *s = _inventorySlots[i];
-
-		if (s->_name) {
-			free(s->_name);
-			s->_name = nullptr;
-			s->_obj = -1;
-			s->_timer = 0;
-			s->_redraw = true;
-		}
+	if (!keepGuiAlive) {
+		hide();
+		return;
 	}
-}
 
-#if 0
-void MacIndy3Gui::drawButtons() {
-	// Erase all inactive buttons. Since active buttons may overlap ones
-	// that are now inactive, we have to do this first.
+	if (!isVisible())
+		show();
 
-	for (int i = 0; i < ARRAYSIZE(_widgets); i++) {
-		Widget *w = _widgets[i];
+	for (Common::HashMap<int, VerbWidget *>::iterator i = _widgets.begin(); i != _widgets.end(); ++i) {
+		VerbWidget *w = i->_value;
 
 		if (w->_kill && w->_visible) {
 			w->undraw();
@@ -1061,89 +1095,23 @@ void MacIndy3Gui::drawButtons() {
 		}
 	}
 
-	// Draw all active buttons
-
-	for (int i = 0; i < ARRAYSIZE(_widgets); i++) {
-		Widget *w = _widgets[i];
+	for (Common::HashMap<int, VerbWidget *>::iterator i = _widgets.begin(); i != _widgets.end(); ++i) {
+		VerbWidget *w = i->_value;
 
-		if (w->_verbslot != -1 && w->_redraw) {
-			debug("Drawing button %d: (%d) %s", i, w->_verbid, w->name());
+		if (w->_verbslot != -1) {
 			w->draw();
 		}
 	}
 }
-#endif
-
-void MacIndy3Gui::drawInventorySlots() {
-	// Since the inventory slots overlap, draw the highlighted ones (and there
-	// should really only be one at a time) first.
-
-	for (int i = 0; i < ARRAYSIZE(_inventorySlots); i++) {
-		InventorySlot *s = _inventorySlots[i];
-
-		if (s->_redraw && s->_timer == 0)
-			drawInventorySlot(s);
-	}
-
-	for (int i = 0; i < ARRAYSIZE(_inventorySlots); i++) {
-		InventorySlot *s = _inventorySlots[i];
-
-		if (s->_redraw)
-			drawInventorySlot(s);
-	}
-}
-
-void MacIndy3Gui::fillInventoryArrows() {
-	for (int i = 0; i < ARRAYSIZE(_inventoryScrollButtons); i++) {
-		InventoryScrollButton *b = _inventoryScrollButtons[i];
-
-		if (b->_redraw)
-			fillInventoryArrow(b);
-	}
-}
-#endif
 
 void MacIndy3Gui::handleEvent(Common::Event &event) {
 	if (!isVisible() || _vm->_userPut <= 0)
 		return;
 
-	for (int i = 0; i < ARRAYSIZE(_widgets); i++) {
-		if (_widgets[i]->handleEvent(event))
+	for (Common::HashMap<int, VerbWidget *>::iterator i = _widgets.begin(); i != _widgets.end(); ++i) {
+		if (i->_value->handleEvent(event))
 			break;
 	}
-
-#if 0
-			if (w->_verbid != 101) {
-				// The input script is invoked when the button animation
-				// times out. Speed-runners will probably hate this, but
-				// I think it looks better this way.
-				w->_redraw = true;
-				w->_timer = BUTTON_TIMER;
-				return;
-			} else {
-				for (int j = 0; j < ARRAYSIZE(_inventorySlots); j++) {
-					InventorySlot *s = _inventorySlots[j];
-
-					if (s->_obj != -1 && s->_bounds.contains(x, y)) {
-						s->_redraw = true;
-						s->_timer = INVENTORY_SLOT_TIMER;
-						return;
-					}
-				}
-
-				for (int j = 0; j < ARRAYSIZE(_inventoryScrollButtons); j++) {
-					InventoryScrollButton *b = _inventoryScrollButtons[j];
-
-					if (b->_enabled && b->_bounds.contains(x, y)) {
-						b->_redraw = true;
-						b->_timer = BUTTON_TIMER;
-						return;
-					}
-				}
-			}
-		}
-	}
-#endif
 }
 
 void MacIndy3Gui::show() {
@@ -1162,8 +1130,8 @@ void MacIndy3Gui::hide() {
 
 	_visible = false;
 
-	for (int i = 0; i < ARRAYSIZE(_widgets); i++)
-		_widgets[i]->reset();
+	for (Common::HashMap<int, VerbWidget *>::iterator i = _widgets.begin(); i != _widgets.end(); ++i)
+		i->_value->reset();
 
 	if (_vm->_virtscr[kVerbVirtScreen].topline != 144)
 		return;
@@ -1187,70 +1155,6 @@ void MacIndy3Gui::clear() {
 	copyRectToScreen(Common::Rect(0, 288, 640, 400));
 }
 
-#if 0
-void MacIndy3Gui::drawInventoryScrollButton(InventoryScrollButton *b) {
-	Common::Rect r = b->_bounds;
-	uint16 *outline;
-	uint16 *arrow;
-
-	if (b->_direction == kScrollUp) {
-		outline = _upArrowOutline;
-		arrow = _upArrow;
-	} else {
-		outline = _downArrowOutline;
-		arrow = _downArrow;
-	}
-
-	// Don't use fillInventoryArrow() here, because it will needlessly copy
-	// the arrow to the screen. We assume the arrow is always white.
-
-	drawShadowFrame(r, 15, 255);
-	drawBitmap(r, outline, 0);
-	drawBitmap(r, arrow, 15);
-}
-
-void MacIndy3Gui::fillInventoryArrow(InventoryScrollButton *b) {
-	Common::Rect r = b->_bounds;
-	uint16 *bitmap = b->_direction == kScrollUp ? _upArrow : _downArrow;
-	byte color = b->_timer ? 0 : 15;
-
-	drawBitmap(r, bitmap, color);
-	b->_redraw = false;
-	copyRectToScreen(r);
-}
-
-void MacIndy3Gui::drawInventorySlot(InventorySlot *s) {
-	debug("Drawing inventory slot: [%d] %s", s->_slot, s->_name);
-	Common::Rect r = s->_bounds;
-	int fg, bg;
-
-	if (s->_timer) {
-		fg = 15;
-		bg = 0;
-	} else {
-		fg = 0;
-		bg = 15;
-	}
-
-	_macScreen->fillRect(r, bg);
-
-	if (s->_name) {
-		int y = _bounds.top - 1;
-		int x = _bounds.left + 4;
-
-		for (int i = 0; s->_name[i]; i++) {
-			if (s->_name[i] != '@') {
-				_fonts[0]->drawChar(_macScreen, s->_name[i], x, y, fg);
-				x += _fonts[0]->getCharWidth(s->_name[i]);
-			}
-		}
-	}
-
-	s->_redraw = false;
-	copyRectToScreen(r);
-}
-#endif
-
 void MacIndy3Gui::copyRectToScreen(Common::Rect r) {
 	_system->copyRectToScreen(_macScreen->getBasePtr(r.left, r.top), _macScreen->pitch, r.left, r.top, r.width(), r.height());
 }
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index ec977d12e37..651234b46ee 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -82,7 +82,6 @@ private:
 
 		virtual void draw() = 0;
 		virtual void undraw() {}
-
 		virtual bool updateTimer();
 
 		// Primitives
@@ -93,18 +92,17 @@ private:
 		void drawShadowFrame(Common::Rect r, byte shadowColor, byte fillColor);
 	};
 
-	class GuiWidget : public Widget {
+	class VerbWidget : public Widget {
 	public:
 		int _verbid = 0;
 		int _verbslot = -1;
 		bool _visible = false;
 		bool _kill = false;
 
-		GuiWidget(int verbid, int x, int y, int width, int height);
-
-		virtual const char *name() = 0;
+		VerbWidget(int x, int y, int width, int height) : Widget(x, y, width, height) {}
 
 		void reset();
+		void threaten() { _kill = true; }
 
 		virtual bool handleEvent(Common::Event &event) = 0;
 		virtual void updateVerb(int verbslot);
@@ -113,14 +111,12 @@ private:
 		void undraw();
 	};
 
-	class Button : public GuiWidget {
+	class Button : public VerbWidget {
 	public:
-		byte *_text = nullptr;
+		Common::String _text;
 
-		Button(int verbid, int x, int y, int width, int height);
-		~Button();
+		Button(int x, int y, int width, int height) : VerbWidget(x, y, width, height) {}
 
-		const char *name() { return _text ? (const char *)_text : "(null)"; }
 		bool handleEvent(Common::Event &event);
 
 		void reset();
@@ -134,106 +130,68 @@ private:
 		kScrollDown
 	};
 
-	class InventoryScrollButton : public Widget {
+	class Inventory : public VerbWidget {
 	private:
-		ScrollDirection _direction;
+		class ScrollButton : public Widget {
+		private:
+			static const uint16 _upArrow[16];
+			static const uint16 _downArrow[16];
 
-	public:
-		InventoryScrollButton(int x, int y, int width, int height, ScrollDirection direction);
+		public:
+			ScrollDirection _direction;
 
-		void draw();
-	};
+			ScrollButton(int x, int y, int width, int height, ScrollDirection direction);
 
-	class InventorySlot : public Widget {
-	private:
-		byte *_name = nullptr;
-		int _slot = -1;
-		int _obj = -1;
+			bool handleEvent(Common::Event &event);
+			void draw();
+		};
 
-	public:
-		InventorySlot(int slot, int x, int y, int width, int height);
-		void reset();
-		bool updateTimer();
-		void draw();
-	};
+		class Slot : public Widget {
+		private:
+			int _slot = -1;
 
-	class InventoryWidget : public GuiWidget {
-	private:
-		InventorySlot *_slots[6];
-		InventoryScrollButton *_scrollButtons[2];
+		public:
+			int _obj = -1;
+			Common::String _name;
+
+			Slot(int slot, int x, int y, int width, int height);
+			void reset();
+			bool handleEvent(Common::Event &event);
+			bool updateTimer();
+			void draw();
+		};
+
+		Slot *_slots[6];
+		ScrollButton *_scrollButtons[2];
+
+		static const uint16 _upArrow[16];
+		static const uint16 _downArrow[16];
 
 	public:
-		InventoryWidget(int verbid, int x, int y, int width, int height);
+		Inventory(int x, int y, int width, int height);
+		~Inventory();
 
-		const char *name() { return "Inventory"; }
+		void updateVerb(int verbslot);
 
 		bool handleEvent(Common::Event &event);
+		bool updateTimer();
 
 		void draw();
 	};
 
-	GuiWidget *_widgets[29];
+	Common::HashMap<int, VerbWidget *> _widgets;
 
 	const uint16 _ulCorner[4] = { 0xF000, 0xC000, 0x8000, 0x8000 };
 	const uint16 _urCorner[4] = { 0xF000, 0x3000, 0x1000, 0x1000 };
 	const uint16 _llCorner[4] = { 0x8000, 0x8000, 0xC000, 0xF000 };
 	const uint16 _lrCorner[5] = { 0x1000, 0x1000, 0x3000, 0xF000 };
 
-	const uint16 _upArrowOutline[16] = {
-		0x0000, 0x0000, 0x0000, 0x0080,	0x0140, 0x0220, 0x0410, 0x0808,
-		0x1C1C, 0x0410, 0x0410, 0x0410, 0x07F0, 0x0000, 0x0000, 0x0000
-	};
-
-	uint16 _upArrow[16] = {
-		0x0000, 0x0000, 0x0000, 0x0000, 0x0080, 0x01C0, 0x03E0, 0x07F0,
-		0x03E0, 0x03E0, 0x03E0, 0x03E0, 0x0000, 0x0000, 0x0000, 0x0000
-	};
-
-	uint16 _downArrowOutline[16] = {
-		0x0000, 0x0000, 0x0000, 0x0000, 0x07F0, 0x0410, 0x0410, 0x0410,
-		0x1C1C, 0x0808, 0x0410, 0x0220, 0x0140, 0x0080, 0x0000, 0x0000
-	};
-
-	uint16 _downArrow[16] = {
-		0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x03E0, 0x03E0, 0x03E0,
-		0x03E0, 0x07F0,	0x03E0, 0x01C0, 0x0080, 0x0000, 0x0000, 0x0000
-	};
-
 	bool isActive();
 
-#if 0
-	void initWidget(Widget *w, int x, int y, int width, int height);
-	void resetBaseWidget(Widget *w);
-
-	void initWidget(int n, int verbid, int x, int y, int width, int height);
-	void resetWidget(Widget *w);
-	void initInventorySlot(int n, int x, int y, int width, int height);
-	void resetInventorySlot(InventorySlot *s);
-	void initInventoryScrollButton(int n, int x, int y, int width, int height, ScrollDirection direction);
-	void resetInventoryScrollButton(InventoryScrollButton *b);
-#endif
-
 	void clear();
 	void show();
 	void hide();
 
-	void updateWidgets(bool &keepGuiAlive, bool &hasInventory);
-#if 0
-	void updateInventorySlots();
-
-	void drawButtons();
-	void drawInventorySlots();
-	void fillInventoryArrows();
-#endif
-
-#if 0
-	void drawButton(Widget *w);
-	void drawInventoryWidget(Widget *w);
-	void drawInventorySlot(InventorySlot *slot);
-	void drawInventoryScrollButton(InventoryScrollButton *b);
-	void fillInventoryArrow(InventoryScrollButton *b);
-#endif
-
 	void copyRectToScreen(Common::Rect r);
 	void fill(Common::Rect r);
 	void drawBitmap(Common::Rect r, const uint16 *bitmap, byte color);


Commit: 6f8f3b4b13416a2748f6a8260d294c4bad056fef
    https://github.com/scummvm/scummvm/commit/6f8f3b4b13416a2748f6a8260d294c4bad056fef
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Update Indy 3 Mac GUI less frequently

It's enough to do it after thigns have had a chance to change. Not every
iteration of waitForTimer(). This means we no longer have as precise
resolution for our animation timers, but I don't think that's any reason
not to cut down on the (admittedly small) amount of extra work the GUI
has to do.

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


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 4a301ed8ca9..093a5a4a3d2 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -395,10 +395,15 @@ void MacIndy3Gui::Widget::reset() {
 	_redraw = false;
 }
 
-bool MacIndy3Gui::Widget::updateTimer() {
+bool MacIndy3Gui::Widget::updateTimer(int delta) {
 	if (_timer == 0)
 		return false;
-	return --_timer == 0;
+
+	if (delta > _timer)
+		delta = _timer;
+
+	_timer -= MIN(delta, _timer);
+	return _timer == 0;
 }
 
 void MacIndy3Gui::Widget::copyRectToScreen(Common::Rect r) {
@@ -483,23 +488,41 @@ void MacIndy3Gui::Button::reset() {
 }
 
 bool MacIndy3Gui::Button::handleEvent(Common::Event &event) {
-	if (event.type != Common::EVENT_LBUTTONDOWN)
+	if (!_verbid || !_enabled)
 		return false;
 
-	int x = event.mouse.x;
-	int y = event.mouse.y;
+	bool caughtEvent = false;
+
+	uint8 key = _vm->_verbs[_verbslot].key;
 
-	if (_verbid && _enabled && _bounds.contains(x, y)) {
+	if (event.type == Common::EVENT_KEYDOWN) {
+		if (!event.kbd.hasFlags(Common::KBD_NON_STICKY) && event.kbd.keycode == key) {
+			caughtEvent = true;
+		}
+	} else if (event.type == Common::EVENT_LBUTTONDOWN) {
+		int x = event.mouse.x;
+		int y = event.mouse.y;
+
+		if (_bounds.contains(x, y))
+			caughtEvent = true;
+	}
+
+	// Events are handled at the end of the animation. This blatant attack
+	// on speedrunners all over the world was done because that's what the
+	// original seems to do, and it looks better. Based on tests in Mac
+	// emulators, the speed of the animation depended on the speed of your
+	// computer, so we just set something that looks good here.
+
+	if (caughtEvent) {
 		_redraw = true;
-		_timer = 15;
-		return true;
+		_timer = 12;
 	}
 
-	return false;
+	return caughtEvent;
 }
 
-bool MacIndy3Gui::Button::updateTimer() {
-	bool ret = VerbWidget::updateTimer();
+bool MacIndy3Gui::Button::updateTimer(int delta) {
+	bool ret = VerbWidget::updateTimer(delta);
 
 	if (ret) {
 		if (_visible) {
@@ -721,6 +744,9 @@ void MacIndy3Gui::Inventory::updateVerb(int verbslot) {
 }
 
 bool MacIndy3Gui::Inventory::handleEvent(Common::Event &event) {
+	if (!_verbid || !_enabled)
+		return false;
+
 	if (event.type != Common::EVENT_LBUTTONDOWN)
 		return false;
 
@@ -737,13 +763,13 @@ bool MacIndy3Gui::Inventory::handleEvent(Common::Event &event) {
 	return false;
 }
 
-bool MacIndy3Gui::Inventory::updateTimer() {
-	VerbWidget::updateTimer();
+bool MacIndy3Gui::Inventory::updateTimer(int delta) {
+	VerbWidget::updateTimer(delta);
 
 	for (int i = 0; i < ARRAYSIZE(_slots); i++) {
 		Slot *s = _slots[i];
 
-		if (s->updateTimer()) {
+		if (s->updateTimer(delta)) {
 			s->_redraw = true;
 			_vm->runInputScript(kInventoryClickArea, s->_obj, 1);
 		}
@@ -752,7 +778,7 @@ bool MacIndy3Gui::Inventory::updateTimer() {
 	for (int i = 0; i < ARRAYSIZE(_scrollButtons); i++) {
 		ScrollButton *b = _scrollButtons[i];
 
-		if (b->updateTimer()) {
+		if (b->updateTimer(delta)) {
 			b->_redraw = true;
 		}
 	}
@@ -819,15 +845,15 @@ bool MacIndy3Gui::Inventory::Slot::handleEvent(Common::Event &event) {
 
 	if (_obj != -1 && _bounds.contains(event.mouse.x, event.mouse.y)) {
 		_redraw = true;
-		_timer = 7;
+		_timer = 12;
 		return true;
 	}
 
 	return false;
 }
 
-bool MacIndy3Gui::Inventory::Slot::updateTimer() {
-	bool ret = Widget::updateTimer();
+bool MacIndy3Gui::Inventory::Slot::updateTimer(int delta) {
+	bool ret = Widget::updateTimer(delta);
 
 	if (ret) {
 		_vm->runInputScript(kInventoryClickArea, _obj, 1);
@@ -892,7 +918,7 @@ bool MacIndy3Gui::Inventory::ScrollButton::handleEvent(Common::Event &event) {
 
 	if (_enabled && _bounds.contains(event.mouse.x, event.mouse.y)) {
 		_redraw = true;
-		_timer = 15;
+		_timer = 12;
 		return true;
 	}
 
@@ -1031,7 +1057,7 @@ void MacIndy3Gui::resetAfterLoad() {
 #endif
 }
 
-void MacIndy3Gui::update() {
+void MacIndy3Gui::update(int delta) {
 	static int lastVerbScript = -1;
 
 	if (_vm->VAR(_vm->VAR_VERB_SCRIPT) != lastVerbScript) {
@@ -1051,7 +1077,7 @@ void MacIndy3Gui::update() {
 	for (Common::HashMap<int, VerbWidget *>::iterator i = _widgets.begin(); i != _widgets.end(); ++i) {
 		VerbWidget *w = i->_value;
 
-		w->updateTimer();
+		w->updateTimer(delta);
 		w->threaten();
 	}
 
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 651234b46ee..b92d55a2dd7 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -53,7 +53,7 @@ public:
 	const Graphics::Font *getFont(int n) { return _fonts[n]; }
 
 	void resetAfterLoad();
-	void update();
+	void update(int delta);
 	void handleEvent(Common::Event &event);
 
 private:
@@ -82,7 +82,7 @@ private:
 
 		virtual void draw() = 0;
 		virtual void undraw() {}
-		virtual bool updateTimer();
+		virtual bool updateTimer(int delta);
 
 		// Primitives
 		void copyRectToScreen(Common::Rect r);
@@ -120,7 +120,7 @@ private:
 		bool handleEvent(Common::Event &event);
 
 		void reset();
-		bool updateTimer();
+		bool updateTimer(int delta);
 		void updateVerb(int verbslot);
 		void draw();
 	};
@@ -157,7 +157,7 @@ private:
 			Slot(int slot, int x, int y, int width, int height);
 			void reset();
 			bool handleEvent(Common::Event &event);
-			bool updateTimer();
+			bool updateTimer(int delta);
 			void draw();
 		};
 
@@ -174,7 +174,7 @@ private:
 		void updateVerb(int verbslot);
 
 		bool handleEvent(Common::Event &event);
-		bool updateTimer();
+		bool updateTimer(int delta);
 
 		void draw();
 	};
diff --git a/engines/scumm/input.cpp b/engines/scumm/input.cpp
index c4c1b018b91..998e78ef4e7 100644
--- a/engines/scumm/input.cpp
+++ b/engines/scumm/input.cpp
@@ -105,6 +105,10 @@ 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->handleEvent(event);
+
 	switch (event.type) {
 	case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
 		if (event.customType >= kScummActionCount) {
@@ -220,10 +224,6 @@ void ScummEngine::parseEvent(Common::Event event) {
 		_mouse.x = event.mouse.x;
 		_mouse.y = event.mouse.y;
 
-		// Handle Mac Indy3 events before scaling the mouse coordinates
-		if (_macIndy3Gui)
-			_macIndy3Gui->handleEvent(event);
-
 		if (_renderMode == Common::kRenderHercA || _renderMode == Common::kRenderHercG) {
 			_mouse.x -= (kHercWidth - _screenWidth * 2) / 2;
 			_mouse.x >>= 1;
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index a554fdce582..9905453f0ea 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -2426,6 +2426,9 @@ Common::Error ScummEngine::go() {
 		// Run the main loop
 		scummLoop(delta);
 
+		if (_macIndy3Gui)
+			_macIndy3Gui->update(delta);
+
 		if (_game.heversion >= 60) {
 			((SoundHE *)_sound)->feedMixer();
 		}
@@ -2464,9 +2467,6 @@ void ScummEngine::waitForTimer(int quarterFrames) {
 		towns_updateGfx();
 #endif
 
-		if (_macIndy3Gui)
-			_macIndy3Gui->update();
-
 		_system->updateScreen();
 		cur = _system->getMillis();
 


Commit: fd63cf734dfb6506a8f09e93785b93b15488d559
    https://github.com/scummvm/scummvm/commit/fd63cf734dfb6506a8f09e93785b93b15488d559
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Hopefully fix Indy 3 Mac verb shortcut keys

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


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 093a5a4a3d2..a2217855600 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -491,12 +491,15 @@ bool MacIndy3Gui::Button::handleEvent(Common::Event &event) {
 	if (!_verbid || !_enabled)
 		return false;
 
-	bool caughtEvent = false;
+	VerbSlot *vs = &_vm->_verbs[_verbslot];
+
+	if (vs->saveid)
+		return false;
 
-	uint8 key = _vm->_verbs[_verbslot].key;
+	bool caughtEvent = false;
 
 	if (event.type == Common::EVENT_KEYDOWN) {
-		if (!event.kbd.hasFlags(Common::KBD_NON_STICKY) && event.kbd.keycode == key) {
+		if (!event.kbd.hasFlags(Common::KBD_NON_STICKY) && event.kbd.keycode == vs->key) {
 			caughtEvent = true;
 		}
 	} else if (event.type == Common::EVENT_LBUTTONDOWN) {
diff --git a/engines/scumm/verbs.cpp b/engines/scumm/verbs.cpp
index 7761715d854..5d0d4b6ce75 100644
--- a/engines/scumm/verbs.cpp
+++ b/engines/scumm/verbs.cpp
@@ -561,13 +561,15 @@ void ScummEngine::checkExecVerbs() {
 			// This is disabled in the SegaCD version as the "vs->key" values setup
 			// by script-17 conflict with the values expected by the generic keyboard
 			// input script. See tracker item #2013.
-			vs = &_verbs[1];
-			for (i = 1; i < _numVerbs; i++, vs++) {
-				if (vs->verbid && vs->saveid == 0 && vs->curmode == 1) {
-					if (_mouseAndKeyboardStat == vs->key) {
-						// Trigger verb as if the user clicked it
-						runInputScript(kVerbClickArea, vs->verbid, 1);
-						return;
+			if (!_macIndy3Gui || !_macIndy3Gui->isVisible()) {
+				vs = &_verbs[1];
+				for (i = 1; i < _numVerbs; i++, vs++) {
+					if (vs->verbid && vs->saveid == 0 && vs->curmode == 1) {
+						if (_mouseAndKeyboardStat == vs->key) {
+							// Trigger verb as if the user clicked it
+							runInputScript(kVerbClickArea, vs->verbid, 1);
+							return;
+						}
 					}
 				}
 			}


Commit: 6bc9ccadef087715db25ac466ec1630368b0ecc4
    https://github.com/scummvm/scummvm/commit/6bc9ccadef087715db25ac466ec1630368b0ecc4
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Fix clicking on Indy 3 Mac GUI sentence line

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


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index a2217855600..1d29f74e9e9 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -889,7 +889,7 @@ void MacIndy3Gui::Inventory::Slot::draw() {
 		int y = _bounds.top - 1;
 		int x = _bounds.left + 4;
 
-		for (uint i = 0; i < _name.size(); i++) {
+		for (uint i = 0; i < _name.size() && x < _bounds.right; i++) {
 			if (_name[i] != '@') {
 				font->drawChar(_surface, _name[i], x, y, fg);
 				x += font->getCharWidth(_name[i]);
diff --git a/engines/scumm/verbs.cpp b/engines/scumm/verbs.cpp
index 5d0d4b6ce75..a6d2a141fdc 100644
--- a/engines/scumm/verbs.cpp
+++ b/engines/scumm/verbs.cpp
@@ -556,20 +556,28 @@ void ScummEngine::checkExecVerbs() {
 		return;
 
 	if (_mouseAndKeyboardStat < MBS_MAX_KEY) {
+		bool ignoreVerbKeys = false;
+
+		// This is disabled in the SegaCD version as the "vs->key" values setup
+		// by script-17 conflict with the values expected by the generic keyboard
+		// input script. See tracker item #2013.
+		if (_game.id == GID_MONKEY && _game.platform == Common::kPlatformSegaCD)
+			ignoreVerbKeys = true;
+
+		// The Mac version of Last Crusade handles verb shortcut keys on
+		// its own, so that is also disabled here.
+		if (_macIndy3Gui && _macIndy3Gui->isVisible())
+			ignoreVerbKeys = true;
+
 		/* Check keypresses */
-		if (!(_game.id == GID_MONKEY && _game.platform == Common::kPlatformSegaCD)) {
-			// This is disabled in the SegaCD version as the "vs->key" values setup
-			// by script-17 conflict with the values expected by the generic keyboard
-			// input script. See tracker item #2013.
-			if (!_macIndy3Gui || !_macIndy3Gui->isVisible()) {
-				vs = &_verbs[1];
-				for (i = 1; i < _numVerbs; i++, vs++) {
-					if (vs->verbid && vs->saveid == 0 && vs->curmode == 1) {
-						if (_mouseAndKeyboardStat == vs->key) {
-							// Trigger verb as if the user clicked it
-							runInputScript(kVerbClickArea, vs->verbid, 1);
-							return;
-						}
+		if (!ignoreVerbKeys) {
+			vs = &_verbs[1];
+			for (i = 1; i < _numVerbs; i++, vs++) {
+				if (vs->verbid && vs->saveid == 0 && vs->curmode == 1) {
+					if (_mouseAndKeyboardStat == vs->key) {
+						// Trigger verb as if the user clicked it
+						runInputScript(kVerbClickArea, vs->verbid, 1);
+						return;
 					}
 				}
 			}
@@ -635,6 +643,9 @@ void ScummEngine::checkExecVerbs() {
 		if (!zone)
 			return;
 
+		if (_macIndy3Gui && _macIndy3Gui->isVisible() && zone->number == kVerbVirtScreen)
+			return;
+
 		over = findVerbAtPos(_mouse.x, _mouse.y);
 		if (over != 0) {
 			// Verb was clicked
@@ -963,9 +974,6 @@ void ScummEngine::verbMouseOver(int verb) {
 }
 
 int ScummEngine::findVerbAtPos(int x, int y) const {
-	if (_macIndy3Gui)
-		return 0;
-
 	if (!_numVerbs)
 		return 0;
 


Commit: 3068bcff72bb7e500bc0ee826ade7a6f4d930fab
    https://github.com/scummvm/scummvm/commit/3068bcff72bb7e500bc0ee826ade7a6f4d930fab
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Removed last bit of pre-restructuring Indy 3 Mac GUI

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 1d29f74e9e9..1d19e21e28c 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -1054,10 +1054,6 @@ void MacIndy3Gui::resetAfterLoad() {
 
 	if (oldSavegame)
 		_vm->VAR(67) /= 2;
-
-#if 0
-	_inventoryOffset = _vm->VAR(67);
-#endif
 }
 
 void MacIndy3Gui::update(int delta) {


Commit: cc8a1ce53da3d46ab38515c916190bfbc8be01aa
    https://github.com/scummvm/scummvm/commit/cc8a1ce53da3d46ab38515c916190bfbc8be01aa
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: The Indy 3 Mac scrollbar is now a widget

Arguably, the buttons should be part of this widget, not part of the
inventory. Maybe.

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 1d19e21e28c..3ede6f6772d 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -392,7 +392,7 @@ MacIndy3Gui::Widget::Widget(int x, int y, int width, int height) {
 void MacIndy3Gui::Widget::reset() {
 	_timer = 0;
 	_enabled = false;
-	_redraw = false;
+	setRedraw(false);
 }
 
 bool MacIndy3Gui::Widget::updateTimer(int delta) {
@@ -462,7 +462,7 @@ void MacIndy3Gui::VerbWidget::updateVerb(int verbslot) {
 	bool enabled = (vs->curmode == 1);
 
 	if (!_visible || _enabled != enabled)
-		_redraw = true;
+		setRedraw(true);
 
 	_verbslot = verbslot;
 	_verbid = vs->verbid;
@@ -474,7 +474,10 @@ void MacIndy3Gui::VerbWidget::draw() {
 	// Clear the area used by the widget
 	fill(_bounds);
 	_visible = true;
-	_redraw = false;
+
+	// Don't copy to screen, because widgets are assumed to keep drawing
+	// after calling this. Also, don't call setRedraw() because there
+	// may be sub-widgets that still need drawing.
 }
 
 void MacIndy3Gui::VerbWidget::undraw() {
@@ -517,7 +520,7 @@ bool MacIndy3Gui::Button::handleEvent(Common::Event &event) {
 	// computer, so we just set something that looks good here.
 
 	if (caughtEvent) {
-		_redraw = true;
+		setRedraw(true);
 		_timer = 12;
 	}
 
@@ -530,7 +533,7 @@ bool MacIndy3Gui::Button::updateTimer(int delta) {
 	if (ret) {
 		if (_visible) {
 			_vm->runInputScript(kVerbClickArea, _verbid, 1);
-			_redraw = true;
+			setRedraw(true);
 		}
 	}
 
@@ -547,7 +550,7 @@ void MacIndy3Gui::Button::updateVerb(int verbslot) {
 	if (_text != (char *)buf) {
 		_text = (char *)buf;
 		_timer = 0;
-		_redraw = true;
+		setRedraw(true);
 	}
 }
 
@@ -615,6 +618,7 @@ void MacIndy3Gui::Button::draw() {
 		}
 	}
 
+	setRedraw(false);
 	copyRectToScreen(_bounds);
 }
 
@@ -667,6 +671,8 @@ MacIndy3Gui::Inventory::Inventory(int x, int y, int width, int height) : MacIndy
 		y += 11;
 	}
 
+	_scrollBar = new ScrollBar(_bounds.right - 20, _bounds.top + 19, 16, 40);
+
 	x = _bounds.right - 20;
 	y = _bounds.top + 4;
 
@@ -678,10 +684,36 @@ MacIndy3Gui::Inventory::~Inventory() {
 	for (int i = 0; i < ARRAYSIZE(_slots); i++)
 		delete _slots[i];
 
+	delete _scrollBar;
+
 	for (int i = 0; i < ARRAYSIZE(_scrollButtons); i++)
 		delete _scrollButtons[i];
 }
 
+void MacIndy3Gui::Inventory::reset() {
+	MacIndy3Gui::Widget::reset();
+
+	for (int i = 0; i < ARRAYSIZE(_slots); i++)
+		_slots[i]->reset();
+
+	_scrollBar->reset();
+
+	for (int i = 0; i < ARRAYSIZE(_scrollButtons); i++)
+		_scrollButtons[i]->reset();
+}
+
+void MacIndy3Gui::Inventory::setRedraw(bool redraw) {
+	MacIndy3Gui::Widget::setRedraw(redraw);
+
+	for (int i = 0; i < ARRAYSIZE(_slots); i++)
+		_slots[i]->setRedraw(redraw);
+
+	_scrollBar->setRedraw(redraw);
+
+	for (int i = 0; i < ARRAYSIZE(_scrollButtons); i++)
+		_scrollButtons[i]->setRedraw(redraw);
+}
+
 void MacIndy3Gui::Inventory::updateVerb(int verbslot) {
 	MacIndy3Gui::VerbWidget::updateVerb(verbslot);
 
@@ -718,13 +750,13 @@ void MacIndy3Gui::Inventory::updateVerb(int verbslot) {
 					if (s->_name != (const char *)name) {
 						s->_name = (const char *)name;
 						s->_timer = 0;
-						s->_redraw = true;
+						s->setRedraw(true);
 					}
 				} else {
 					if (!s->_name.empty()) {
 						s->_name.clear();
 						s->_timer = 0;
-						s->_redraw = true;
+						s->setRedraw(true);
 					}
 				}
 
@@ -741,7 +773,7 @@ void MacIndy3Gui::Inventory::updateVerb(int verbslot) {
 			s->_name.clear();
 			s->_obj = -1;
 			s->_timer = 0;
-			s->_redraw = true;
+			s->setRedraw(true);
 		}
 	}
 }
@@ -773,7 +805,7 @@ bool MacIndy3Gui::Inventory::updateTimer(int delta) {
 		Slot *s = _slots[i];
 
 		if (s->updateTimer(delta)) {
-			s->_redraw = true;
+			s->setRedraw(true);
 			_vm->runInputScript(kInventoryClickArea, s->_obj, 1);
 		}
 	}
@@ -782,7 +814,7 @@ bool MacIndy3Gui::Inventory::updateTimer(int delta) {
 		ScrollButton *b = _scrollButtons[i];
 
 		if (b->updateTimer(delta)) {
-			b->_redraw = true;
+			b->setRedraw(true);
 		}
 	}
 
@@ -795,12 +827,8 @@ void MacIndy3Gui::Inventory::draw() {
 
 		MacIndy3Gui::VerbWidget::draw();
 
-		for (int i = 0; i < ARRAYSIZE(_slots); i++)
-			_slots[i]->reset();
-
 		drawShadowBox(_bounds);
 		drawShadowFrame(Common::Rect(_bounds.left + 4, _bounds.top + 4, _bounds.right - 22, _bounds.bottom - 4), 0, 15);
-		drawShadowFrame(Common::Rect(_bounds.right - 20, _bounds.top + 19, _bounds.right - 4, _bounds.bottom - 19), 0, 255);
 
 		for (int i = 0; i < ARRAYSIZE(_scrollButtons); i++) {
 			ScrollButton *s = _scrollButtons[i];
@@ -809,7 +837,6 @@ void MacIndy3Gui::Inventory::draw() {
 			drawShadowFrame(s->_bounds, 15, 255);
 			drawBitmap(s->_bounds, arrow, 0);
 			s->draw();
-			s->_redraw = true;
 		}
 
 		copyRectToScreen(_bounds);
@@ -828,8 +855,12 @@ void MacIndy3Gui::Inventory::draw() {
 			_slots[i]->draw();
 	}
 
+	_scrollBar->draw();
+
 	for (int i = 0; i < ARRAYSIZE(_scrollButtons); i++)
 		_scrollButtons[i]->draw();
+
+	setRedraw(false);
 }
 
 MacIndy3Gui::Inventory::Slot::Slot(int slot, int x, int y, int width, int height) : MacIndy3Gui::Widget(x, y, width, height) {
@@ -847,7 +878,7 @@ bool MacIndy3Gui::Inventory::Slot::handleEvent(Common::Event &event) {
 		return false;
 
 	if (_obj != -1 && _bounds.contains(event.mouse.x, event.mouse.y)) {
-		_redraw = true;
+		setRedraw(true);
 		_timer = 12;
 		return true;
 	}
@@ -860,7 +891,7 @@ bool MacIndy3Gui::Inventory::Slot::updateTimer(int delta) {
 
 	if (ret) {
 		_vm->runInputScript(kInventoryClickArea, _obj, 1);
-		_redraw = true;
+		setRedraw(true);
 	}
 
 	return ret;
@@ -897,8 +928,28 @@ void MacIndy3Gui::Inventory::Slot::draw() {
 		}
 	}
 
-	_redraw = false;
+	setRedraw(false);
+	copyRectToScreen(_bounds);
+}
+
+MacIndy3Gui::Inventory::ScrollBar::ScrollBar(int x, int y, int width, int height) : MacIndy3Gui::Widget(x, y, width, height) {
+}
+
+bool MacIndy3Gui::Inventory::ScrollBar::handleEvent(Common::Event &event) {
+	if (event.type != Common::EVENT_LBUTTONDOWN)
+		return false;
+
+	return false;
+}
+
+void MacIndy3Gui::Inventory::ScrollBar::draw() {
+	if (!_redraw)
+		return;
+
+	debug("Drawing scrollbar");
+	drawShadowFrame(_bounds, 0, 255);
 	copyRectToScreen(_bounds);
+	setRedraw(false);
 }
 
 const uint16 MacIndy3Gui::Inventory::ScrollButton::_upArrow[16] = {
@@ -920,7 +971,7 @@ bool MacIndy3Gui::Inventory::ScrollButton::handleEvent(Common::Event &event) {
 		return false;
 
 	if (_enabled && _bounds.contains(event.mouse.x, event.mouse.y)) {
-		_redraw = true;
+		setRedraw(true);
 		_timer = 12;
 		return true;
 	}
@@ -934,12 +985,13 @@ void MacIndy3Gui::Inventory::ScrollButton::draw() {
 
 	debug("Drawing inventory arrow %d", _direction);
 
-	_redraw = false;
+	setRedraw(false);
 
 	const uint16 *arrow = (_direction == kScrollUp) ? _upArrow : _downArrow;
 	byte color = _timer ? 0 : 15;
 
 	drawBitmap(_bounds, arrow, color);
+	setRedraw(false);
 	copyRectToScreen(_bounds);
 }
 
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index b92d55a2dd7..cd1462ba0d0 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -78,7 +78,8 @@ private:
 		Widget(int x, int y, int width, int height);
 		virtual ~Widget() {}
 
-		void reset();
+		virtual void reset();
+		virtual void setRedraw(bool redraw) { _redraw = redraw; }
 
 		virtual void draw() = 0;
 		virtual void undraw() {}
@@ -132,6 +133,14 @@ private:
 
 	class Inventory : public VerbWidget {
 	private:
+		class ScrollBar : public Widget {
+		public:
+			ScrollBar(int x, int y, int width, int height);
+
+			bool handleEvent(Common::Event &event);
+			void draw();
+		};
+
 		class ScrollButton : public Widget {
 		private:
 			static const uint16 _upArrow[16];
@@ -162,6 +171,7 @@ private:
 		};
 
 		Slot *_slots[6];
+		ScrollBar *_scrollBar;
 		ScrollButton *_scrollButtons[2];
 
 		static const uint16 _upArrow[16];
@@ -171,6 +181,9 @@ private:
 		Inventory(int x, int y, int width, int height);
 		~Inventory();
 
+		void reset();
+		void setRedraw(bool redraw);
+
 		void updateVerb(int verbslot);
 
 		bool handleEvent(Common::Event &event);


Commit: 7186851de64478a583e25cd2e9557a6771328eef
    https://github.com/scummvm/scummvm/commit/7186851de64478a583e25cd2e9557a6771328eef
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Fix Indy 3 Mac GUI inventory widget being undrawn over and over

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 3ede6f6772d..01f979f964f 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -481,6 +481,7 @@ void MacIndy3Gui::VerbWidget::draw() {
 }
 
 void MacIndy3Gui::VerbWidget::undraw() {
+	debug("Undrawing button [%d]", _verbid);
 	fill(_bounds);
 	copyRectToScreen(_bounds);
 }
@@ -691,7 +692,7 @@ MacIndy3Gui::Inventory::~Inventory() {
 }
 
 void MacIndy3Gui::Inventory::reset() {
-	MacIndy3Gui::Widget::reset();
+	MacIndy3Gui::VerbWidget::reset();
 
 	for (int i = 0; i < ARRAYSIZE(_slots); i++)
 		_slots[i]->reset();


Commit: e40c3a2fbbd76239217e9f07d117bd4bd6fcde55
    https://github.com/scummvm/scummvm/commit/e40c3a2fbbd76239217e9f07d117bd4bd6fcde55
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: More Indy 3 Mac GUI cleanup + inventory scrolling

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 01f979f964f..d0b8827809d 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -358,25 +358,35 @@ Common::KeyState ScummEngine::mac_showOldStyleBannerAndPause(const char *msg, in
 	return ks;
 }
 
-/*
- * The infamous verb GUI for the Macintosh version of Indiana Jones and the
- * Last Crusade.
- *
- * 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
- * haven't been able to find in any of the early scripts of the game.
- *
- * But the design goal here is to keep things as simple as possible, even if
- * that means using brute force. So the GUI figures out which verbs are active
- * by scanning all the verbs, and which objects are in the inventory by
- * scanning all objects.
- *
- * We used to coerce the Mac verb GUI into something that looked like the
- * GUI from all other versions. This used a number of variables and hard-coded
- * verbs. The only thing still used of all this is variable 67, to keep track
- * of the inventory scroll position. The fake verbs are removed when loading
- * old savegames, but the variables are assumed to be harmless.
- */
+// ===========================================================================
+// The infamous verb GUI for the Macintosh version of Indiana Jones and the
+// Last Crusade.
+//
+// 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
+// haven't been able to find in any of the early scripts of the game.
+//
+// But the design goal here is to keep things as simple as possible, even if
+// that means using brute force. So the GUI figures out which verbs are active
+// by scanning all the verbs, and which objects are in the inventory by
+// scanning all objects.
+//
+// We used to coerce the Mac verb GUI into something that looked like the
+// GUI from all other versions. This used a number of variables and hard-coded
+// verbs. The only thing still used of all this is variable 67, to keep track
+// of the inventory scroll position. The fake verbs are removed when loading
+// old savegames, but the variables are assumed to be harmless.
+// ===========================================================================
+
+#define DEBUG_VERB_SCRIPTS		1
+
+#define WIDGET_TIMER_JIFFIES	12
+#define REPEAT_TIMER_JIFFIES	18
+
+// ---------------------------------------------------------------------------
+// The basic building block of the GUI is the Widget. It has a bounding box, a
+// timer, a couple of drawing primitives, etc.
+// ---------------------------------------------------------------------------
 
 ScummEngine *MacIndy3Gui::Widget::_vm = nullptr;
 Graphics::Surface *MacIndy3Gui::Widget::_surface = nullptr;
@@ -390,13 +400,13 @@ MacIndy3Gui::Widget::Widget(int x, int y, int width, int height) {
 }
 
 void MacIndy3Gui::Widget::reset() {
-	_timer = 0;
+	clearTimer();
 	_enabled = false;
 	setRedraw(false);
 }
 
 bool MacIndy3Gui::Widget::updateTimer(int delta) {
-	if (_timer == 0)
+	if (!hasTimer())
 		return false;
 
 	if (delta > _timer)
@@ -414,42 +424,53 @@ void MacIndy3Gui::Widget::fill(Common::Rect r) {
 	_gui->fill(r);
 }
 
-void MacIndy3Gui::Widget::drawBitmap(Common::Rect r, const uint16 *bitmap, byte color) {
+void MacIndy3Gui::Widget::drawBitmap(Common::Rect r, const uint16 *bitmap, Color color) {
 	_gui->drawBitmap(r, bitmap, color);
 }
 
+// The shadow box is the basic outline of the verb buttons and the inventory
+// widget. A slightly rounded rectangle with a shadow and a highlight.
+
 void MacIndy3Gui::Widget::drawShadowBox(Common::Rect r) {
-	_surface->hLine(r.left + 1, r.top, r.right - 3, 0);
-	_surface->hLine(r.left + 1, r.bottom - 2, r.right - 3, 0);
-	_surface->vLine(r.left, r.top + 1, r.bottom - 3, 0);
-	_surface->vLine(r.right - 2, r.top + 1, r.bottom - 3, 0);
+	_surface->hLine(r.left + 1, r.top, r.right - 3, kBlack);
+	_surface->hLine(r.left + 1, r.bottom - 2, r.right - 3, kBlack);
+	_surface->vLine(r.left, r.top + 1, r.bottom - 3, kBlack);
+	_surface->vLine(r.right - 2, r.top + 1, r.bottom - 3, kBlack);
 
-	_surface->hLine(r.left + 2, r.bottom - 1, r.right - 1, 0);
-	_surface->vLine(r.right - 1, r.top + 2, r.bottom - 2, 0);
+	_surface->hLine(r.left + 2, r.bottom - 1, r.right - 1, kBlack);
+	_surface->vLine(r.right - 1, r.top + 2, r.bottom - 2, kBlack);
 
-	_surface->hLine(r.left + 1, r.top + 1, r.right - 3, 15);
-	_surface->vLine(r.left + 1, r.top + 2, r.bottom - 3, 15);
+	_surface->hLine(r.left + 1, r.top + 1, r.right - 3, kWhite);
+	_surface->vLine(r.left + 1, r.top + 2, r.bottom - 3, kWhite);
 }
 
-void MacIndy3Gui::Widget::drawShadowFrame(Common::Rect r, byte shadowColor, byte fillColor) {
-	_surface->hLine(r.left, r.top, r.right - 1, 0);
-	_surface->hLine(r.left, r.bottom - 1, r.right - 1, 0);
-	_surface->vLine(r.left, r.top + 1, r.bottom - 2, 0);
-	_surface->vLine(r.right - 1, r.top + 1, r.bottom - 2, 0);
+// The shadow frame is a rectangle with a highlight. It can be filled or
+// unfilled.
+
+void MacIndy3Gui::Widget::drawShadowFrame(Common::Rect r, Color shadowColor, Color fillColor) {
+	_surface->hLine(r.left, r.top, r.right - 1, kBlack);
+	_surface->hLine(r.left, r.bottom - 1, r.right - 1, kBlack);
+	_surface->vLine(r.left, r.top + 1, r.bottom - 2, kBlack);
+	_surface->vLine(r.right - 1, r.top + 1, r.bottom - 2, kBlack);
 
 	_surface->hLine(r.left + 1, r.top + 1, r.right - 2, shadowColor);
 	_surface->vLine(r.left + 1, r.top + 2, r.bottom - 2, shadowColor);
 
-	if (fillColor != 255) {
+	if (fillColor != kTransparency) {
 		Common::Rect fillRect(r.left + 2, r.top + 2, r.right - 1, r.bottom - 1);
 
-		if (fillColor == 0)
+		if (fillColor == kBackground)
 			fill(fillRect);
 		else
 			_surface->fillRect(fillRect, fillColor);
 	}
 }
 
+// ---------------------------------------------------------------------------
+// The VerbWidget is what the user interacts with. Each one is connected to a
+// verb id and slot. A VerbWidget can consist of several Widgets.
+// ---------------------------------------------------------------------------
+
 void MacIndy3Gui::VerbWidget::reset() {
 	MacIndy3Gui::Widget::reset();
 	_verbslot = -1;
@@ -481,18 +502,25 @@ void MacIndy3Gui::VerbWidget::draw() {
 }
 
 void MacIndy3Gui::VerbWidget::undraw() {
-	debug("Undrawing button [%d]", _verbid);
+	debug(1, "VerbWidget: Undrawing [%d]", _verbid);
+
+	_visible = false;
 	fill(_bounds);
 	copyRectToScreen(_bounds);
 }
 
+// ---------------------------------------------------------------------------
+// The most common type of VerbWidget is the button, which is used for verbs
+// and conversation options.
+// ---------------------------------------------------------------------------
+
 void MacIndy3Gui::Button::reset() {
 	MacIndy3Gui::VerbWidget::reset();
 	_text.clear();
 }
 
 bool MacIndy3Gui::Button::handleEvent(Common::Event &event) {
-	if (!_verbid || !_enabled)
+	if (!_enabled || !_verbid)
 		return false;
 
 	VerbSlot *vs = &_vm->_verbs[_verbslot];
@@ -507,10 +535,7 @@ bool MacIndy3Gui::Button::handleEvent(Common::Event &event) {
 			caughtEvent = true;
 		}
 	} else if (event.type == Common::EVENT_LBUTTONDOWN) {
-		int x = event.mouse.x;
-		int y = event.mouse.y;
-
-		if (_bounds.contains(x, y))
+		if (_bounds.contains(event.mouse))
 			caughtEvent = true;
 	}
 
@@ -522,7 +547,7 @@ bool MacIndy3Gui::Button::handleEvent(Common::Event &event) {
 
 	if (caughtEvent) {
 		setRedraw(true);
-		_timer = 12;
+		setTimer(WIDGET_TIMER_JIFFIES);
 	}
 
 	return caughtEvent;
@@ -550,7 +575,7 @@ void MacIndy3Gui::Button::updateVerb(int verbslot) {
 	_vm->convertMessageToString(ptr, buf, sizeof(buf));
 	if (_text != (char *)buf) {
 		_text = (char *)buf;
-		_timer = 0;
+		clearTimer();
 		setRedraw(true);
 	}
 }
@@ -559,11 +584,11 @@ void MacIndy3Gui::Button::draw() {
 	if (!_redraw)
 		return;
 
-	debug("Drawing button [%d] %s", _verbid, _text.c_str());
+	debug(1, "Button: Drawing [%d] %s", _verbid, _text.c_str());
 
 	MacIndy3Gui::VerbWidget::draw();
 
-	if (_timer == 0) {
+	if (!hasTimer()) {
 		drawShadowBox(_bounds);
 	} else {
 		// I have only been able to capture a screenshot of the pressed
@@ -576,14 +601,20 @@ void MacIndy3Gui::Button::draw() {
 		// rounded while the lower right is not. I'm going to assume
 		// that the shadow is always drawn, and the rest of the button
 		// is just shifted down to the right. That would make the other
-		// two corners rounded.
-		_surface->hLine(_bounds.left + 2, _bounds.top + 1, _bounds.right - 2, 0);
-		_surface->hLine(_bounds.left + 2, _bounds.bottom - 1, _bounds.right - 1, 0);
-		_surface->vLine(_bounds.left + 1, _bounds.top + 2, _bounds.bottom - 2, 0);
-		_surface->vLine(_bounds.right - 1, _bounds.top + 2, _bounds.bottom - 2, 0);
-
-		_surface->hLine(_bounds.left + 2, _bounds.top + 2, _bounds.right - 2, 15);
-		_surface->vLine(_bounds.left + 2, _bounds.top + 3, _bounds.bottom - 2, 15);
+		// two corners rounded, so only the lower right one isn't.
+
+		int x0 = _bounds.left + 1;
+		int x1 = _bounds.right - 1;
+		int y0 = _bounds.top + 1;
+		int y1 = _bounds.bottom - 1;
+
+		_surface->hLine(x0 + 1, y0, x1 - 1, kBlack);
+		_surface->hLine(x0 + 1, y1, x1, kBlack);
+		_surface->vLine(x0, y0 + 1, y1 - 1, kBlack);
+		_surface->vLine(x1, y0 + 1, y1 - 1, kBlack);
+
+		_surface->hLine(x0 + 1, y0 + 1, x1 - 1, kWhite);
+		_surface->vLine(x0 + 1, y0 + 2, y1 - 1, kWhite);
 	}
 
 	// The text is drawn centered. Based on experimentation, I think the
@@ -593,8 +624,8 @@ void MacIndy3Gui::Button::draw() {
 	// This gives us pixel perfect rendering for the English verbs.
 
 	if (!_text.empty()) {
-		const Graphics::Font *boldFont = _gui->getFont(1);
-		const Graphics::Font *outlineFont = _gui->getFont(2);
+		const Graphics::Font *boldFont = _gui->getFont(kBold);
+		const Graphics::Font *outlineFont = _gui->getFont(kOutline);
 
 		int stringWidth = 0;
 		for (uint i = 0; i < _text.size(); i++)
@@ -602,9 +633,9 @@ void MacIndy3Gui::Button::draw() {
 
 		int x = (_bounds.left + (_bounds.width() - 1 - stringWidth) / 2) - 1;
 		int y = _bounds.top + 2;
-		int color = _enabled ? 15 : 0;
+		Color color = _enabled ? kWhite : kBlack;
 
-		if (_timer) {
+		if (hasTimer()) {
 			x++;
 			y++;
 		}
@@ -612,7 +643,7 @@ void MacIndy3Gui::Button::draw() {
 		for (uint i = 0; i < _text.size() && x < _bounds.right; i++) {
 			if (x >= _bounds.left) {
 				if (_enabled)
-					outlineFont->drawChar(_surface, _text[i], x, y, 0);
+					outlineFont->drawChar(_surface, _text[i], x, y, kBlack);
 				boldFont->drawChar(_surface, _text[i], x + 1, y, color);
 			}
 			x += boldFont->getCharWidth(_text[i]);
@@ -623,28 +654,12 @@ void MacIndy3Gui::Button::draw() {
 	copyRectToScreen(_bounds);
 }
 
-// Desired behavior:
-//
-// The drag handle of the scrollbar never changes size. It's only drawn when
-// there are enough inventory items to scroll.
-//
-// The size of the scroll handle is fixed, not scaled to indicate the number of
-// objects in your inventory.
-//
-// The exact positions of the scroll handle are not yet known. Probably just a
-// simple calculation.
-//
-// Clicking on an arrow scrolls up or down by one row. Clicking and holding
-// scrolls the inventory. The time between scrolling is constant, i.e. the
-// first delay is not different. The delay seems to depend on the speed of the
-// Mac, so pick something that feels right.
-//
-// Clicking above or below the handle scrolls up or down by - probably - one
-// page. I've never seen the inventory full enough for this to mean anything
-// else than scrolling to the top or bottom.
-//
-// Dragging the handle is not possible. Clicking on the handle is probably
-// indistinguishable from clicking above or below.
+// ---------------------------------------------------------------------------
+// Unlike in the DOS version, where each inventory object shown on screen is
+// its own verb, in the Macintosh version the entire Inventory widget is one
+// single verb. It consists of the widget itself, the inventory slots, and
+// the scrollbar.
+// ---------------------------------------------------------------------------
 
 const uint16 MacIndy3Gui::Inventory::_upArrow[16] = {
 	0x0000, 0x0000, 0x0000, 0x0080,	0x0140, 0x0220, 0x0410, 0x0808,
@@ -672,10 +687,9 @@ MacIndy3Gui::Inventory::Inventory(int x, int y, int width, int height) : MacIndy
 		y += 11;
 	}
 
-	_scrollBar = new ScrollBar(_bounds.right - 20, _bounds.top + 19, 16, 40);
-
 	x = _bounds.right - 20;
-	y = _bounds.top + 4;
+
+	_scrollBar = new ScrollBar(x, _bounds.top + 19, 16, 40);
 
 	_scrollButtons[0] = new ScrollButton(x, _bounds.top + 4, 16, 16, kScrollUp);
 	_scrollButtons[1] = new ScrollButton(x, _bounds.bottom - 20, 16, 16, kScrollDown);
@@ -715,87 +729,57 @@ void MacIndy3Gui::Inventory::setRedraw(bool redraw) {
 		_scrollButtons[i]->setRedraw(redraw);
 }
 
-void MacIndy3Gui::Inventory::updateVerb(int verbslot) {
-	MacIndy3Gui::VerbWidget::updateVerb(verbslot);
-
-	int owner = _vm->VAR(_vm->VAR_EGO);
-
-	int invCount = _vm->getInventoryCount(owner);
-	int invOffset = _vm->VAR(67);
-
-	// The scroll offset must be non-negative and if there are six or less
-	// items in the inventory, the inventory is fixed in the top position.
-
-	if (invOffset < 0 || invCount <= 6)
-		invOffset = 0;
-
-	// If there are more than six items in the inventory, clamp the scroll
-	// offset to be at most invCount - 6.
+bool MacIndy3Gui::Inventory::handleEvent(Common::Event &event) {
+	if (!_enabled || !_verbid || event.type != Common::EVENT_LBUTTONDOWN)
+		return false;
 
-	if (invCount > 6 && invOffset >= invCount - 6)
-		invOffset = invCount - 6;
+	for (int i = 0; i < ARRAYSIZE(_slots); i++) {
+		if (_slots[i]->handleEvent(event))
+			return true;
+	}
 
-	_vm->VAR(67) = invOffset;
+	for (int i = 0; i < ARRAYSIZE(_scrollButtons); i++) {
+		ScrollButton *b = _scrollButtons[i];
 
-	int invSlot = 0;
-	invCount = 0;
+		// Scrolling is done by one object at a time, though you can
+		// hold down the mouse button to repeat.
 
-	for (int i = 0; i < _vm->_numInventory && invSlot < ARRAYSIZE(_slots); i++) {
-		int obj = _vm->_inventory[i];
-		if (obj && _vm->getOwner(obj) == owner) {
-			if (++invCount >= invOffset) {
-				Slot *s = _slots[invSlot];
-				const byte *name = _vm->getObjOrActorName(obj);
-
-				if (name) {
-					if (s->_name != (const char *)name) {
-						s->_name = (const char *)name;
-						s->_timer = 0;
-						s->setRedraw(true);
-					}
-				} else {
-					if (!s->_name.empty()) {
-						s->_name.clear();
-						s->_timer = 0;
-						s->setRedraw(true);
-					}
-				}
-
-				s->_obj = obj;
-				invSlot++;
-			}
+		if (b->handleEvent(event)) {
+			_scrollBar->moveInvOffset((b->_direction == kScrollUp) ? -1 : 1);
+			return true;
 		}
 	}
 
-	for (int i = invSlot; i < ARRAYSIZE(_slots); i++) {
-		Slot *s = _slots[i];
+	if (_scrollBar->handleEvent(event))
+		return true;
 
-		if (!s->_name.empty()) {
-			s->_name.clear();
-			s->_obj = -1;
-			s->_timer = 0;
-			s->setRedraw(true);
-		}
-	}
+	return false;
 }
 
-bool MacIndy3Gui::Inventory::handleEvent(Common::Event &event) {
-	if (!_verbid || !_enabled)
-		return false;
-
-	if (event.type != Common::EVENT_LBUTTONDOWN)
+bool MacIndy3Gui::Inventory::handleMouseHeld(Common::Point &pressed, Common::Point &held) {
+	if (!_enabled || !_verbid)
 		return false;
 
 	for (int i = 0; i < ARRAYSIZE(_slots); i++) {
-		if (_slots[i]->handleEvent(event))
+		if (_slots[i]->handleMouseHeld(pressed, held))
 			return true;
 	}
 
 	for (int i = 0; i < ARRAYSIZE(_scrollButtons); i++) {
-		if (_scrollButtons[i]->handleEvent(event))
+		ScrollButton *b = _scrollButtons[i];
+
+		if (b->handleMouseHeld(pressed, held)) {
+			_scrollBar->moveInvOffset((b->_direction == kScrollUp) ? -1 : 1);
 			return true;
+		}
 	}
 
+	// It would be possible to handle dragging the scrollbar handle, but
+	// the original didn't.
+
+	if (_scrollBar->handleMouseHeld(pressed, held))
+		return true;
+
 	return false;
 }
 
@@ -807,7 +791,7 @@ bool MacIndy3Gui::Inventory::updateTimer(int delta) {
 
 		if (s->updateTimer(delta)) {
 			s->setRedraw(true);
-			_vm->runInputScript(kInventoryClickArea, s->_obj, 1);
+			_vm->runInputScript(kInventoryClickArea, s->getObject(), 1);
 		}
 	}
 
@@ -822,21 +806,78 @@ bool MacIndy3Gui::Inventory::updateTimer(int delta) {
 	return false;
 }
 
+void MacIndy3Gui::Inventory::updateVerb(int verbslot) {
+	MacIndy3Gui::VerbWidget::updateVerb(verbslot);
+
+	int owner = _vm->VAR(_vm->VAR_EGO);
+
+	int invCount = _vm->getInventoryCount(owner);
+	int invOffset = _gui->getInventoryScrollOffset();
+
+	// The scroll offset must be non-negative and if there are six or less
+	// items in the inventory, the inventory is fixed in the top position.
+
+	if (invOffset < 0 || invCount <= 6)
+		invOffset = 0;
+
+	// If there are more than six items in the inventory, clamp the scroll
+	// offset to be at most invCount - 6.
+
+	if (invCount > 6 && invOffset >= invCount - 6)
+		invOffset = invCount - 6;
+
+	_scrollButtons[0]->setEnabled(invCount > 0 && invOffset > 0);
+	_scrollButtons[1]->setEnabled(invCount > 0 && invOffset + 6 < invCount);
+
+	_scrollBar->setEnabled(invCount > 6);
+	_scrollBar->setCounters(invCount, invOffset);
+
+	_gui->setInventoryScrollOffset(invOffset);
+
+	int invSlot = 0;
+
+	// Assign the objects to the inventory slots
+
+	for (int i = 0; i < _vm->_numInventory && invSlot < ARRAYSIZE(_slots); i++) {
+		int obj = _vm->_inventory[i];
+		if (obj && _vm->getOwner(obj) == owner) {
+			if (--invOffset < 0) {
+				_slots[invSlot]->setObject(obj);
+				invSlot++;
+			}
+		}
+	}
+
+	// Clear the remaining slots
+
+	for (int i = invSlot; i < ARRAYSIZE(_slots); i++) {
+		Slot *s = _slots[i];
+
+		if (s->hasName()) {
+			s->clearName();
+			s->clearObject();
+			s->setEnabled(false);
+			s->clearTimer();
+			s->setRedraw(true);
+		}
+	}
+}
+
 void MacIndy3Gui::Inventory::draw() {
 	if (_redraw) {
-		debug("Drawing button [%d] Inventory", _verbid);
+		debug(1, "Inventory: Drawing [%d]", _verbid);
 
 		MacIndy3Gui::VerbWidget::draw();
 
 		drawShadowBox(_bounds);
-		drawShadowFrame(Common::Rect(_bounds.left + 4, _bounds.top + 4, _bounds.right - 22, _bounds.bottom - 4), 0, 15);
+		drawShadowFrame(Common::Rect(_bounds.left + 4, _bounds.top + 4, _bounds.right - 22, _bounds.bottom - 4), kBlack, kWhite);
 
 		for (int i = 0; i < ARRAYSIZE(_scrollButtons); i++) {
 			ScrollButton *s = _scrollButtons[i];
 			const uint16 *arrow = (s->_direction == kScrollUp) ? _upArrow : _downArrow;
 
-			drawShadowFrame(s->_bounds, 15, 255);
-			drawBitmap(s->_bounds, arrow, 0);
+			drawShadowFrame(s->_bounds, kWhite, kTransparency);
+			drawBitmap(s->_bounds, arrow, kBlack);
 			s->draw();
 		}
 
@@ -847,12 +888,12 @@ void MacIndy3Gui::Inventory::draw() {
 	// there should really only be one at a time) last.
 
 	for (int i = 0; i < ARRAYSIZE(_slots); i++) {
-		if (!_slots[i]->_timer)
+		if (!_slots[i]->hasTimer())
 			_slots[i]->draw();
 	}
 
 	for (int i = 0; i < ARRAYSIZE(_slots); i++) {
-		if (_slots[i]->_timer)
+		if (_slots[i]->hasTimer())
 			_slots[i]->draw();
 	}
 
@@ -862,8 +903,13 @@ void MacIndy3Gui::Inventory::draw() {
 		_scrollButtons[i]->draw();
 
 	setRedraw(false);
+	// Everything has already been copied to the screen
 }
 
+// ---------------------------------------------------------------------------
+// Inventory::Slot is a widget for a single inventory object.
+// ---------------------------------------------------------------------------
+
 MacIndy3Gui::Inventory::Slot::Slot(int slot, int x, int y, int width, int height) : MacIndy3Gui::Widget(x, y, width, height) {
 	_slot = slot;
 }
@@ -871,16 +917,35 @@ MacIndy3Gui::Inventory::Slot::Slot(int slot, int x, int y, int width, int height
 void MacIndy3Gui::Inventory::Slot::reset() {
 	Widget::reset();
 	_name.clear();
-	_obj = -1;
+	clearObject();
+}
+
+void MacIndy3Gui::Inventory::Slot::setObject(int obj) {
+	_obj = obj;
+	setEnabled(true);
+
+	const byte *name = _vm->getObjOrActorName(obj);
+
+	if (name) {
+		if (_name != (const char *)name) {
+			_name = (const char *)name;
+			clearTimer();
+			setRedraw(true);
+		}
+	} else if (hasName()) {
+		clearName();
+		clearTimer();
+		setRedraw(true);
+	}
 }
 
 bool MacIndy3Gui::Inventory::Slot::handleEvent(Common::Event &event) {
-	if (event.type != Common::EVENT_LBUTTONDOWN)
+	if (!_enabled || event.type != Common::EVENT_LBUTTONDOWN)
 		return false;
 
-	if (_obj != -1 && _bounds.contains(event.mouse.x, event.mouse.y)) {
+	if (_bounds.contains(event.mouse)) {
 		setRedraw(true);
-		_timer = 12;
+		setTimer(WIDGET_TIMER_JIFFIES);
 		return true;
 	}
 
@@ -891,7 +956,7 @@ bool MacIndy3Gui::Inventory::Slot::updateTimer(int delta) {
 	bool ret = Widget::updateTimer(delta);
 
 	if (ret) {
-		_vm->runInputScript(kInventoryClickArea, _obj, 1);
+		_vm->runInputScript(kInventoryClickArea, getObject(), 1);
 		setRedraw(true);
 	}
 
@@ -902,21 +967,21 @@ void MacIndy3Gui::Inventory::Slot::draw() {
 	if (!_redraw)
 		return;
 
-	debug("Drawing inventory slot: [%d] %s", _slot, _name.c_str());
-	int fg, bg;
+	debug(1, "Inventory::Slot: Drawing [%d] %s", _slot, _name.c_str());
+	Color fg, bg;
 
-	if (_timer) {
-		fg = 15;
-		bg = 0;
+	if (hasTimer()) {
+		fg = kWhite;
+		bg = kBlack;
 	} else {
-		fg = 0;
-		bg = 15;
+		fg = kBlack;
+		bg = kWhite;
 	}
 
 	_surface->fillRect(_bounds, bg);
 
-	if (!_name.empty()) {
-		const Graphics::Font *font = _gui->getFont(0);
+	if (hasName()) {
+		const Graphics::Font *font = _gui->getFont(kRegular);
 
 		int y = _bounds.top - 1;
 		int x = _bounds.left + 4;
@@ -933,26 +998,108 @@ void MacIndy3Gui::Inventory::Slot::draw() {
 	copyRectToScreen(_bounds);
 }
 
+// ---------------------------------------------------------------------------
+// Inventory::ScrollBar is the slider which shows if the inventory contains
+// more objects than are visible on screen.
+// ---------------------------------------------------------------------------
+
 MacIndy3Gui::Inventory::ScrollBar::ScrollBar(int x, int y, int width, int height) : MacIndy3Gui::Widget(x, y, width, height) {
 }
 
 bool MacIndy3Gui::Inventory::ScrollBar::handleEvent(Common::Event &event) {
-	if (event.type != Common::EVENT_LBUTTONDOWN)
+	if (!_enabled || event.type != Common::EVENT_LBUTTONDOWN)
 		return false;
 
+	// I'm guessing that clicking on the scrollbar is supposed to scroll one
+	// page at a time, but I haven't had enough inventory items for this to
+	// mean anything other than scrolling to the top or the bottom.
+	//
+	// The direction depends on if you click above or below the handle.
+	// There is nothing special about clicking the handle itself.
+
+	if (_bounds.contains(event.mouse)) {
+		if (event.mouse.y < _bounds.top + getHandlePosition() + 4)
+			moveInvOffset(-6);
+		else
+			moveInvOffset(6);
+	}
+
 	return false;
 }
 
+void MacIndy3Gui::Inventory::ScrollBar::setCounters(int invCount, int invOffset) {
+	if (invCount != _invCount || invOffset != _invOffset)
+		setRedraw(true);
+
+	_invCount = invCount;
+	_invOffset = invOffset;
+}
+
+void MacIndy3Gui::Inventory::ScrollBar::moveInvOffset(int offset) {
+	int newOffset = _invOffset + offset;
+
+	if (newOffset < 0)
+		newOffset = 0;
+	else if (newOffset > _invCount - 6)
+		newOffset = _invCount - 6;
+
+	if (newOffset != _invOffset) {
+		_invOffset = newOffset;
+		_gui->setInventoryScrollOffset(_invOffset);
+		setRedraw(true);
+	}
+}
+
+int MacIndy3Gui::Inventory::ScrollBar::getHandlePosition() {
+	if (_invOffset == 0)
+		return 0;
+
+	// Hopefully this matches the original scroll handle position.
+
+	int maxPos = _bounds.height() - 8;
+
+	if (_invOffset >= _invCount - 6)
+		return maxPos;
+
+	return maxPos * _invOffset / (_invCount - 6);
+}
+
+void MacIndy3Gui::Inventory::ScrollBar::reset() {
+	MacIndy3Gui::Widget::reset();
+	_invCount = 0;
+	_invOffset = 0;
+}
+
 void MacIndy3Gui::Inventory::ScrollBar::draw() {
 	if (!_redraw)
 		return;
 
-	debug("Drawing scrollbar");
-	drawShadowFrame(_bounds, 0, 255);
-	copyRectToScreen(_bounds);
+	debug(1, "Inventory::Scrollbar: Drawing");
+	drawShadowFrame(_bounds, kBlack, kBackground);
+
+	// The scrollbar handle is only drawn when there are enough inventory
+	// objects to scroll.
+
+	if (_enabled) {
+		debug(1, "Inventory::Scrollbar: Drawing handle");
+
+		int y = _bounds.top + getHandlePosition();
+
+		// The height of the scrollbar handle never changes, regardless
+		// of how many items you are carrying.
+		drawShadowFrame(Common::Rect(_bounds.left, y, _bounds.right, y + 8), kWhite, kTransparency);
+	}
+
 	setRedraw(false);
+	copyRectToScreen(_bounds);
 }
 
+// ---------------------------------------------------------------------------
+// Inventory::ScrollButton is the buttons used to scroll the inventory up and
+// down. They're only responsible for drawing the filled part of the arrow,
+// since the rest of the buttons are drawn by the Inventory widget itself.
+// ---------------------------------------------------------------------------
+
 const uint16 MacIndy3Gui::Inventory::ScrollButton::_upArrow[16] = {
 	0x0000, 0x0000, 0x0000, 0x0000,	0x0080, 0x01C0, 0x03E0, 0x07F0,
 	0x03E0, 0x03E0, 0x03E0, 0x03E0,	0x0000, 0x0000, 0x0000, 0x0000
@@ -968,47 +1115,58 @@ MacIndy3Gui::Inventory::ScrollButton::ScrollButton(int x, int y, int width, int
 }
 
 bool MacIndy3Gui::Inventory::ScrollButton::handleEvent(Common::Event &event) {
-	if (event.type != Common::EVENT_LBUTTONDOWN)
+	if (!_enabled || event.type != Common::EVENT_LBUTTONDOWN)
 		return false;
 
-	if (_enabled && _bounds.contains(event.mouse.x, event.mouse.y)) {
+	if (_bounds.contains(event.mouse)) {
 		setRedraw(true);
-		_timer = 12;
+		setTimer(WIDGET_TIMER_JIFFIES);
 		return true;
 	}
 
 	return false;
 }
 
+bool MacIndy3Gui::Inventory::ScrollButton::handleMouseHeld(Common::Point &pressed, Common::Point &held) {
+	if (!_enabled)
+		return false;
+
+	return _bounds.contains(pressed) && _bounds.contains(held);
+}
+
 void MacIndy3Gui::Inventory::ScrollButton::draw() {
 	if (!_redraw)
 		return;
 
-	debug("Drawing inventory arrow %d", _direction);
-
-	setRedraw(false);
+	debug(1, "Inventory::ScrollButton: Drawing [%d]", _direction);
 
 	const uint16 *arrow = (_direction == kScrollUp) ? _upArrow : _downArrow;
-	byte color = _timer ? 0 : 15;
+	Color color = hasTimer() ? kBlack : kWhite;
 
 	drawBitmap(_bounds, arrow, color);
+
 	setRedraw(false);
 	copyRectToScreen(_bounds);
 }
 
+// ---------------------------------------------------------------------------
+// The MacIndy3Gui class ties the whole thing together, mostly by delegating
+// the work to the individual widgets.
+// ---------------------------------------------------------------------------
+
 MacIndy3Gui::MacIndy3Gui(OSystem *system, ScummEngine *vm) :
-	_system(system), _vm(vm), _macScreen(vm->_macScreen), _visible(false) {
+	_system(system), _vm(vm), _surface(vm->_macScreen), _visible(false) {
 	Graphics::MacFontManager *mfm = _vm->_macFontManager;
 
-	_fonts[0] = mfm->getFont(Graphics::MacFont(Graphics::kMacFontGeneva, 9));
-	_fonts[1] = mfm->getFont(Graphics::MacFont(Graphics::kMacFontGeneva, 9, Graphics::kMacFontBold));
-	_fonts[2] = mfm->getFont(Graphics::MacFont(Graphics::kMacFontGeneva, 9, Graphics::kMacFontBold | Graphics::kMacFontOutline | Graphics::kMacFontCondense));
+	_fonts[kRegular] = mfm->getFont(Graphics::MacFont(Graphics::kMacFontGeneva, 9));
+	_fonts[kBold] = mfm->getFont(Graphics::MacFont(Graphics::kMacFontGeneva, 9, Graphics::kMacFontBold));
+	_fonts[kOutline] = mfm->getFont(Graphics::MacFont(Graphics::kMacFontGeneva, 9, Graphics::kMacFontBold | Graphics::kMacFontOutline | Graphics::kMacFontCondense));
 
 	// There is one widget for every verb in the game. Verbs include the
 	// inventory widget and conversation options.
 
 	Widget::_vm = _vm;
-	Widget::_surface = _macScreen;
+	Widget::_surface = _surface;
 	Widget::_gui = this;
 
 	_widgets[  1] = new Button(137, 312,  68, 18); // Open
@@ -1042,7 +1200,7 @@ MacIndy3Gui::MacIndy3Gui(OSystem *system, ScummEngine *vm) :
 	_widgets[125] = new Button(423, 352, 151, 18); // Converse 6
 
 	for (Common::HashMap<int, VerbWidget *>::iterator i = _widgets.begin(); i != _widgets.end(); ++i)
-		i->_value->_verbid = i->_key;
+		i->_value->setVerbid(i->_key);
 }
 
 MacIndy3Gui::~MacIndy3Gui() {
@@ -1050,6 +1208,18 @@ MacIndy3Gui::~MacIndy3Gui() {
 		delete i->_value;
 }
 
+// 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.
+
+int MacIndy3Gui::getInventoryScrollOffset() {
+	return _vm->VAR(67);
+}
+
+void MacIndy3Gui::setInventoryScrollOffset(int n) {
+	_vm->VAR(67) = n;
+}
+
 bool MacIndy3Gui::isActive() {
 	// The GUI is only allowed if the verb area has the expected size. There
 	// are scenes (e.g. the flight path to Venice) where it's not.
@@ -1106,16 +1276,18 @@ void MacIndy3Gui::resetAfterLoad() {
 	// The old GUI adjusted the offset by 2 when scrolling the inventory.
 
 	if (oldSavegame)
-		_vm->VAR(67) /= 2;
+		setInventoryScrollOffset(getInventoryScrollOffset() / 2);
 }
 
 void MacIndy3Gui::update(int delta) {
+#if DEBUG_VERB_SCRIPT
 	static int lastVerbScript = -1;
 
 	if (_vm->VAR(_vm->VAR_VERB_SCRIPT) != lastVerbScript) {
-		debug("VAR_VERB_SCRIPT = %d", _vm->VAR(_vm->VAR_VERB_SCRIPT));
+		debug(1, "MacIndy3Gui: New verb script: %d", _vm->VAR(_vm->VAR_VERB_SCRIPT));
 		lastVerbScript = _vm->VAR(_vm->VAR_VERB_SCRIPT);
 	}
+#endif
 
 	if (!isActive()) {
 		if (isVisible())
@@ -1123,6 +1295,20 @@ void MacIndy3Gui::update(int delta) {
 		return;
 	}
 
+	if (_leftButtonIsPressed) {
+		_timer -= delta;
+
+		if (_timer <= 0) {
+			debug(2, "MacIndy3Gui: Left button still down");
+
+			_timer = REPEAT_TIMER_JIFFIES;
+
+			for (Common::HashMap<int, VerbWidget *>::iterator i = _widgets.begin(); i != _widgets.end(); ++i)
+				if (i->_value->handleMouseHeld(_leftButtonPressed, _leftButtonHeld))
+					break;
+		}
+	}
+
 	// Tentatively mark the verb widgets for removal. Any widget that wants
 	// to stay has to say so.
 
@@ -1151,7 +1337,7 @@ void MacIndy3Gui::update(int delta) {
 				const byte *ptr = _vm->getResourceAddress(rtVerb, i);
 				byte buf[270];
 				_vm->convertMessageToString(ptr, buf, sizeof(buf));
-				debug("Unknown verb: %d %s", vs->verbid, buf);
+				warning("MacIndy3Gui: Unknown verb: %d %s", vs->verbid, buf);
 			}
 		}
 	}
@@ -1167,7 +1353,7 @@ void MacIndy3Gui::update(int delta) {
 	for (Common::HashMap<int, VerbWidget *>::iterator i = _widgets.begin(); i != _widgets.end(); ++i) {
 		VerbWidget *w = i->_value;
 
-		if (w->_kill && w->_visible) {
+		if (w->isDying() && w->isVisible()) {
 			w->undraw();
 			w->reset();
 		}
@@ -1176,9 +1362,8 @@ void MacIndy3Gui::update(int delta) {
 	for (Common::HashMap<int, VerbWidget *>::iterator i = _widgets.begin(); i != _widgets.end(); ++i) {
 		VerbWidget *w = i->_value;
 
-		if (w->_verbslot != -1) {
+		if (w->hasVerb())
 			w->draw();
-		}
 	}
 }
 
@@ -1186,6 +1371,27 @@ void MacIndy3Gui::handleEvent(Common::Event &event) {
 	if (!isVisible() || _vm->_userPut <= 0)
 		return;
 
+	if (event.type == Common::EVENT_LBUTTONDOWN) {
+		if (!_leftButtonIsPressed) {
+			debug(2, "MacIndy3Gui: Left button down");
+
+			_leftButtonIsPressed = true;
+			_leftButtonPressed = event.mouse;
+			_leftButtonHeld = event.mouse;
+			_timer = REPEAT_TIMER_JIFFIES;
+		}
+	} else if (event.type == Common::EVENT_LBUTTONUP) {
+		if (_leftButtonIsPressed) {
+			debug(2, "MacIndy3Gui: Left button up");
+
+			_leftButtonIsPressed = false;
+			_timer = 0;
+		}
+	} else if (event.type == Common::EVENT_MOUSEMOVE) {
+		if (_leftButtonIsPressed)
+			_leftButtonHeld = event.mouse;
+	}
+
 	for (Common::HashMap<int, VerbWidget *>::iterator i = _widgets.begin(); i != _widgets.end(); ++i) {
 		if (i->_value->handleEvent(event))
 			break;
@@ -1196,7 +1402,7 @@ void MacIndy3Gui::show() {
 	if (isVisible())
 		return;
 
-	debug("SHOW");
+	debug(1, "MacIndy3Gui: Showing");
 
 	clear();
 	_visible = true;
@@ -1207,6 +1413,8 @@ void MacIndy3Gui::hide() {
 		return;
 
 	_visible = false;
+	_leftButtonIsPressed = false;
+	_timer = 0;
 
 	for (Common::HashMap<int, VerbWidget *>::iterator i = _widgets.begin(); i != _widgets.end(); ++i)
 		i->_value->reset();
@@ -1214,50 +1422,50 @@ void MacIndy3Gui::hide() {
 	if (_vm->_virtscr[kVerbVirtScreen].topline != 144)
 		return;
 
-	debug("HIDE");
+	debug(1, "MacIndy3Gui: Hiding");
 
-	_macScreen->fillRect(Common::Rect(0, 288, 640, 400), 0);
-	_system->copyRectToScreen(_macScreen->getBasePtr(0, 288), _macScreen->pitch, 0, 288, 640, 112);
+	_surface->fillRect(Common::Rect(0, 288, 640, 400), kBlack);
+	_system->copyRectToScreen(_surface->getBasePtr(0, 288), _surface->pitch, 0, 288, 640, 112);
 }
 
 void MacIndy3Gui::clear() {
-	_macScreen->fillRect(Common::Rect(0, 288, 640, 289), 0);
-	_macScreen->fillRect(Common::Rect(0, 373, 640, 400), 0);
+	_surface->fillRect(Common::Rect(0, 288, 640, 289), kBlack);
+	_surface->fillRect(Common::Rect(0, 373, 640, 400), kBlack);
 
 	fill(Common::Rect(0, 290, 640, 373));
-	drawBitmap(Common::Rect(  0, 290,   4, 294), _ulCorner, 0);
-	drawBitmap(Common::Rect(636, 290, 640, 294), _urCorner, 0);
-	drawBitmap(Common::Rect(  0, 369,   4, 373), _llCorner, 0);
-	drawBitmap(Common::Rect(636, 369, 640, 373), _lrCorner, 0);
+	drawBitmap(Common::Rect(  0, 290,   4, 294), _ulCorner, kBlack);
+	drawBitmap(Common::Rect(636, 290, 640, 294), _urCorner, kBlack);
+	drawBitmap(Common::Rect(  0, 369,   4, 373), _llCorner, kBlack);
+	drawBitmap(Common::Rect(636, 369, 640, 373), _lrCorner, kBlack);
 
 	copyRectToScreen(Common::Rect(0, 288, 640, 400));
 }
 
 void MacIndy3Gui::copyRectToScreen(Common::Rect r) {
-	_system->copyRectToScreen(_macScreen->getBasePtr(r.left, r.top), _macScreen->pitch, r.left, r.top, r.width(), r.height());
+	_system->copyRectToScreen(_surface->getBasePtr(r.left, r.top), _surface->pitch, r.left, r.top, r.width(), r.height());
 }
 
 void MacIndy3Gui::fill(Common::Rect r) {
-	int pitch = _macScreen->pitch;
+	int pitch = _surface->pitch;
 
 	// Fill the screen with either gray of a checkerboard pattern.
 
 	if (_vm->_renderMode == Common::kRenderMacintoshBW) {
-		byte *row = (byte *)_macScreen->getBasePtr(r.left, r.top);
+		byte *row = (byte *)_surface->getBasePtr(r.left, r.top);
 
 		for (int y = r.top; y < r.bottom; y++) {
 			byte *ptr = row;
 			for (int x = r.left; x < r.right; x++)
-				*ptr++ = ((x + y) & 1) ? 15 : 0;
+				*ptr++ = ((x + y) & 1) ? kWhite : kBlack;
 			row += pitch;
 		}
 	} else
-		_macScreen->fillRect(r, 7);
+		_surface->fillRect(r, kLightGray);
 }
 
-void MacIndy3Gui::drawBitmap(Common::Rect r, const uint16 *bitmap, byte color) {
-	byte *ptr = (byte *)_macScreen->getBasePtr(r.left, r.top);
-	int pitch = _macScreen->pitch;
+void MacIndy3Gui::drawBitmap(Common::Rect r, const uint16 *bitmap, Color color) {
+	byte *ptr = (byte *)_surface->getBasePtr(r.left, r.top);
+	int pitch = _surface->pitch;
 
 	assert(r.width() <= 16);
 
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index cd1462ba0d0..aa020d3c355 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -35,6 +35,38 @@ class ScummEngine;
 
 class MacIndy3Gui {
 public:
+	enum Color {
+		kBlack = 0,
+		kBlue = 1,
+		kGreen = 2,
+		kCyan = 3,
+		kRed = 4,
+		kMagenta = 5,
+		kBrown = 6,
+		kLightGray = 7,
+		kDarkGray = 8,
+		kBrightBlue = 9,
+		kBrightGreen = 10,
+		kBrightCyan = 11,
+		kBrightRed = 12,
+		kBrightMagenta = 13,
+		kBrightYellow = 14,
+		kWhite = 15,
+		kBackground = 254,	// Gray or checkerboard
+		kTransparency = 255
+	};
+
+	enum FontId {
+		kRegular = 0,
+		kBold = 1,
+		kOutline = 2
+	};
+
+	enum ScrollDirection {
+		kScrollUp,
+		kScrollDown
+	};
+
 	MacIndy3Gui(OSystem *system, ScummEngine *vm);
 	~MacIndy3Gui();
 
@@ -49,63 +81,91 @@ public:
 	// the verb area to clear the power meters and text.
 
 	bool isVisible() { return _visible; }
-
 	const Graphics::Font *getFont(int n) { return _fonts[n]; }
 
 	void resetAfterLoad();
 	void update(int delta);
 	void handleEvent(Common::Event &event);
 
+	int getInventoryScrollOffset();
+	void setInventoryScrollOffset(int n);
+
 private:
 	OSystem *_system = nullptr;
 	ScummEngine *_vm = nullptr;
-	Graphics::Surface *_macScreen = nullptr;
+	Graphics::Surface *_surface = nullptr;
 	const Graphics::Font *_fonts[3];
 
 	bool _visible = false;
 
+	bool _leftButtonIsPressed = false;
+	Common::Point _leftButtonPressed;
+	Common::Point _leftButtonHeld;
+
+	int _timer = 0;
+
 	class Widget {
+	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;
-		int _timer = 0;
-		bool _enabled = false;
-		bool _redraw = false;
 
 		Widget(int x, int y, int width, int height);
 		virtual ~Widget() {}
 
-		virtual void reset();
+		void setEnabled(bool b) { _enabled = b; }
+
+		void setTimer(int t) { _timer = t; }
+		void clearTimer() { _timer = 0; }
+		bool hasTimer() { return _timer > 0; }
+
 		virtual void setRedraw(bool redraw) { _redraw = redraw; }
 
+		virtual void reset();
+
+		virtual bool handleEvent(Common::Event &event) = 0;
+		virtual bool handleMouseHeld(Common::Point &pressed, Common::Point &held) { return false; }
+		virtual bool updateTimer(int delta);
+
 		virtual void draw() = 0;
 		virtual void undraw() {}
-		virtual bool updateTimer(int delta);
 
 		// Primitives
 		void copyRectToScreen(Common::Rect r);
 		void fill(Common::Rect r);
-		void drawBitmap(Common::Rect r, const uint16 *bitmap, byte color);
+		void drawBitmap(Common::Rect r, const uint16 *bitmap, Color color);
 		void drawShadowBox(Common::Rect r);
-		void drawShadowFrame(Common::Rect r, byte shadowColor, byte fillColor);
+		void drawShadowFrame(Common::Rect r, Color shadowColor, Color fillColor);
 	};
 
 	class VerbWidget : public Widget {
-	public:
+	protected:
+		bool _visible = false;
 		int _verbid = 0;
 		int _verbslot = -1;
-		bool _visible = false;
 		bool _kill = false;
 
+	public:
+
 		VerbWidget(int x, int y, int width, int height) : Widget(x, y, width, height) {}
 
-		void reset();
+		void setVerbid(int n) { _verbid = n; }
+		bool hasVerb() { return _verbslot != -1; }
+		bool isVisible() { return _visible; }
 		void threaten() { _kill = true; }
+		bool isDying() { return _kill; }
+
+		void reset();
 
-		virtual bool handleEvent(Common::Event &event) = 0;
 		virtual void updateVerb(int verbslot);
 
 		void draw();
@@ -113,9 +173,11 @@ private:
 	};
 
 	class Button : public VerbWidget {
-	public:
+	private:
 		Common::String _text;
 
+	public:
+
 		Button(int x, int y, int width, int height) : VerbWidget(x, y, width, height) {}
 
 		bool handleEvent(Common::Event &event);
@@ -123,21 +185,28 @@ private:
 		void reset();
 		bool updateTimer(int delta);
 		void updateVerb(int verbslot);
-		void draw();
-	};
 
-	enum ScrollDirection {
-		kScrollUp,
-		kScrollDown
+		void draw();
 	};
 
 	class Inventory : public VerbWidget {
 	private:
 		class ScrollBar : public Widget {
+		private:
+			int _invCount = 0;
+			int _invOffset = 0;
+
 		public:
 			ScrollBar(int x, int y, int width, int height);
 
+			void setCounters(int invCount, int invOffset);
+			void moveInvOffset(int offset);
+			int getHandlePosition();
+
+			void reset();
+
 			bool handleEvent(Common::Event &event);
+
 			void draw();
 		};
 
@@ -152,21 +221,33 @@ private:
 			ScrollButton(int x, int y, int width, int height, ScrollDirection direction);
 
 			bool handleEvent(Common::Event &event);
+			bool handleMouseHeld(Common::Point &pressed, Common::Point &held);
+
 			void draw();
 		};
 
 		class Slot : public Widget {
 		private:
+			Common::String _name;
 			int _slot = -1;
-
-		public:
 			int _obj = -1;
-			Common::String _name;
 
+		public:
 			Slot(int slot, int x, int y, int width, int height);
+
+			void clearName() { _name.clear(); }
+			bool hasName() { return !_name.empty(); }
+
+			void clearObject() { _obj = -1; }
+
+			void setObject(int n);
+			int getObject() { return _obj; }
+
 			void reset();
+
 			bool handleEvent(Common::Event &event);
 			bool updateTimer(int delta);
+
 			void draw();
 		};
 
@@ -181,13 +262,14 @@ private:
 		Inventory(int x, int y, int width, int height);
 		~Inventory();
 
-		void reset();
 		void setRedraw(bool redraw);
 
-		void updateVerb(int verbslot);
+		void reset();
 
 		bool handleEvent(Common::Event &event);
+		bool handleMouseHeld(Common::Point &pressed, Common::Point &held);
 		bool updateTimer(int delta);
+		void updateVerb(int verbslot);
 
 		void draw();
 	};
@@ -207,7 +289,7 @@ private:
 
 	void copyRectToScreen(Common::Rect r);
 	void fill(Common::Rect r);
-	void drawBitmap(Common::Rect r, const uint16 *bitmap, byte color);
+	void drawBitmap(Common::Rect r, const uint16 *bitmap, Color color);
 };
 
 } // End of namespace Scumm
diff --git a/engines/scumm/input.cpp b/engines/scumm/input.cpp
index 998e78ef4e7..e092feea5b7 100644
--- a/engines/scumm/input.cpp
+++ b/engines/scumm/input.cpp
@@ -105,8 +105,8 @@ 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)
+	// Handle Mac Indy3 events before scaling the mouse coordinates.
+	if (_macIndy3Gui && _macIndy3Gui->isVisible())
 		_macIndy3Gui->handleEvent(event);
 
 	switch (event.type) {


Commit: 44b7558756e6f16b9e04c9fbfb3f2de564ba7ca0
    https://github.com/scummvm/scummvm/commit/44b7558756e6f16b9e04c9fbfb3f2de564ba7ca0
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Remove Indy 3 Mac hacks that are no longer needed

Changed paths:
    engines/scumm/cursor.cpp
    engines/scumm/gfx.cpp
    engines/scumm/saveload.cpp
    engines/scumm/string.cpp
    engines/scumm/verbs.cpp


diff --git a/engines/scumm/cursor.cpp b/engines/scumm/cursor.cpp
index c2f98dc7604..fe2a30f397b 100644
--- a/engines/scumm/cursor.cpp
+++ b/engines/scumm/cursor.cpp
@@ -993,7 +993,7 @@ void ScummEngine_v5::setBuiltinCursor(int idx) {
 		}
 	}
 
-	if (_game.id == GID_INDY3 && _macScreen) {
+	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,
diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp
index bc56053e7ca..7109e6354c5 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 (_macScreen && _game.id == GID_INDY3 && _charset->_textScreenID == kTextVirtScreen) {
+		if (_macIndy3Gui && _charset->_textScreenID == kTextVirtScreen) {
 			mac_undrawIndy3TextBox();
 			return;
 		}
diff --git a/engines/scumm/saveload.cpp b/engines/scumm/saveload.cpp
index 8c3f15b3cfd..868be4c6f3e 100644
--- a/engines/scumm/saveload.cpp
+++ b/engines/scumm/saveload.cpp
@@ -2064,7 +2064,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()) || (_game.id == GID_INDY3 && _macScreen)) {
+		if ((_game.id == GID_LOOM && !_macCursorFile.empty()) || _macIndy3Gui) {
 			setBuiltinCursor(0);
 		}
 	}
diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp
index c14369c2659..7867f0992f8 100644
--- a/engines/scumm/string.cpp
+++ b/engines/scumm/string.cpp
@@ -1124,7 +1124,7 @@ void ScummEngine::CHARSET_1() {
 	if (_isRTL)
 		fakeBidiString(_charsetBuffer + _charsetBufPos, true, sizeof(_charsetBuffer) - _charsetBufPos);
 
-	bool createTextBox = (_macScreen && _game.id == GID_INDY3);
+	bool createTextBox = (_macIndy3Gui != nullptr);
 	bool drawTextBox = false;
 
 	while (handleNextCharsetCode(a, &c)) {
@@ -1231,33 +1231,6 @@ void ScummEngine::drawString(int a, const byte *msg) {
 	_charset->_disableOffsX = _charset->_firstChar = true;
 	_charset->setCurID(_string[a].charset);
 
-	// HACK: Correct positions of text in books in Indy3 Mac.
-	// See also bug #8759. Not needed when using the Mac font.
-	if (_game.id == GID_INDY3 && _game.platform == Common::kPlatformMacintosh && a == 1 && !_macScreen) {
-		if (_currentRoom == 75) {
-			// Grail Diary Page 1 (Library)
-			if (_charset->_startLeft < 160)
-				_charset->_startLeft = _charset->_left = _string[a].xpos - 22;
-			else if (_charset->_startLeft < 200)
-				_charset->_startLeft = _charset->_left = _string[a].xpos - 10;
-		} else if (_currentRoom == 90) {
-			// Grail Diary Page 2 (Catacombs - Engravings)
-			if (_charset->_startLeft < 160)
-				_charset->_startLeft = _charset->_left = _string[a].xpos - 21;
-			else if (_charset->_startLeft < 200)
-				_charset->_startLeft = _charset->_left = _string[a].xpos - 15;
-		} else if (_currentRoom == 69) {
-			// Grail Diary Page 3 (Catacombs - Music)
-			if (_charset->_startLeft < 160)
-				_charset->_startLeft = _charset->_left = _string[a].xpos - 15;
-			else if (_charset->_startLeft < 200)
-				_charset->_startLeft = _charset->_left = _string[a].xpos - 10;
-		} else if (_currentRoom == 74) {
-			// Biplane Manual
-			_charset->_startLeft = _charset->_left = _string[a].xpos - 35;
-		}
-	}
-
 	if (_game.version >= 5)
 		memcpy(_charsetColorMap, _charsetData[_charset->getCurID()], 4);
 
diff --git a/engines/scumm/verbs.cpp b/engines/scumm/verbs.cpp
index a6d2a141fdc..91084d44e9e 100644
--- a/engines/scumm/verbs.cpp
+++ b/engines/scumm/verbs.cpp
@@ -1095,29 +1095,6 @@ void ScummEngine::drawVerb(int verb, int mode) {
 	if (!verb)
 		return;
 
-	// The way we implement high-resolution font on a scaled low-resolution
-	// background requires there to always be a text surface telling which
-	// pixels have been drawn on. This means that the "has mask" feature is
-	// not correctly implemented, and in most cases this works just fine
-	// for both Loom and Indiana Jones and the Last Crusade.
-	//
-	// However, there is at least one scene in Indiana Jones - room 80,
-	// where you escape from the zeppelin on a biplane - where the game
-	// (sloppily, in my opinion) draws two disabled verbs (Travel and Talk)
-	// and then counts on the background to draw over them. Obviously that
-	// won't work here.
-	//
-	// I thought it would be possible to base this workaround on room
-	// height, but then verbs aren't redrawn after reading books. So I
-	// guess the safest path is to limit it to this particular room.
-	//
-	// Note that with the low-resolution font, which does implement the
-	// "has mask" feature, the Macintosh version still needs a hack in
-	// printChar() for black text to work properly. This version of the
-	// game is weird.
-	if (_game.id == GID_INDY3 && _macScreen && _currentRoom == 80)
-		return;
-
 	vs = &_verbs[verb];
 
 	if (!vs->saveid && vs->curmode && vs->verbid) {


Commit: 436872ffbbc895431fd0d66886008fe56b300938
    https://github.com/scummvm/scummvm/commit/436872ffbbc895431fd0d66886008fe56b300938
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: This should fix some transitions with the Indy 3 Mac GUI

If the GUI says it's invisible, it's invisible. But if it says it's
visible, that may not be true because things may have changed so that
the GUI is no longer allowed to be on screen.

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 d0b8827809d..6fa6720f3cb 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -1251,6 +1251,16 @@ bool MacIndy3Gui::isActive() {
 	return false;
 }
 
+bool MacIndy3Gui::isVisible() {
+	if (!_visible)
+		return false;
+
+	// The visibility flag may not have been updated yet, so better check
+	// that the GUI is still active.
+
+	return isActive();
+}
+
 void MacIndy3Gui::resetAfterLoad() {
 	_visible = false;
 
@@ -1280,11 +1290,11 @@ void MacIndy3Gui::resetAfterLoad() {
 }
 
 void MacIndy3Gui::update(int delta) {
-#if DEBUG_VERB_SCRIPT
+#if DEBUG_VERB_SCRIPTS
 	static int lastVerbScript = -1;
 
 	if (_vm->VAR(_vm->VAR_VERB_SCRIPT) != lastVerbScript) {
-		debug(1, "MacIndy3Gui: New verb script: %d", _vm->VAR(_vm->VAR_VERB_SCRIPT));
+		debug("MacIndy3Gui: New verb script: %d", _vm->VAR(_vm->VAR_VERB_SCRIPT));
 		lastVerbScript = _vm->VAR(_vm->VAR_VERB_SCRIPT);
 	}
 #endif
@@ -1295,7 +1305,7 @@ void MacIndy3Gui::update(int delta) {
 		return;
 	}
 
-	if (_leftButtonIsPressed) {
+	if (delta > 0 && _leftButtonIsPressed) {
 		_timer -= delta;
 
 		if (_timer <= 0) {
@@ -1315,7 +1325,8 @@ void MacIndy3Gui::update(int delta) {
 	for (Common::HashMap<int, VerbWidget *>::iterator i = _widgets.begin(); i != _widgets.end(); ++i) {
 		VerbWidget *w = i->_value;
 
-		w->updateTimer(delta);
+		if (delta > 0)
+			w->updateTimer(delta);
 		w->threaten();
 	}
 
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index aa020d3c355..ef29db74eb0 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -80,7 +80,7 @@ public:
 	// it's not drawing verbs, so the SCUMM engine is allowed to draw in
 	// the verb area to clear the power meters and text.
 
-	bool isVisible() { return _visible; }
+	bool isVisible();
 	const Graphics::Font *getFont(int n) { return _fonts[n]; }
 
 	void resetAfterLoad();


Commit: 30367e1dd2b0164286259e292806b0fbd0be26d6
    https://github.com/scummvm/scummvm/commit/30367e1dd2b0164286259e292806b0fbd0be26d6
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Fix Indy 3 Mac GUI inventory string drawing

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 6fa6720f3cb..646d03bba71 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -924,11 +924,14 @@ void MacIndy3Gui::Inventory::Slot::setObject(int obj) {
 	_obj = obj;
 	setEnabled(true);
 
-	const byte *name = _vm->getObjOrActorName(obj);
+	const byte *ptr = _vm->getObjOrActorName(obj);
 
-	if (name) {
-		if (_name != (const char *)name) {
-			_name = (const char *)name;
+	if (ptr) {
+		byte buf[270];
+		_vm->convertMessageToString(ptr, buf, sizeof(buf));
+
+		if (_name != (const char *)buf) {
+			_name = (const char *)buf;
 			clearTimer();
 			setRedraw(true);
 		}
@@ -987,10 +990,8 @@ void MacIndy3Gui::Inventory::Slot::draw() {
 		int x = _bounds.left + 4;
 
 		for (uint i = 0; i < _name.size() && x < _bounds.right; i++) {
-			if (_name[i] != '@') {
-				font->drawChar(_surface, _name[i], x, y, fg);
-				x += font->getCharWidth(_name[i]);
-			}
+			font->drawChar(_surface, _name[i], x, y, fg);
+			x += font->getCharWidth(_name[i]);
 		}
 	}
 


Commit: 16d610f69dc7813968a215b89f1332ead9a676ed
    https://github.com/scummvm/scummvm/commit/16d610f69dc7813968a215b89f1332ead9a676ed
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: On second thought, don't adjust the Indy 3 Mac GUI scroll offset

While it's true that we used to increase/decrease the scroll offset by
two in the old GUI, that was becaues the inventory was presented in two
columns. The scroll offset was correct, and does not have to be adjusted
when loading pre-new GUI savegames.

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 646d03bba71..8fc28bfe942 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -1275,19 +1275,10 @@ void MacIndy3Gui::resetAfterLoad() {
 	// In old savegames, the DOS verb IDs may still be present, and have
 	// to be removed.
 
-	bool oldSavegame = false;
-
 	for (int i = 0; i < _vm->_numVerbs; i++) {
-		if (_vm->_verbs[i].verbid >= 102 && _vm->_verbs[i].verbid <= 108) {
+		if (_vm->_verbs[i].verbid >= 102 && _vm->_verbs[i].verbid <= 108)
 			_vm->killVerb(i);
-			oldSavegame = true;
-		}
 	}
-
-	// The old GUI adjusted the offset by 2 when scrolling the inventory.
-
-	if (oldSavegame)
-		setInventoryScrollOffset(getInventoryScrollOffset() / 2);
 }
 
 void MacIndy3Gui::update(int delta) {


Commit: 2bc18cf9716386ff96eee325ac568778e5200198
    https://github.com/scummvm/scummvm/commit/2bc18cf9716386ff96eee325ac568778e5200198
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Mac Indy 3 GUI: Add "const"

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 8fc28bfe942..dbe254981a1 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -416,22 +416,22 @@ bool MacIndy3Gui::Widget::updateTimer(int delta) {
 	return _timer == 0;
 }
 
-void MacIndy3Gui::Widget::copyRectToScreen(Common::Rect r) {
+void MacIndy3Gui::Widget::copyRectToScreen(Common::Rect r) const {
 	_gui->copyRectToScreen(r);
 }
 
-void MacIndy3Gui::Widget::fill(Common::Rect r) {
+void MacIndy3Gui::Widget::fill(Common::Rect r) const {
 	_gui->fill(r);
 }
 
-void MacIndy3Gui::Widget::drawBitmap(Common::Rect r, const uint16 *bitmap, Color color) {
+void MacIndy3Gui::Widget::drawBitmap(Common::Rect r, const uint16 *bitmap, Color color) const {
 	_gui->drawBitmap(r, bitmap, color);
 }
 
 // The shadow box is the basic outline of the verb buttons and the inventory
 // widget. A slightly rounded rectangle with a shadow and a highlight.
 
-void MacIndy3Gui::Widget::drawShadowBox(Common::Rect r) {
+void MacIndy3Gui::Widget::drawShadowBox(Common::Rect r) const {
 	_surface->hLine(r.left + 1, r.top, r.right - 3, kBlack);
 	_surface->hLine(r.left + 1, r.bottom - 2, r.right - 3, kBlack);
 	_surface->vLine(r.left, r.top + 1, r.bottom - 3, kBlack);
@@ -447,7 +447,7 @@ void MacIndy3Gui::Widget::drawShadowBox(Common::Rect r) {
 // The shadow frame is a rectangle with a highlight. It can be filled or
 // unfilled.
 
-void MacIndy3Gui::Widget::drawShadowFrame(Common::Rect r, Color shadowColor, Color fillColor) {
+void MacIndy3Gui::Widget::drawShadowFrame(Common::Rect r, Color shadowColor, Color fillColor) const {
 	_surface->hLine(r.left, r.top, r.right - 1, kBlack);
 	_surface->hLine(r.left, r.bottom - 1, r.right - 1, kBlack);
 	_surface->vLine(r.left, r.top + 1, r.bottom - 2, kBlack);
@@ -1213,15 +1213,15 @@ MacIndy3Gui::~MacIndy3Gui() {
 // continue that tradition, just in case. If nothing else, it gives us an easy
 // way to still store it in savegames.
 
-int MacIndy3Gui::getInventoryScrollOffset() {
+int MacIndy3Gui::getInventoryScrollOffset() const {
 	return _vm->VAR(67);
 }
 
-void MacIndy3Gui::setInventoryScrollOffset(int n) {
+void MacIndy3Gui::setInventoryScrollOffset(int n) const {
 	_vm->VAR(67) = n;
 }
 
-bool MacIndy3Gui::isActive() {
+bool MacIndy3Gui::isActive() const {
 	// The GUI is only allowed if the verb area has the expected size. There
 	// are scenes (e.g. the flight path to Venice) where it's not.
 
@@ -1252,7 +1252,7 @@ bool MacIndy3Gui::isActive() {
 	return false;
 }
 
-bool MacIndy3Gui::isVisible() {
+bool MacIndy3Gui::isVisible() const {
 	if (!_visible)
 		return false;
 
@@ -1444,11 +1444,11 @@ void MacIndy3Gui::clear() {
 	copyRectToScreen(Common::Rect(0, 288, 640, 400));
 }
 
-void MacIndy3Gui::copyRectToScreen(Common::Rect r) {
+void MacIndy3Gui::copyRectToScreen(Common::Rect r) const {
 	_system->copyRectToScreen(_surface->getBasePtr(r.left, r.top), _surface->pitch, r.left, r.top, r.width(), r.height());
 }
 
-void MacIndy3Gui::fill(Common::Rect r) {
+void MacIndy3Gui::fill(Common::Rect r) const {
 	int pitch = _surface->pitch;
 
 	// Fill the screen with either gray of a checkerboard pattern.
@@ -1466,7 +1466,7 @@ void MacIndy3Gui::fill(Common::Rect r) {
 		_surface->fillRect(r, kLightGray);
 }
 
-void MacIndy3Gui::drawBitmap(Common::Rect r, const uint16 *bitmap, Color color) {
+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;
 
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index ef29db74eb0..dc2f26b01a2 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -80,15 +80,15 @@ public:
 	// it's not drawing verbs, so the SCUMM engine is allowed to draw in
 	// the verb area to clear the power meters and text.
 
-	bool isVisible();
-	const Graphics::Font *getFont(int n) { return _fonts[n]; }
+	bool isVisible() const;
+	const Graphics::Font *getFont(int n) const { return _fonts[n]; }
 
 	void resetAfterLoad();
 	void update(int delta);
 	void handleEvent(Common::Event &event);
 
-	int getInventoryScrollOffset();
-	void setInventoryScrollOffset(int n);
+	int getInventoryScrollOffset() const;
+	void setInventoryScrollOffset(int n) const;
 
 private:
 	OSystem *_system = nullptr;
@@ -126,7 +126,7 @@ private:
 
 		void setTimer(int t) { _timer = t; }
 		void clearTimer() { _timer = 0; }
-		bool hasTimer() { return _timer > 0; }
+		bool hasTimer() const { return _timer > 0; }
 
 		virtual void setRedraw(bool redraw) { _redraw = redraw; }
 
@@ -140,11 +140,11 @@ private:
 		virtual void undraw() {}
 
 		// Primitives
-		void copyRectToScreen(Common::Rect r);
-		void fill(Common::Rect r);
-		void drawBitmap(Common::Rect r, const uint16 *bitmap, Color color);
-		void drawShadowBox(Common::Rect r);
-		void drawShadowFrame(Common::Rect r, Color shadowColor, Color fillColor);
+		void copyRectToScreen(Common::Rect r) const;
+		void fill(Common::Rect r) const;
+		void drawBitmap(Common::Rect r, const uint16 *bitmap, Color color) const;
+		void drawShadowBox(Common::Rect r) const;
+		void drawShadowFrame(Common::Rect r, Color shadowColor, Color fillColor) const;
 	};
 
 	class VerbWidget : public Widget {
@@ -159,10 +159,10 @@ private:
 		VerbWidget(int x, int y, int width, int height) : Widget(x, y, width, height) {}
 
 		void setVerbid(int n) { _verbid = n; }
-		bool hasVerb() { return _verbslot != -1; }
-		bool isVisible() { return _visible; }
+		bool hasVerb() const { return _verbslot != -1; }
+		bool isVisible() const { return _visible; }
 		void threaten() { _kill = true; }
-		bool isDying() { return _kill; }
+		bool isDying() const { return _kill; }
 
 		void reset();
 
@@ -236,12 +236,12 @@ private:
 			Slot(int slot, int x, int y, int width, int height);
 
 			void clearName() { _name.clear(); }
-			bool hasName() { return !_name.empty(); }
+			bool hasName() const { return !_name.empty(); }
 
 			void clearObject() { _obj = -1; }
 
 			void setObject(int n);
-			int getObject() { return _obj; }
+			int getObject() const { return _obj; }
 
 			void reset();
 
@@ -281,15 +281,15 @@ private:
 	const uint16 _llCorner[4] = { 0x8000, 0x8000, 0xC000, 0xF000 };
 	const uint16 _lrCorner[5] = { 0x1000, 0x1000, 0x3000, 0xF000 };
 
-	bool isActive();
+	bool isActive() const;
 
 	void clear();
 	void show();
 	void hide();
 
-	void copyRectToScreen(Common::Rect r);
-	void fill(Common::Rect r);
-	void drawBitmap(Common::Rect r, const uint16 *bitmap, Color color);
+	void copyRectToScreen(Common::Rect r) const;
+	void fill(Common::Rect r) const;
+	void drawBitmap(Common::Rect r, const uint16 *bitmap, Color color) const;
 };
 
 } // End of namespace Scumm


Commit: e0f856f82c7a08bd5b3ea96d3e138b11ce45b8d7
    https://github.com/scummvm/scummvm/commit/e0f856f82c7a08bd5b3ea96d3e138b11ce45b8d7
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Indy 3 Mac GUI scroll bar fixes

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index dbe254981a1..d7a748e6a7c 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -1015,13 +1015,16 @@ bool MacIndy3Gui::Inventory::ScrollBar::handleEvent(Common::Event &event) {
 	// page at a time, but I haven't had enough inventory items for this to
 	// mean anything other than scrolling to the top or the bottom.
 	//
-	// The direction depends on if you click above or below the handle.
-	// There is nothing special about clicking the handle itself.
+	// The direction depends on if you click above or below the handle. The
+	// original also allowed you to click on the handle itself, but the
+	// behavior was hard to predict so we ignore those completely.
 
 	if (_bounds.contains(event.mouse)) {
-		if (event.mouse.y < _bounds.top + getHandlePosition() + 4)
+		int pos = _bounds.top + getHandlePosition();
+
+		if (event.mouse.y < pos)
 			moveInvOffset(-6);
-		else
+		else if (event.mouse.y >= pos + 8)
 			moveInvOffset(6);
 	}
 
@@ -1132,7 +1135,10 @@ bool MacIndy3Gui::Inventory::ScrollButton::handleMouseHeld(Common::Point &presse
 	if (!_enabled)
 		return false;
 
-	return _bounds.contains(pressed) && _bounds.contains(held);
+	// The scroll button doesn't care if the mouse has moved outside while
+	// being held.
+
+	return _bounds.contains(pressed);
 }
 
 void MacIndy3Gui::Inventory::ScrollButton::draw() {


Commit: 264295b029acfd6414cfd162aa2b505e628d4824
    https://github.com/scummvm/scummvm/commit/264295b029acfd6414cfd162aa2b505e628d4824
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Rework Indy 3 Mac GUI verb script check

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index d7a748e6a7c..943d613a9c6 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -1235,25 +1235,44 @@ bool MacIndy3Gui::isActive() const {
 	if (vs->topline != 144 || vs->h != 56)
 		return false;
 
-	// The GUI is only allowed for certain verb scripts:
-	//
-	//   4 - Regular verb GUI
-	//  18 - Conversations
-	// 200 - Venice, outdoors
-	// 201 - Travel to Henry's or Venice
-	// 205 - Travel from Venice
-	//
-	// Other known verb scripts where the GUI is not allowed:
-	//
-	//  19 - Boxing
+	struct VerbScript {
+		int script;
+		int room;
+	};
+
+	// The GUI is only allowed for certain verb scripts. It's not even
+	// always used then, but those cases are caught by the VirtScreen
+	// check above.
+
+	VerbScript verbScripts[] = {
+		{   4,  0 },	// Regular verb GUI
+		{  18,  0 },	// Conversations
+		{ 200, 15 },	// Venice, outdoors
+		{ 200, 83 },	// Endgame, first trial
+		{ 201,  6 },	// Travel from Barnet College
+		{ 205, 15 }	// Travel from Venice
+	};
+
+	int script = _vm->VAR(_vm->VAR_VERB_SCRIPT);
+	int room = _vm->_currentRoom;
+
+	for (int i = 0; i < ARRAYSIZE(verbScripts); i++) {
+		if (verbScripts[i].script < 100 || verbScripts[i].room == room) {
+			if (verbScripts[i].script == script)
+				return true;
+		}
+	}
 
-	int verbScript = _vm->VAR(_vm->VAR_VERB_SCRIPT);
-	int guiVerbScripts[] = { 4, 18, 200, 201, 205 };
+#if DEBUG_VERB_SCRIPTS
+	static int lastVerbScript = -1;
+	static int lastRoom = -1;
 
-	for (int i = 0; i < ARRAYSIZE(guiVerbScripts); i++) {
-		if (verbScript == guiVerbScripts[i])
-			return true;
+	if (script != lastVerbScript || room != lastRoom) {
+		debug("MacIndy3Gui: Unhandled verb script: %d (%d)", script, room);
+		lastVerbScript = script;
+		lastRoom = room;
 	}
+#endif
 
 	return false;
 }
@@ -1288,15 +1307,6 @@ void MacIndy3Gui::resetAfterLoad() {
 }
 
 void MacIndy3Gui::update(int delta) {
-#if DEBUG_VERB_SCRIPTS
-	static int lastVerbScript = -1;
-
-	if (_vm->VAR(_vm->VAR_VERB_SCRIPT) != lastVerbScript) {
-		debug("MacIndy3Gui: New verb script: %d", _vm->VAR(_vm->VAR_VERB_SCRIPT));
-		lastVerbScript = _vm->VAR(_vm->VAR_VERB_SCRIPT);
-	}
-#endif
-
 	if (!isActive()) {
 		if (isVisible())
 			hide();


Commit: a67b1e9758eb980670c217f17c9e1537533414f8
    https://github.com/scummvm/scummvm/commit/a67b1e9758eb980670c217f17c9e1537533414f8
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Remove Indy 3 Mac GUI verb script check

It seems it's no longer needed. Also, cleaned up naming a bit so that
instead of "active" and "visible", the GUI is now "allowed" and
"active".

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


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 943d613a9c6..8e3805474ab 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -54,9 +54,9 @@ 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. All
-	// other drawing to that area is suspended.
-	if (vs->number == kVerbVirtScreen && _macIndy3Gui->isVisible())
+	// The verb screen is completely replaced with a custom GUI. While
+	// it is active, all other drawing to that area is suspended.
+	if (vs->number == kVerbVirtScreen && _macIndy3Gui->isGuiActive())
 		return;
 
 	const byte *pixels = vs->getPixels(x, top);
@@ -1227,64 +1227,25 @@ void MacIndy3Gui::setInventoryScrollOffset(int n) const {
 	_vm->VAR(67) = n;
 }
 
-bool MacIndy3Gui::isActive() const {
-	// The GUI is only allowed if the verb area has the expected size. There
-	// are scenes (e.g. the flight path to Venice) where it's not.
+bool MacIndy3Gui::isGuiAllowed() const {
+	// The GUI is only allowed if the verb area has the expected size. That
+	// really seems to be all that's needed.
 
 	VirtScreen *vs = &_vm->_virtscr[kVerbVirtScreen];
 	if (vs->topline != 144 || vs->h != 56)
 		return false;
 
-	struct VerbScript {
-		int script;
-		int room;
-	};
-
-	// The GUI is only allowed for certain verb scripts. It's not even
-	// always used then, but those cases are caught by the VirtScreen
-	// check above.
-
-	VerbScript verbScripts[] = {
-		{   4,  0 },	// Regular verb GUI
-		{  18,  0 },	// Conversations
-		{ 200, 15 },	// Venice, outdoors
-		{ 200, 83 },	// Endgame, first trial
-		{ 201,  6 },	// Travel from Barnet College
-		{ 205, 15 }	// Travel from Venice
-	};
-
-	int script = _vm->VAR(_vm->VAR_VERB_SCRIPT);
-	int room = _vm->_currentRoom;
-
-	for (int i = 0; i < ARRAYSIZE(verbScripts); i++) {
-		if (verbScripts[i].script < 100 || verbScripts[i].room == room) {
-			if (verbScripts[i].script == script)
-				return true;
-		}
-	}
-
-#if DEBUG_VERB_SCRIPTS
-	static int lastVerbScript = -1;
-	static int lastRoom = -1;
-
-	if (script != lastVerbScript || room != lastRoom) {
-		debug("MacIndy3Gui: Unhandled verb script: %d (%d)", script, room);
-		lastVerbScript = script;
-		lastRoom = room;
-	}
-#endif
-
-	return false;
+	return true;
 }
 
-bool MacIndy3Gui::isVisible() const {
+bool MacIndy3Gui::isGuiActive() const {
 	if (!_visible)
 		return false;
 
 	// The visibility flag may not have been updated yet, so better check
-	// that the GUI is still active.
+	// that the GUI is still allowed.
 
-	return isActive();
+	return isGuiAllowed();
 }
 
 void MacIndy3Gui::resetAfterLoad() {
@@ -1307,8 +1268,8 @@ void MacIndy3Gui::resetAfterLoad() {
 }
 
 void MacIndy3Gui::update(int delta) {
-	if (!isActive()) {
-		if (isVisible())
+	if (!isGuiAllowed()) {
+		if (_visible)
 			hide();
 		return;
 	}
@@ -1366,7 +1327,7 @@ void MacIndy3Gui::update(int delta) {
 		return;
 	}
 
-	if (!isVisible())
+	if (!_visible)
 		show();
 
 	for (Common::HashMap<int, VerbWidget *>::iterator i = _widgets.begin(); i != _widgets.end(); ++i) {
@@ -1387,7 +1348,7 @@ void MacIndy3Gui::update(int delta) {
 }
 
 void MacIndy3Gui::handleEvent(Common::Event &event) {
-	if (!isVisible() || _vm->_userPut <= 0)
+	if (!isGuiActive() || _vm->_userPut <= 0)
 		return;
 
 	if (event.type == Common::EVENT_LBUTTONDOWN) {
@@ -1418,7 +1379,7 @@ void MacIndy3Gui::handleEvent(Common::Event &event) {
 }
 
 void MacIndy3Gui::show() {
-	if (isVisible())
+	if (_visible)
 		return;
 
 	debug(1, "MacIndy3Gui: Showing");
@@ -1428,7 +1389,7 @@ void MacIndy3Gui::show() {
 }
 
 void MacIndy3Gui::hide() {
-	if (!isVisible())
+	if (!_visible)
 		return;
 
 	_visible = false;
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index dc2f26b01a2..762b61c79bf 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -70,17 +70,17 @@ public:
 	MacIndy3Gui(OSystem *system, ScummEngine *vm);
 	~MacIndy3Gui();
 
-	// There is a distinction between the GUI being active and being
-	// visible. Active means that it's allowed to draw verbs. Visible
-	// means that it's actually drawn verbs. From the outside, only the
-	// visibility is relevant.
+	// 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 scren. From
+	// the outside, only the latter is relevant.
 	//
 	// One case where this makes a difference is when boxing with the
 	// coach. During the "10 minutes later" sign, the GUI is active but
 	// it's not drawing verbs, so the SCUMM engine is allowed to draw in
 	// the verb area to clear the power meters and text.
 
-	bool isVisible() const;
+	bool isGuiActive() const;
 	const Graphics::Font *getFont(int n) const { return _fonts[n]; }
 
 	void resetAfterLoad();
@@ -281,7 +281,7 @@ private:
 	const uint16 _llCorner[4] = { 0x8000, 0x8000, 0xC000, 0xF000 };
 	const uint16 _lrCorner[5] = { 0x1000, 0x1000, 0x3000, 0xF000 };
 
-	bool isActive() const;
+	bool isGuiAllowed() const;
 
 	void clear();
 	void show();
diff --git a/engines/scumm/input.cpp b/engines/scumm/input.cpp
index e092feea5b7..4d1393eb9d8 100644
--- a/engines/scumm/input.cpp
+++ b/engines/scumm/input.cpp
@@ -106,7 +106,7 @@ void ScummEngine_v80he::parseEvent(Common::Event event) {
 
 void ScummEngine::parseEvent(Common::Event event) {
 	// Handle Mac Indy3 events before scaling the mouse coordinates.
-	if (_macIndy3Gui && _macIndy3Gui->isVisible())
+	if (_macIndy3Gui && _macIndy3Gui->isGuiActive())
 		_macIndy3Gui->handleEvent(event);
 
 	switch (event.type) {
diff --git a/engines/scumm/verbs.cpp b/engines/scumm/verbs.cpp
index 91084d44e9e..398f08c2ae5 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->isVisible())
+		if (_macIndy3Gui && _macIndy3Gui->isGuiActive())
 			ignoreVerbKeys = true;
 
 		/* Check keypresses */
@@ -643,7 +643,7 @@ void ScummEngine::checkExecVerbs() {
 		if (!zone)
 			return;
 
-		if (_macIndy3Gui && _macIndy3Gui->isVisible() && zone->number == kVerbVirtScreen)
+		if (_macIndy3Gui && _macIndy3Gui->isGuiActive() && zone->number == kVerbVirtScreen)
 			return;
 
 		over = findVerbAtPos(_mouse.x, _mouse.y);


Commit: ae3dbd74f34e22d991abfdf5b665fe71974d705d
    https://github.com/scummvm/scummvm/commit/ae3dbd74f34e22d991abfdf5b665fe71974d705d
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Cleanup Indy 3 Mac GUI scrollbar logic

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 8e3805474ab..34f1a88b220 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -813,23 +813,26 @@ void MacIndy3Gui::Inventory::updateVerb(int verbslot) {
 
 	int invCount = _vm->getInventoryCount(owner);
 	int invOffset = _gui->getInventoryScrollOffset();
+	int numSlots = ARRAYSIZE(_slots);
 
-	// The scroll offset must be non-negative and if there are six or less
-	// items in the inventory, the inventory is fixed in the top position.
+	// The scroll offset must be non-negative and if there are numSlots or
+	// less objects in the inventory, the inventory is fixed in the top
+	// position.
 
-	if (invOffset < 0 || invCount <= 6)
+	if (invOffset < 0 || invCount <= numSlots)
 		invOffset = 0;
 
-	// If there are more than six items in the inventory, clamp the scroll
-	// offset to be at most invCount - 6.
+	// If there are more than numSlots objects in the inventory, clamp the
+	// scroll offset so that the inventory does not go past the last
+	// numSlots objets.
 
-	if (invCount > 6 && invOffset >= invCount - 6)
-		invOffset = invCount - 6;
+	if (invCount > numSlots && invOffset > invCount - numSlots)
+		invOffset = invCount - numSlots;
 
-	_scrollButtons[0]->setEnabled(invCount > 0 && invOffset > 0);
-	_scrollButtons[1]->setEnabled(invCount > 0 && invOffset + 6 < invCount);
+	_scrollButtons[0]->setEnabled(invOffset > 0);
+	_scrollButtons[1]->setEnabled(invCount > numSlots && invOffset < invCount - numSlots);
 
-	_scrollBar->setEnabled(invCount > 6);
+	_scrollBar->setEnabled(invCount > numSlots);
 	_scrollBar->setCounters(invCount, invOffset);
 
 	_gui->setInventoryScrollOffset(invOffset);
@@ -838,7 +841,7 @@ void MacIndy3Gui::Inventory::updateVerb(int verbslot) {
 
 	// Assign the objects to the inventory slots
 
-	for (int i = 0; i < _vm->_numInventory && invSlot < ARRAYSIZE(_slots); i++) {
+	for (int i = 0; i < _vm->_numInventory && invSlot < numSlots; i++) {
 		int obj = _vm->_inventory[i];
 		if (obj && _vm->getOwner(obj) == owner) {
 			if (--invOffset < 0) {
@@ -850,7 +853,7 @@ void MacIndy3Gui::Inventory::updateVerb(int verbslot) {
 
 	// Clear the remaining slots
 
-	for (int i = invSlot; i < ARRAYSIZE(_slots); i++) {
+	for (int i = invSlot; i < numSlots; i++) {
 		Slot *s = _slots[i];
 
 		if (s->hasName()) {
@@ -1021,11 +1024,12 @@ bool MacIndy3Gui::Inventory::ScrollBar::handleEvent(Common::Event &event) {
 
 	if (_bounds.contains(event.mouse)) {
 		int pos = _bounds.top + getHandlePosition();
+		int numSlots = ARRAYSIZE(_slots);
 
 		if (event.mouse.y < pos)
-			moveInvOffset(-6);
+			moveInvOffset(-numSlots);
 		else if (event.mouse.y >= pos + 8)
-			moveInvOffset(6);
+			moveInvOffset(numSlots);
 	}
 
 	return false;
@@ -1041,11 +1045,12 @@ void MacIndy3Gui::Inventory::ScrollBar::setCounters(int invCount, int invOffset)
 
 void MacIndy3Gui::Inventory::ScrollBar::moveInvOffset(int offset) {
 	int newOffset = _invOffset + offset;
+	int maxOffset = _invCount - ARRAYSIZE(_slots);
 
 	if (newOffset < 0)
 		newOffset = 0;
-	else if (newOffset > _invCount - 6)
-		newOffset = _invCount - 6;
+	else if (newOffset > maxOffset)
+		newOffset = maxOffset;
 
 	if (newOffset != _invOffset) {
 		_invOffset = newOffset;
@@ -1061,11 +1066,12 @@ int MacIndy3Gui::Inventory::ScrollBar::getHandlePosition() {
 	// Hopefully this matches the original scroll handle position.
 
 	int maxPos = _bounds.height() - 8;
+	int maxOffset = _invCount - ARRAYSIZE(_slots);
 
-	if (_invOffset >= _invCount - 6)
+	if (_invOffset >= maxOffset)
 		return maxPos;
 
-	return maxPos * _invOffset / (_invCount - 6);
+	return maxPos * _invOffset / maxOffset;
 }
 
 void MacIndy3Gui::Inventory::ScrollBar::reset() {


Commit: 811347569fd2b1eed177e025ae9b2c3ea0bcfaae
    https://github.com/scummvm/scummvm/commit/811347569fd2b1eed177e025ae9b2c3ea0bcfaae
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: More Indy 3 Mac GUI cleanup

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 34f1a88b220..8e6f895d75a 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -833,7 +833,7 @@ void MacIndy3Gui::Inventory::updateVerb(int verbslot) {
 	_scrollButtons[1]->setEnabled(invCount > numSlots && invOffset < invCount - numSlots);
 
 	_scrollBar->setEnabled(invCount > numSlots);
-	_scrollBar->setCounters(invCount, invOffset);
+	_scrollBar->setInventoryParameters(invCount, invOffset);
 
 	_gui->setInventoryScrollOffset(invOffset);
 
@@ -1035,8 +1035,8 @@ bool MacIndy3Gui::Inventory::ScrollBar::handleEvent(Common::Event &event) {
 	return false;
 }
 
-void MacIndy3Gui::Inventory::ScrollBar::setCounters(int invCount, int invOffset) {
-	if (invCount != _invCount || invOffset != _invOffset)
+void MacIndy3Gui::Inventory::ScrollBar::setInventoryParameters(int invCount, int invOffset) {
+	if (invOffset != _invOffset)
 		setRedraw(true);
 
 	_invCount = invCount;
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 762b61c79bf..1daa6cd32a6 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -122,7 +122,13 @@ private:
 		Widget(int x, int y, int width, int height);
 		virtual ~Widget() {}
 
-		void setEnabled(bool b) { _enabled = b; }
+		void setEnabled(bool enabled) {
+			if (enabled != _enabled)
+				setRedraw(true);
+			if (!_enabled)
+				_timer = 0;
+			_enabled = enabled;
+		}
 
 		void setTimer(int t) { _timer = t; }
 		void clearTimer() { _timer = 0; }
@@ -199,7 +205,7 @@ private:
 		public:
 			ScrollBar(int x, int y, int width, int height);
 
-			void setCounters(int invCount, int invOffset);
+			void setInventoryParameters(int invCount, int invOffset);
 			void moveInvOffset(int offset);
 			int getHandlePosition();
 


Commit: a052cd4eeae92c449a95c492368ef5939e9717df
    https://github.com/scummvm/scummvm/commit/a052cd4eeae92c449a95c492368ef5939e9717df
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Slight drawing optimization in Indy 3 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 8e6f895d75a..e6921e74b2c 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -416,8 +416,8 @@ bool MacIndy3Gui::Widget::updateTimer(int delta) {
 	return _timer == 0;
 }
 
-void MacIndy3Gui::Widget::copyRectToScreen(Common::Rect r) const {
-	_gui->copyRectToScreen(r);
+void MacIndy3Gui::Widget::markScreenAsDirty(Common::Rect r) const {
+	_gui->markScreenAsDirty(r);
 }
 
 void MacIndy3Gui::Widget::fill(Common::Rect r) const {
@@ -506,7 +506,7 @@ void MacIndy3Gui::VerbWidget::undraw() {
 
 	_visible = false;
 	fill(_bounds);
-	copyRectToScreen(_bounds);
+	markScreenAsDirty(_bounds);
 }
 
 // ---------------------------------------------------------------------------
@@ -651,7 +651,7 @@ void MacIndy3Gui::Button::draw() {
 	}
 
 	setRedraw(false);
-	copyRectToScreen(_bounds);
+	markScreenAsDirty(_bounds);
 }
 
 // ---------------------------------------------------------------------------
@@ -884,7 +884,7 @@ void MacIndy3Gui::Inventory::draw() {
 			s->draw();
 		}
 
-		copyRectToScreen(_bounds);
+		markScreenAsDirty(_bounds);
 	}
 
 	// Since the inventory slots overlap, draw the highlighted ones (and
@@ -906,7 +906,6 @@ void MacIndy3Gui::Inventory::draw() {
 		_scrollButtons[i]->draw();
 
 	setRedraw(false);
-	// Everything has already been copied to the screen
 }
 
 // ---------------------------------------------------------------------------
@@ -999,7 +998,7 @@ void MacIndy3Gui::Inventory::Slot::draw() {
 	}
 
 	setRedraw(false);
-	copyRectToScreen(_bounds);
+	markScreenAsDirty(_bounds);
 }
 
 // ---------------------------------------------------------------------------
@@ -1101,7 +1100,7 @@ void MacIndy3Gui::Inventory::ScrollBar::draw() {
 	}
 
 	setRedraw(false);
-	copyRectToScreen(_bounds);
+	markScreenAsDirty(_bounds);
 }
 
 // ---------------------------------------------------------------------------
@@ -1159,7 +1158,7 @@ void MacIndy3Gui::Inventory::ScrollButton::draw() {
 	drawBitmap(_bounds, arrow, color);
 
 	setRedraw(false);
-	copyRectToScreen(_bounds);
+	markScreenAsDirty(_bounds);
 }
 
 // ---------------------------------------------------------------------------
@@ -1214,6 +1213,8 @@ MacIndy3Gui::MacIndy3Gui(OSystem *system, ScummEngine *vm) :
 
 	for (Common::HashMap<int, VerbWidget *>::iterator i = _widgets.begin(); i != _widgets.end(); ++i)
 		i->_value->setVerbid(i->_key);
+
+	_dirtyRects.clear();
 }
 
 MacIndy3Gui::~MacIndy3Gui() {
@@ -1351,6 +1352,8 @@ void MacIndy3Gui::update(int delta) {
 		if (w->hasVerb())
 			w->draw();
 	}
+
+	copyDirtyRectsToScreen();
 }
 
 void MacIndy3Gui::handleEvent(Common::Event &event) {
@@ -1424,11 +1427,31 @@ void MacIndy3Gui::clear() {
 	drawBitmap(Common::Rect(  0, 369,   4, 373), _llCorner, kBlack);
 	drawBitmap(Common::Rect(636, 369, 640, 373), _lrCorner, kBlack);
 
-	copyRectToScreen(Common::Rect(0, 288, 640, 400));
+	markScreenAsDirty(Common::Rect(0, 288, 640, 400));
 }
 
-void MacIndy3Gui::copyRectToScreen(Common::Rect r) const {
-	_system->copyRectToScreen(_surface->getBasePtr(r.left, r.top), _surface->pitch, r.left, r.top, r.width(), r.height());
+void MacIndy3Gui::markScreenAsDirty(Common::Rect r) {
+	// As long as we always call this with the most encompassing rect
+	// first, it is trivial to filter out unnecessary calls.
+
+	for (uint i = 0; i < _dirtyRects.size(); i++) {
+		if (_dirtyRects[i].contains(r))
+			return;
+	}
+
+	_dirtyRects.push_back(r);
+}
+
+void MacIndy3Gui::copyDirtyRectsToScreen() {
+	for (uint i = 0; i < _dirtyRects.size(); i++) {
+		_system->copyRectToScreen(
+			_surface->getBasePtr(_dirtyRects[i].left, _dirtyRects[i].top),
+			_surface->pitch,
+			_dirtyRects[i].left, _dirtyRects[i].top,
+			_dirtyRects[i].width(), _dirtyRects[i].height());
+	}
+
+	_dirtyRects.clear();
 }
 
 void MacIndy3Gui::fill(Common::Rect r) const {
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 1daa6cd32a6..6f78cbef379 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -146,11 +146,12 @@ private:
 		virtual void undraw() {}
 
 		// Primitives
-		void copyRectToScreen(Common::Rect r) const;
 		void fill(Common::Rect r) const;
 		void drawBitmap(Common::Rect r, const uint16 *bitmap, Color color) const;
 		void drawShadowBox(Common::Rect r) const;
 		void drawShadowFrame(Common::Rect r, Color shadowColor, Color fillColor) const;
+
+		void markScreenAsDirty(Common::Rect r) const;
 	};
 
 	class VerbWidget : public Widget {
@@ -282,6 +283,8 @@ private:
 
 	Common::HashMap<int, VerbWidget *> _widgets;
 
+	Common::Array<Common::Rect> _dirtyRects;
+
 	const uint16 _ulCorner[4] = { 0xF000, 0xC000, 0x8000, 0x8000 };
 	const uint16 _urCorner[4] = { 0xF000, 0x3000, 0x1000, 0x1000 };
 	const uint16 _llCorner[4] = { 0x8000, 0x8000, 0xC000, 0xF000 };
@@ -293,9 +296,11 @@ private:
 	void show();
 	void hide();
 
-	void copyRectToScreen(Common::Rect r) const;
 	void fill(Common::Rect r) const;
 	void drawBitmap(Common::Rect r, const uint16 *bitmap, Color color) const;
+
+	void markScreenAsDirty(Common::Rect r);
+	void copyDirtyRectsToScreen();
 };
 
 } // End of namespace Scumm


Commit: 6d501281ff21521ebe99db740aa5963ce3c11626
    https://github.com/scummvm/scummvm/commit/6d501281ff21521ebe99db740aa5963ce3c11626
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Redraw Indy 3 Mac GUI widget immediately if it responds to an event

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 e6921e74b2c..5c63a244bc0 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -581,7 +581,7 @@ void MacIndy3Gui::Button::updateVerb(int verbslot) {
 }
 
 void MacIndy3Gui::Button::draw() {
-	if (!_redraw)
+	if (!getRedraw())
 		return;
 
 	debug(1, "Button: Drawing [%d] %s", _verbid, _text.c_str());
@@ -867,7 +867,7 @@ void MacIndy3Gui::Inventory::updateVerb(int verbslot) {
 }
 
 void MacIndy3Gui::Inventory::draw() {
-	if (_redraw) {
+	if (getRedraw()) {
 		debug(1, "Inventory: Drawing [%d]", _verbid);
 
 		MacIndy3Gui::VerbWidget::draw();
@@ -969,7 +969,7 @@ bool MacIndy3Gui::Inventory::Slot::updateTimer(int delta) {
 }
 
 void MacIndy3Gui::Inventory::Slot::draw() {
-	if (!_redraw)
+	if (!getRedraw())
 		return;
 
 	debug(1, "Inventory::Slot: Drawing [%d] %s", _slot, _name.c_str());
@@ -1080,7 +1080,7 @@ void MacIndy3Gui::Inventory::ScrollBar::reset() {
 }
 
 void MacIndy3Gui::Inventory::ScrollBar::draw() {
-	if (!_redraw)
+	if (!getRedraw())
 		return;
 
 	debug(1, "Inventory::Scrollbar: Drawing");
@@ -1147,7 +1147,7 @@ bool MacIndy3Gui::Inventory::ScrollButton::handleMouseHeld(Common::Point &presse
 }
 
 void MacIndy3Gui::Inventory::ScrollButton::draw() {
-	if (!_redraw)
+	if (!getRedraw())
 		return;
 
 	debug(1, "Inventory::ScrollButton: Drawing [%d]", _direction);
@@ -1381,9 +1381,18 @@ void MacIndy3Gui::handleEvent(Common::Event &event) {
 			_leftButtonHeld = event.mouse;
 	}
 
+	// 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,
+	// we do that redrawing immediately, not on the next update.
+
 	for (Common::HashMap<int, VerbWidget *>::iterator i = _widgets.begin(); i != _widgets.end(); ++i) {
-		if (i->_value->handleEvent(event))
+		if (i->_value->handleEvent(event)) {
+			if (i->_value->getRedraw()) {
+				i->_value->draw();
+				copyDirtyRectsToScreen();
+			}
 			break;
+		}
 	}
 }
 
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 6f78cbef379..a042ef97ab3 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -134,6 +134,7 @@ private:
 		void clearTimer() { _timer = 0; }
 		bool hasTimer() const { return _timer > 0; }
 
+		bool getRedraw() const { return _redraw; }
 		virtual void setRedraw(bool redraw) { _redraw = redraw; }
 
 		virtual void reset();


Commit: 260b64db731a2b06d313aa702616afee4b9ff7d5
    https://github.com/scummvm/scummvm/commit/260b64db731a2b06d313aa702616afee4b9ff7d5
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: The Mac executables for Indy 3 and Loom are now required.

Changed paths:
    engines/scumm/scumm.cpp


diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 9905453f0ea..9a71635125c 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -1164,10 +1164,8 @@ Common::Error ScummEngine::init() {
 			}
 
 			if (macResourceFile.empty()) {
-				GUI::MessageDialog dialog(_(
-"Could not find the 'Indy' Macintosh executable. High-resolution fonts will\n"
-"be disabled."), _("OK"));
-				dialog.runModal();
+				return Common::Error(Common::kReadingFailed, _(
+"This game requires the 'Indy' Macintosh executable for its fonts."));
 			}
 
 		} else if (_game.id == GID_LOOM) {
@@ -1190,10 +1188,8 @@ Common::Error ScummEngine::init() {
 			}
 
 			if (macResourceFile.empty()) {
-				GUI::MessageDialog dialog(_(
-"Could not find the 'Loom' Macintosh executable. Music and high-resolution\n"
-"versions of font and cursor will be disabled."), _("OK"));
-				dialog.runModal();
+				return Common::Error(Common::kReadingFailed, _(
+"This game requires the 'Loom' Macintosh executable for its music and fonts."));
 			}
 		} else if (_game.id == GID_MONKEY) {
 			// Try both with and without underscore in the


Commit: 8402d2999c4148ad2d5252c11edde071b7da533f
    https://github.com/scummvm/scummvm/commit/8402d2999c4148ad2d5252c11edde071b7da533f
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Remove low-resolution fallback for Indy 3 Mac credit undrawing

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 5c63a244bc0..6a6671b732b 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -244,24 +244,11 @@ void ScummEngine::mac_undrawIndy3TextBox() {
 }
 
 void ScummEngine::mac_undrawIndy3CreditsText() {
-	if (_macScreen) {
-		// Set _masMask to make the text clear, and _textScreenID to
-		// ensure that it's the main area that's cleared. Note that
-		// this only works with the high-resolution font.
-		_charset->_hasMask = true;
-		_charset->_textScreenID = kMainVirtScreen;
-		restoreCharsetBg();
-	} else {
-		// The DOS VGA version clear the text by using the putState
-		// opcode. I would have been more comfortable if I could have
-		// compared it to the EGA version, but I don't have that.
-		// Judging by the size and position of the object, they should
-		// be the same.
-		putState(946, 0);
-		markObjectRectAsDirty(946);
-		if (_bgNeedsRedraw)
-			clearDrawObjectQueue();
-	}
+	// Set _masMask to make the text clear, and _textScreenID to ensure
+	// that it's the main area that's cleared.
+	_charset->_hasMask = true;
+	_charset->_textScreenID = kMainVirtScreen;
+	restoreCharsetBg();
 }
 
 void ScummEngine::mac_drawBorder(int x, int y, int w, int h, byte color) {


Commit: 131b5b8238874e98448f2e1733ac2d26e8524282
    https://github.com/scummvm/scummvm/commit/131b5b8238874e98448f2e1733ac2d26e8524282
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Remove another Indy 3 Mac hack

This shouldn't be needed any longer, since we're now passing objects,
not inventory verbs, to the script.

Changed paths:
    engines/scumm/script.cpp


diff --git a/engines/scumm/script.cpp b/engines/scumm/script.cpp
index a846fd11a87..25efbb6ae61 100644
--- a/engines/scumm/script.cpp
+++ b/engines/scumm/script.cpp
@@ -1428,26 +1428,8 @@ void ScummEngine::runInputScript(int clickArea, int val, int mode) {
 		_lastInputScriptTime = time;
 	}
 
-	if (verbScript) {
-		// It seems that script 18 expects to be called twice: Once
-		// with parameter 1 (kVerbClickArea) to clear all the verbs,
-		// and once with 3 (kInventoryClickArea) to print the "Take
-		// this <something>" message.
-		//
-		// In the 256-color DOS version, this is all done in the same
-		// call to the script.
-		//
-		// Should this workaround apply to other input scripts
-		// as well? I don't know.
-		if (_game.id == GID_INDY3 && _game.platform == Common::kPlatformMacintosh && verbScript == 18 && val >= 101 && val <= 106) {
-			args[0] = kVerbClickArea;
-			runScript(verbScript, 0, 0, args);
-
-			args[0] = kInventoryClickArea;
-			runScript(verbScript, 0, 0, args);
-		} else
-			runScript(verbScript, 0, 0, args);
-	}
+	if (verbScript)
+		runScript(verbScript, 0, 0, args);
 }
 
 void ScummEngine::decreaseScriptDelay(int amount) {


Commit: e54128eba96215771a3f7ac9d73720075d4ebea1
    https://github.com/scummvm/scummvm/commit/e54128eba96215771a3f7ac9d73720075d4ebea1
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Fix clicking on Indy 3 Mac inventory objects

I accidentally called the verb script twice.

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 6a6671b732b..3149e793fef 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -774,12 +774,8 @@ bool MacIndy3Gui::Inventory::updateTimer(int delta) {
 	VerbWidget::updateTimer(delta);
 
 	for (int i = 0; i < ARRAYSIZE(_slots); i++) {
-		Slot *s = _slots[i];
-
-		if (s->updateTimer(delta)) {
-			s->setRedraw(true);
-			_vm->runInputScript(kInventoryClickArea, s->getObject(), 1);
-		}
+		if (_slots[i]->updateTimer(delta))
+			return true;
 	}
 
 	for (int i = 0; i < ARRAYSIZE(_scrollButtons); i++) {


Commit: 2223c963952bc484461f8fa568541cbe846a1af4
    https://github.com/scummvm/scummvm/commit/2223c963952bc484461f8fa568541cbe846a1af4
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: More Indy 3 Mac GUI cleanups

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


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 3149e793fef..f229c7ecb3a 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -56,7 +56,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 (vs->number == kVerbVirtScreen && _macIndy3Gui->isGuiActive())
+	if (vs->number == kVerbVirtScreen && _macIndy3Gui->isVerbGuiActive())
 		return;
 
 	const byte *pixels = vs->getPixels(x, top);
@@ -365,8 +365,6 @@ Common::KeyState ScummEngine::mac_showOldStyleBannerAndPause(const char *msg, in
 // old savegames, but the variables are assumed to be harmless.
 // ===========================================================================
 
-#define DEBUG_VERB_SCRIPTS		1
-
 #define WIDGET_TIMER_JIFFIES	12
 #define REPEAT_TIMER_JIFFIES	18
 
@@ -392,15 +390,27 @@ void MacIndy3Gui::Widget::reset() {
 	setRedraw(false);
 }
 
-bool MacIndy3Gui::Widget::updateTimer(int delta) {
-	if (!hasTimer())
-		return false;
+void MacIndy3Gui::Widget::updateTimer(int delta) {
+	if (hasTimer()) {
+		if (delta > _timer)
+			delta = _timer;
+
+		_timer -= delta;
+
+		if (_timer == 0)
+			timeOut();
+	}
+}
 
-	if (delta > _timer)
-		delta = _timer;
+void MacIndy3Gui::Widget::draw() {
+	markScreenAsDirty(_bounds);
+	_redraw = false;
+}
 
-	_timer -= MIN(delta, _timer);
-	return _timer == 0;
+void MacIndy3Gui::Widget::undraw() {
+	fill(_bounds);
+	markScreenAsDirty(_bounds);
+	_redraw = false;
 }
 
 void MacIndy3Gui::Widget::markScreenAsDirty(Common::Rect r) const {
@@ -479,21 +489,15 @@ void MacIndy3Gui::VerbWidget::updateVerb(int verbslot) {
 }
 
 void MacIndy3Gui::VerbWidget::draw() {
-	// Clear the area used by the widget
-	fill(_bounds);
+	Widget::draw();
 	_visible = true;
-
-	// Don't copy to screen, because widgets are assumed to keep drawing
-	// after calling this. Also, don't call setRedraw() because there
-	// may be sub-widgets that still need drawing.
 }
 
 void MacIndy3Gui::VerbWidget::undraw() {
 	debug(1, "VerbWidget: Undrawing [%d]", _verbid);
 
+	Widget::undraw();
 	_visible = false;
-	fill(_bounds);
-	markScreenAsDirty(_bounds);
 }
 
 // ---------------------------------------------------------------------------
@@ -518,9 +522,8 @@ 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.hasFlags(Common::KBD_NON_STICKY) && event.kbd.keycode == vs->key)
 			caughtEvent = true;
-		}
 	} else if (event.type == Common::EVENT_LBUTTONDOWN) {
 		if (_bounds.contains(event.mouse))
 			caughtEvent = true;
@@ -540,17 +543,11 @@ bool MacIndy3Gui::Button::handleEvent(Common::Event &event) {
 	return caughtEvent;
 }
 
-bool MacIndy3Gui::Button::updateTimer(int delta) {
-	bool ret = VerbWidget::updateTimer(delta);
-
-	if (ret) {
-		if (_visible) {
-			_vm->runInputScript(kVerbClickArea, _verbid, 1);
-			setRedraw(true);
-		}
+void MacIndy3Gui::Button::timeOut() {
+	if (_visible) {
+		_vm->runInputScript(kVerbClickArea, _verbid, 1);
+		setRedraw(true);
 	}
-
-	return ret;
 }
 
 void MacIndy3Gui::Button::updateVerb(int verbslot) {
@@ -574,6 +571,7 @@ void MacIndy3Gui::Button::draw() {
 	debug(1, "Button: Drawing [%d] %s", _verbid, _text.c_str());
 
 	MacIndy3Gui::VerbWidget::draw();
+	fill(_bounds);
 
 	if (!hasTimer()) {
 		drawShadowBox(_bounds);
@@ -636,9 +634,6 @@ void MacIndy3Gui::Button::draw() {
 			x += boldFont->getCharWidth(_text[i]);
 		}
 	}
-
-	setRedraw(false);
-	markScreenAsDirty(_bounds);
 }
 
 // ---------------------------------------------------------------------------
@@ -770,23 +765,16 @@ bool MacIndy3Gui::Inventory::handleMouseHeld(Common::Point &pressed, Common::Poi
 	return false;
 }
 
-bool MacIndy3Gui::Inventory::updateTimer(int delta) {
-	VerbWidget::updateTimer(delta);
-
-	for (int i = 0; i < ARRAYSIZE(_slots); i++) {
-		if (_slots[i]->updateTimer(delta))
-			return true;
-	}
+void MacIndy3Gui::Inventory::updateTimer(int delta) {
+	Widget::updateTimer(delta);
 
-	for (int i = 0; i < ARRAYSIZE(_scrollButtons); i++) {
-		ScrollButton *b = _scrollButtons[i];
+	for (int i = 0; i < ARRAYSIZE(_slots); i++)
+		_slots[i]->updateTimer(delta);
 
-		if (b->updateTimer(delta)) {
-			b->setRedraw(true);
-		}
-	}
+	_scrollBar->updateTimer(delta);
 
-	return false;
+	for (int i = 0; i < ARRAYSIZE(_scrollButtons); i++)
+		_scrollButtons[i]->updateTimer(delta);
 }
 
 void MacIndy3Gui::Inventory::updateVerb(int verbslot) {
@@ -836,17 +824,8 @@ void MacIndy3Gui::Inventory::updateVerb(int verbslot) {
 
 	// Clear the remaining slots
 
-	for (int i = invSlot; i < numSlots; i++) {
-		Slot *s = _slots[i];
-
-		if (s->hasName()) {
-			s->clearName();
-			s->clearObject();
-			s->setEnabled(false);
-			s->clearTimer();
-			s->setRedraw(true);
-		}
-	}
+	for (int i = invSlot; i < numSlots; i++)
+		_slots[i]->clearObject();
 }
 
 void MacIndy3Gui::Inventory::draw() {
@@ -866,8 +845,6 @@ void MacIndy3Gui::Inventory::draw() {
 			drawBitmap(s->_bounds, arrow, kBlack);
 			s->draw();
 		}
-
-		markScreenAsDirty(_bounds);
 	}
 
 	// Since the inventory slots overlap, draw the highlighted ones (and
@@ -887,8 +864,6 @@ void MacIndy3Gui::Inventory::draw() {
 
 	for (int i = 0; i < ARRAYSIZE(_scrollButtons); i++)
 		_scrollButtons[i]->draw();
-
-	setRedraw(false);
 }
 
 // ---------------------------------------------------------------------------
@@ -901,13 +876,22 @@ MacIndy3Gui::Inventory::Slot::Slot(int slot, int x, int y, int width, int height
 
 void MacIndy3Gui::Inventory::Slot::reset() {
 	Widget::reset();
-	_name.clear();
+	clearName();
 	clearObject();
 }
 
+void MacIndy3Gui::Inventory::Slot::clearObject() {
+	_obj = -1;
+	setEnabled(false);
+
+	if (hasName()) {
+		clearName();
+		setRedraw(true);
+	}
+}
+
 void MacIndy3Gui::Inventory::Slot::setObject(int obj) {
 	_obj = obj;
-	setEnabled(true);
 
 	const byte *ptr = _vm->getObjOrActorName(obj);
 
@@ -916,11 +900,13 @@ void MacIndy3Gui::Inventory::Slot::setObject(int obj) {
 		_vm->convertMessageToString(ptr, buf, sizeof(buf));
 
 		if (_name != (const char *)buf) {
+			setEnabled(true);
 			_name = (const char *)buf;
 			clearTimer();
 			setRedraw(true);
 		}
 	} else if (hasName()) {
+		setEnabled(false);
 		clearName();
 		clearTimer();
 		setRedraw(true);
@@ -940,15 +926,9 @@ bool MacIndy3Gui::Inventory::Slot::handleEvent(Common::Event &event) {
 	return false;
 }
 
-bool MacIndy3Gui::Inventory::Slot::updateTimer(int delta) {
-	bool ret = Widget::updateTimer(delta);
-
-	if (ret) {
-		_vm->runInputScript(kInventoryClickArea, getObject(), 1);
-		setRedraw(true);
-	}
-
-	return ret;
+void MacIndy3Gui::Inventory::Slot::timeOut() {
+	_vm->runInputScript(kInventoryClickArea, getObject(), 1);
+	setRedraw(true);
 }
 
 void MacIndy3Gui::Inventory::Slot::draw() {
@@ -956,6 +936,9 @@ void MacIndy3Gui::Inventory::Slot::draw() {
 		return;
 
 	debug(1, "Inventory::Slot: Drawing [%d] %s", _slot, _name.c_str());
+
+	Widget::draw();
+
 	Color fg, bg;
 
 	if (hasTimer()) {
@@ -979,9 +962,6 @@ void MacIndy3Gui::Inventory::Slot::draw() {
 			x += font->getCharWidth(_name[i]);
 		}
 	}
-
-	setRedraw(false);
-	markScreenAsDirty(_bounds);
 }
 
 // ---------------------------------------------------------------------------
@@ -1067,6 +1047,8 @@ void MacIndy3Gui::Inventory::ScrollBar::draw() {
 		return;
 
 	debug(1, "Inventory::Scrollbar: Drawing");
+
+	Widget::draw();
 	drawShadowFrame(_bounds, kBlack, kBackground);
 
 	// The scrollbar handle is only drawn when there are enough inventory
@@ -1129,12 +1111,19 @@ bool MacIndy3Gui::Inventory::ScrollButton::handleMouseHeld(Common::Point &presse
 	return _bounds.contains(pressed);
 }
 
+void MacIndy3Gui::Inventory::ScrollButton::timeOut() {
+	// Nothing happens, but the button changes state.
+	setRedraw(true);
+}
+
 void MacIndy3Gui::Inventory::ScrollButton::draw() {
 	if (!getRedraw())
 		return;
 
 	debug(1, "Inventory::ScrollButton: Drawing [%d]", _direction);
 
+	Widget::draw();
+
 	const uint16 *arrow = (_direction == kScrollUp) ? _upArrow : _downArrow;
 	Color color = hasTimer() ? kBlack : kWhite;
 
@@ -1217,7 +1206,7 @@ void MacIndy3Gui::setInventoryScrollOffset(int n) const {
 	_vm->VAR(67) = n;
 }
 
-bool MacIndy3Gui::isGuiAllowed() const {
+bool MacIndy3Gui::isVerbGuiAllowed() const {
 	// The GUI is only allowed if the verb area has the expected size. That
 	// really seems to be all that's needed.
 
@@ -1228,14 +1217,14 @@ bool MacIndy3Gui::isGuiAllowed() const {
 	return true;
 }
 
-bool MacIndy3Gui::isGuiActive() const {
+bool MacIndy3Gui::isVerbGuiActive() const {
 	if (!_visible)
 		return false;
 
 	// The visibility flag may not have been updated yet, so better check
 	// that the GUI is still allowed.
 
-	return isGuiAllowed();
+	return isVerbGuiAllowed();
 }
 
 void MacIndy3Gui::resetAfterLoad() {
@@ -1258,7 +1247,7 @@ void MacIndy3Gui::resetAfterLoad() {
 }
 
 void MacIndy3Gui::update(int delta) {
-	if (!isGuiAllowed()) {
+	if (!isVerbGuiAllowed()) {
 		if (_visible)
 			hide();
 		return;
@@ -1286,6 +1275,7 @@ void MacIndy3Gui::update(int delta) {
 
 		if (delta > 0)
 			w->updateTimer(delta);
+
 		w->threaten();
 	}
 
@@ -1340,7 +1330,7 @@ void MacIndy3Gui::update(int delta) {
 }
 
 void MacIndy3Gui::handleEvent(Common::Event &event) {
-	if (!isGuiActive() || _vm->_userPut <= 0)
+	if (!isVerbGuiActive() || _vm->_userPut <= 0)
 		return;
 
 	if (event.type == Common::EVENT_LBUTTONDOWN) {
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index a042ef97ab3..65b8a161d2a 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -80,7 +80,7 @@ public:
 	// it's not drawing verbs, so the SCUMM engine is allowed to draw in
 	// the verb area to clear the power meters and text.
 
-	bool isGuiActive() const;
+	bool isVerbGuiActive() const;
 	const Graphics::Font *getFont(int n) const { return _fonts[n]; }
 
 	void resetAfterLoad();
@@ -141,10 +141,11 @@ private:
 
 		virtual bool handleEvent(Common::Event &event) = 0;
 		virtual bool handleMouseHeld(Common::Point &pressed, Common::Point &held) { return false; }
-		virtual bool updateTimer(int delta);
+		virtual void updateTimer(int delta);
+		virtual void timeOut() {}
 
-		virtual void draw() = 0;
-		virtual void undraw() {}
+		virtual void draw();
+		virtual void undraw();
 
 		// Primitives
 		void fill(Common::Rect r) const;
@@ -191,7 +192,7 @@ private:
 		bool handleEvent(Common::Event &event);
 
 		void reset();
-		bool updateTimer(int delta);
+		void timeOut();
 		void updateVerb(int verbslot);
 
 		void draw();
@@ -230,6 +231,7 @@ private:
 
 			bool handleEvent(Common::Event &event);
 			bool handleMouseHeld(Common::Point &pressed, Common::Point &held);
+			void timeOut();
 
 			void draw();
 		};
@@ -246,15 +248,14 @@ private:
 			void clearName() { _name.clear(); }
 			bool hasName() const { return !_name.empty(); }
 
-			void clearObject() { _obj = -1; }
-
+			void clearObject();
 			void setObject(int n);
 			int getObject() const { return _obj; }
 
 			void reset();
 
 			bool handleEvent(Common::Event &event);
-			bool updateTimer(int delta);
+			void timeOut();
 
 			void draw();
 		};
@@ -276,7 +277,7 @@ private:
 
 		bool handleEvent(Common::Event &event);
 		bool handleMouseHeld(Common::Point &pressed, Common::Point &held);
-		bool updateTimer(int delta);
+		void updateTimer(int delta);
 		void updateVerb(int verbslot);
 
 		void draw();
@@ -291,7 +292,7 @@ private:
 	const uint16 _llCorner[4] = { 0x8000, 0x8000, 0xC000, 0xF000 };
 	const uint16 _lrCorner[5] = { 0x1000, 0x1000, 0x3000, 0xF000 };
 
-	bool isGuiAllowed() const;
+	bool isVerbGuiAllowed() const;
 
 	void clear();
 	void show();
diff --git a/engines/scumm/input.cpp b/engines/scumm/input.cpp
index 4d1393eb9d8..3f1db3ff46c 100644
--- a/engines/scumm/input.cpp
+++ b/engines/scumm/input.cpp
@@ -106,7 +106,7 @@ void ScummEngine_v80he::parseEvent(Common::Event event) {
 
 void ScummEngine::parseEvent(Common::Event event) {
 	// Handle Mac Indy3 events before scaling the mouse coordinates.
-	if (_macIndy3Gui && _macIndy3Gui->isGuiActive())
+	if (_macIndy3Gui && _macIndy3Gui->isVerbGuiActive())
 		_macIndy3Gui->handleEvent(event);
 
 	switch (event.type) {
diff --git a/engines/scumm/verbs.cpp b/engines/scumm/verbs.cpp
index 398f08c2ae5..f40ec1ca7f7 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->isGuiActive())
+		if (_macIndy3Gui && _macIndy3Gui->isVerbGuiActive())
 			ignoreVerbKeys = true;
 
 		/* Check keypresses */
@@ -643,7 +643,7 @@ void ScummEngine::checkExecVerbs() {
 		if (!zone)
 			return;
 
-		if (_macIndy3Gui && _macIndy3Gui->isGuiActive() && zone->number == kVerbVirtScreen)
+		if (_macIndy3Gui && _macIndy3Gui->isVerbGuiActive() && zone->number == kVerbVirtScreen)
 			return;
 
 		over = findVerbAtPos(_mouse.x, _mouse.y);


Commit: c186a025a3a7564f1dab859311319b8e7efce503
    https://github.com/scummvm/scummvm/commit/c186a025a3a7564f1dab859311319b8e7efce503
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Redraw the Indy 3 Mac inventory scrollbar when necessary

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index f229c7ecb3a..b940a33918a 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -1001,6 +1001,9 @@ void MacIndy3Gui::Inventory::ScrollBar::setInventoryParameters(int invCount, int
 	if (invOffset != _invOffset)
 		setRedraw(true);
 
+	if (invCount != _invCount && _invCount >= ARRAYSIZE(_slots))
+		setRedraw(true);
+
 	_invCount = invCount;
 	_invOffset = invOffset;
 }


Commit: 0e3eac4c3497b4d113de199f54f47f3a22dcff9d
    https://github.com/scummvm/scummvm/commit/0e3eac4c3497b4d113de199f54f47f3a22dcff9d
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Add comment to Indy 3 Mac GUI

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index b940a33918a..70f5954c924 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -969,6 +969,9 @@ void MacIndy3Gui::Inventory::Slot::draw() {
 // more objects than are visible on screen.
 // ---------------------------------------------------------------------------
 
+// NB: This class makes several references to ARRAYSIZE(_slots), but accessing
+//     members of the enclosing class like that should be ok in C++11.
+
 MacIndy3Gui::Inventory::ScrollBar::ScrollBar(int x, int y, int width, int height) : MacIndy3Gui::Widget(x, y, width, height) {
 }
 


Commit: 274ebe2db432c6f97507d2e0f73350d4802c02f6
    https://github.com/scummvm/scummvm/commit/274ebe2db432c6f97507d2e0f73350d4802c02f6
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Moved Indy 3 Mac GUI "hiding" debug message

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 70f5954c924..bb9f6aa8166 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -1389,6 +1389,8 @@ void MacIndy3Gui::hide() {
 	if (!_visible)
 		return;
 
+	debug(1, "MacIndy3Gui: Hiding");
+
 	_visible = false;
 	_leftButtonIsPressed = false;
 	_timer = 0;
@@ -1399,8 +1401,6 @@ void MacIndy3Gui::hide() {
 	if (_vm->_virtscr[kVerbVirtScreen].topline != 144)
 		return;
 
-	debug(1, "MacIndy3Gui: Hiding");
-
 	_surface->fillRect(Common::Rect(0, 288, 640, 400), kBlack);
 	_system->copyRectToScreen(_surface->getBasePtr(0, 288), _surface->pitch, 0, 288, 640, 112);
 }


Commit: afcdabe18f197f130c1d16543b1e902b110833dc
    https://github.com/scummvm/scummvm/commit/afcdabe18f197f130c1d16543b1e902b110833dc
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Indy 3 Mac GUI dleanup

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 bb9f6aa8166..9889cac53da 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -643,16 +643,6 @@ void MacIndy3Gui::Button::draw() {
 // the scrollbar.
 // ---------------------------------------------------------------------------
 
-const uint16 MacIndy3Gui::Inventory::_upArrow[16] = {
-	0x0000, 0x0000, 0x0000, 0x0080,	0x0140, 0x0220, 0x0410, 0x0808,
-	0x1C1C, 0x0410, 0x0410, 0x0410,	0x07F0, 0x0000, 0x0000, 0x0000
-};
-
-const uint16 MacIndy3Gui::Inventory::_downArrow[16] = {
-	0x0000, 0x0000, 0x0000, 0x0000,	0x07F0, 0x0410, 0x0410, 0x0410,
-	0x1C1C, 0x0808, 0x0410, 0x0220,	0x0140, 0x0080, 0x0000, 0x0000
-};
-
 MacIndy3Gui::Inventory::Inventory(int x, int y, int width, int height) : MacIndy3Gui::VerbWidget(x, y, width, height) {
 	x = _bounds.left + 6;
 	y = _bounds.top + 6;
@@ -837,9 +827,23 @@ void MacIndy3Gui::Inventory::draw() {
 		drawShadowBox(_bounds);
 		drawShadowFrame(Common::Rect(_bounds.left + 4, _bounds.top + 4, _bounds.right - 22, _bounds.bottom - 4), kBlack, kWhite);
 
+		const uint16 upArrow[] = {
+			0x0000, 0x0000, 0x0000, 0x0080,
+			0x0140, 0x0220, 0x0410, 0x0808,
+			0x1C1C, 0x0410, 0x0410, 0x0410,
+			0x07F0, 0x0000, 0x0000, 0x0000
+		};
+
+		const uint16 downArrow[] = {
+			0x0000, 0x0000, 0x0000, 0x0000,
+			0x07F0, 0x0410, 0x0410, 0x0410,
+			0x1C1C, 0x0808, 0x0410, 0x0220,
+			0x0140, 0x0080, 0x0000, 0x0000
+		};
+
 		for (int i = 0; i < ARRAYSIZE(_scrollButtons); i++) {
 			ScrollButton *s = _scrollButtons[i];
-			const uint16 *arrow = (s->_direction == kScrollUp) ? _upArrow : _downArrow;
+			const uint16 *arrow = (s->_direction == kScrollUp) ? upArrow : downArrow;
 
 			drawShadowFrame(s->_bounds, kWhite, kTransparency);
 			drawBitmap(s->_bounds, arrow, kBlack);
@@ -1080,16 +1084,6 @@ void MacIndy3Gui::Inventory::ScrollBar::draw() {
 // since the rest of the buttons are drawn by the Inventory widget itself.
 // ---------------------------------------------------------------------------
 
-const uint16 MacIndy3Gui::Inventory::ScrollButton::_upArrow[16] = {
-	0x0000, 0x0000, 0x0000, 0x0000,	0x0080, 0x01C0, 0x03E0, 0x07F0,
-	0x03E0, 0x03E0, 0x03E0, 0x03E0,	0x0000, 0x0000, 0x0000, 0x0000
-};
-
-const uint16 MacIndy3Gui::Inventory::ScrollButton::_downArrow[16] = {
-	0x0000, 0x0000, 0x0000, 0x0000,	0x0000, 0x03E0, 0x03E0, 0x03E0,
-	0x03E0, 0x07F0,	0x03E0, 0x01C0,	0x0080, 0x0000, 0x0000, 0x0000
-};
-
 MacIndy3Gui::Inventory::ScrollButton::ScrollButton(int x, int y, int width, int height, ScrollDirection direction) : MacIndy3Gui::Widget(x, y, width, height) {
 	_direction = direction;
 }
@@ -1130,7 +1124,21 @@ void MacIndy3Gui::Inventory::ScrollButton::draw() {
 
 	Widget::draw();
 
-	const uint16 *arrow = (_direction == kScrollUp) ? _upArrow : _downArrow;
+	const uint16 upArrow[] = {
+		0x0000, 0x0000, 0x0000, 0x0000,
+		0x0080, 0x01C0, 0x03E0, 0x07F0,
+		0x03E0, 0x03E0, 0x03E0, 0x03E0,
+		0x0000, 0x0000, 0x0000, 0x0000
+	};
+
+	const uint16 downArrow[] = {
+		0x0000, 0x0000, 0x0000, 0x0000,
+		0x0000, 0x03E0, 0x03E0, 0x03E0,
+		0x03E0, 0x07F0,	0x03E0, 0x01C0,
+		0x0080, 0x0000, 0x0000, 0x0000
+	};
+
+	const uint16 *arrow = (_direction == kScrollUp) ? upArrow : downArrow;
 	Color color = hasTimer() ? kBlack : kWhite;
 
 	drawBitmap(_bounds, arrow, color);
@@ -1224,13 +1232,10 @@ bool MacIndy3Gui::isVerbGuiAllowed() const {
 }
 
 bool MacIndy3Gui::isVerbGuiActive() const {
-	if (!_visible)
-		return false;
-
 	// The visibility flag may not have been updated yet, so better check
 	// that the GUI is still allowed.
 
-	return isVerbGuiAllowed();
+	return _visible && isVerbGuiAllowed();
 }
 
 void MacIndy3Gui::resetAfterLoad() {
@@ -1253,26 +1258,21 @@ void MacIndy3Gui::resetAfterLoad() {
 }
 
 void MacIndy3Gui::update(int delta) {
-	if (!isVerbGuiAllowed()) {
+	if (isVerbGuiAllowed() && updateVerbs(delta)) {
+		if (!_visible)
+			show();
+
+		updateMouseHeldTimer(delta);
+		drawVerbs();
+	} else {
 		if (_visible)
 			hide();
-		return;
 	}
 
-	if (delta > 0 && _leftButtonIsPressed) {
-		_timer -= delta;
-
-		if (_timer <= 0) {
-			debug(2, "MacIndy3Gui: Left button still down");
-
-			_timer = REPEAT_TIMER_JIFFIES;
-
-			for (Common::HashMap<int, VerbWidget *>::iterator i = _widgets.begin(); i != _widgets.end(); ++i)
-				if (i->_value->handleMouseHeld(_leftButtonPressed, _leftButtonHeld))
-					break;
-		}
-	}
+	copyDirtyRectsToScreen();
+}
 
+bool MacIndy3Gui::updateVerbs(int delta) {
 	// Tentatively mark the verb widgets for removal. Any widget that wants
 	// to stay has to say so.
 
@@ -1285,7 +1285,7 @@ void MacIndy3Gui::update(int delta) {
 		w->threaten();
 	}
 
-	bool keepGuiAlive = false;
+	bool hasActiveVerbs = false;
 
 	// Collect all active verbs. Verb slot 0 is special, apparently, so we
 	// don't look at that one.
@@ -1298,7 +1298,7 @@ void MacIndy3Gui::update(int delta) {
 
 			if (w) {
 				w->updateVerb(i);
-				keepGuiAlive = true;
+				hasActiveVerbs = true;
 			} else {
 				const byte *ptr = _vm->getResourceAddress(rtVerb, i);
 				byte buf[270];
@@ -1308,13 +1308,27 @@ void MacIndy3Gui::update(int delta) {
 		}
 	}
 
-	if (!keepGuiAlive) {
-		hide();
-		return;
+	return hasActiveVerbs;
+}
+
+void MacIndy3Gui::updateMouseHeldTimer(int delta) {
+	if (delta > 0 && _leftButtonIsPressed) {
+		_timer -= delta;
+
+		if (_timer <= 0) {
+			debug(2, "MacIndy3Gui: Left button still down");
+
+			_timer = REPEAT_TIMER_JIFFIES;
+
+			for (Common::HashMap<int, VerbWidget *>::iterator i = _widgets.begin(); i != _widgets.end(); ++i)
+				if (i->_value->handleMouseHeld(_leftButtonPressed, _leftButtonHeld))
+					break;
+		}
 	}
+}
 
-	if (!_visible)
-		show();
+void MacIndy3Gui::drawVerbs() {
+	// The possible verbs overlap each other. Remove the dead ones first, then draw the live ones.
 
 	for (Common::HashMap<int, VerbWidget *>::iterator i = _widgets.begin(); i != _widgets.end(); ++i) {
 		VerbWidget *w = i->_value;
@@ -1331,8 +1345,6 @@ void MacIndy3Gui::update(int delta) {
 		if (w->hasVerb())
 			w->draw();
 	}
-
-	copyDirtyRectsToScreen();
 }
 
 void MacIndy3Gui::handleEvent(Common::Event &event) {
@@ -1381,8 +1393,24 @@ void MacIndy3Gui::show() {
 
 	debug(1, "MacIndy3Gui: Showing");
 
-	clear();
 	_visible = true;
+
+	_surface->fillRect(Common::Rect(0, 288, 640, 289), kBlack);
+	_surface->fillRect(Common::Rect(0, 373, 640, 400), kBlack);
+
+	fill(Common::Rect(0, 290, 640, 373));
+
+	const uint16 ulCorner[] = { 0xF000, 0xC000, 0x8000, 0x8000 };
+	const uint16 urCorner[] = { 0xF000, 0x3000, 0x1000, 0x1000 };
+	const uint16 llCorner[] = { 0x8000, 0x8000, 0xC000, 0xF000 };
+	const uint16 lrCorner[] = { 0x1000, 0x1000, 0x3000, 0xF000 };
+
+	drawBitmap(Common::Rect(  0, 290,   4, 294), ulCorner, kBlack);
+	drawBitmap(Common::Rect(636, 290, 640, 294), urCorner, kBlack);
+	drawBitmap(Common::Rect(  0, 369,   4, 373), llCorner, kBlack);
+	drawBitmap(Common::Rect(636, 369, 640, 373), lrCorner, kBlack);
+
+	markScreenAsDirty(Common::Rect(0, 288, 640, 400));
 }
 
 void MacIndy3Gui::hide() {
@@ -1398,24 +1426,10 @@ void MacIndy3Gui::hide() {
 	for (Common::HashMap<int, VerbWidget *>::iterator i = _widgets.begin(); i != _widgets.end(); ++i)
 		i->_value->reset();
 
-	if (_vm->_virtscr[kVerbVirtScreen].topline != 144)
-		return;
-
-	_surface->fillRect(Common::Rect(0, 288, 640, 400), kBlack);
-	_system->copyRectToScreen(_surface->getBasePtr(0, 288), _surface->pitch, 0, 288, 640, 112);
-}
-
-void MacIndy3Gui::clear() {
-	_surface->fillRect(Common::Rect(0, 288, 640, 289), kBlack);
-	_surface->fillRect(Common::Rect(0, 373, 640, 400), kBlack);
-
-	fill(Common::Rect(0, 290, 640, 373));
-	drawBitmap(Common::Rect(  0, 290,   4, 294), _ulCorner, kBlack);
-	drawBitmap(Common::Rect(636, 290, 640, 294), _urCorner, kBlack);
-	drawBitmap(Common::Rect(  0, 369,   4, 373), _llCorner, kBlack);
-	drawBitmap(Common::Rect(636, 369, 640, 373), _lrCorner, kBlack);
-
-	markScreenAsDirty(Common::Rect(0, 288, 640, 400));
+	if (isVerbGuiAllowed()) {
+		_surface->fillRect(Common::Rect(0, 288, 640, 400), kBlack);
+		markScreenAsDirty(Common::Rect(0, 288, 640, 400));
+	}
 }
 
 void MacIndy3Gui::markScreenAsDirty(Common::Rect r) {
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 65b8a161d2a..7d42e89ff82 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -104,6 +104,10 @@ private:
 
 	int _timer = 0;
 
+	bool updateVerbs(int delta);
+	void updateMouseHeldTimer(int delta);
+	void drawVerbs();
+
 	class Widget {
 	private:
 		int _timer = 0;
@@ -220,10 +224,6 @@ private:
 		};
 
 		class ScrollButton : public Widget {
-		private:
-			static const uint16 _upArrow[16];
-			static const uint16 _downArrow[16];
-
 		public:
 			ScrollDirection _direction;
 
@@ -284,17 +284,10 @@ private:
 	};
 
 	Common::HashMap<int, VerbWidget *> _widgets;
-
 	Common::Array<Common::Rect> _dirtyRects;
 
-	const uint16 _ulCorner[4] = { 0xF000, 0xC000, 0x8000, 0x8000 };
-	const uint16 _urCorner[4] = { 0xF000, 0x3000, 0x1000, 0x1000 };
-	const uint16 _llCorner[4] = { 0x8000, 0x8000, 0xC000, 0xF000 };
-	const uint16 _lrCorner[5] = { 0x1000, 0x1000, 0x3000, 0xF000 };
-
 	bool isVerbGuiAllowed() const;
 
-	void clear();
 	void show();
 	void hide();
 


Commit: 175d53905cfd2e9f119599a836968715781fb276
    https://github.com/scummvm/scummvm/commit/175d53905cfd2e9f119599a836968715781fb276
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Increased savegame version

The new Mac GUI doesn't change the savegame format, but it removes a
couple of verbs. It can read old savegames just fine, but new ones will
not be usable by older versions.

Changed paths:
    engines/scumm/saveload.cpp


diff --git a/engines/scumm/saveload.cpp b/engines/scumm/saveload.cpp
index 868be4c6f3e..606facdaed5 100644
--- a/engines/scumm/saveload.cpp
+++ b/engines/scumm/saveload.cpp
@@ -68,7 +68,7 @@ struct SaveInfoSection {
 
 #define SaveInfoSectionSize (4+4+4 + 4+4 + 4+2)
 
-#define CURRENT_VER 109
+#define CURRENT_VER 110
 #define INFOSECTION_VERSION 2
 
 #pragma mark -


Commit: 9902d8e1338e0246f0506765499d2b4a21a4ce2f
    https://github.com/scummvm/scummvm/commit/9902d8e1338e0246f0506765499d2b4a21a4ce2f
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Indy 3 Mac GUI cleanup

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 9889cac53da..e28088b64c7 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -1197,15 +1197,15 @@ MacIndy3Gui::MacIndy3Gui(OSystem *system, ScummEngine *vm) :
 	_widgets[124] = new Button( 67, 352, 151, 18); // Converse 5
 	_widgets[125] = new Button(423, 352, 151, 18); // Converse 6
 
-	for (Common::HashMap<int, VerbWidget *>::iterator i = _widgets.begin(); i != _widgets.end(); ++i)
-		i->_value->setVerbid(i->_key);
+	for (auto &it: _widgets)
+		it._value->setVerbid(it._key);
 
 	_dirtyRects.clear();
 }
 
 MacIndy3Gui::~MacIndy3Gui() {
-	for (Common::HashMap<int, VerbWidget *>::iterator i = _widgets.begin(); i != _widgets.end(); ++i)
-		delete i->_value;
+	for (auto &it: _widgets)
+		delete it._value;
 }
 
 // Before the GUI rewrite, the scroll offset was saved in variable 67. Let's
@@ -1241,8 +1241,8 @@ bool MacIndy3Gui::isVerbGuiActive() const {
 void MacIndy3Gui::resetAfterLoad() {
 	_visible = false;
 
-	for (Common::HashMap<int, VerbWidget *>::iterator i = _widgets.begin(); i != _widgets.end(); ++i)
-		i->_value->reset();
+	for (auto &it: _widgets)
+		it._value->reset();
 
 	// In the DOS version, verb ID 102-106 were used for the visible
 	// inventory items, and 107-108 for inventory arrow buttons. In the
@@ -1276,8 +1276,8 @@ bool MacIndy3Gui::updateVerbs(int delta) {
 	// Tentatively mark the verb widgets for removal. Any widget that wants
 	// to stay has to say so.
 
-	for (Common::HashMap<int, VerbWidget *>::iterator i = _widgets.begin(); i != _widgets.end(); ++i) {
-		VerbWidget *w = i->_value;
+	for (auto &it: _widgets) {
+		VerbWidget *w = it._value;
 
 		if (delta > 0)
 			w->updateTimer(delta);
@@ -1320,9 +1320,10 @@ void MacIndy3Gui::updateMouseHeldTimer(int delta) {
 
 			_timer = REPEAT_TIMER_JIFFIES;
 
-			for (Common::HashMap<int, VerbWidget *>::iterator i = _widgets.begin(); i != _widgets.end(); ++i)
-				if (i->_value->handleMouseHeld(_leftButtonPressed, _leftButtonHeld))
+			for (auto &it: _widgets) {
+				if (it._value->handleMouseHeld(_leftButtonPressed, _leftButtonHeld))
 					break;
+			}
 		}
 	}
 }
@@ -1330,8 +1331,8 @@ void MacIndy3Gui::updateMouseHeldTimer(int delta) {
 void MacIndy3Gui::drawVerbs() {
 	// The possible verbs overlap each other. Remove the dead ones first, then draw the live ones.
 
-	for (Common::HashMap<int, VerbWidget *>::iterator i = _widgets.begin(); i != _widgets.end(); ++i) {
-		VerbWidget *w = i->_value;
+	for (auto &it: _widgets) {
+		VerbWidget *w = it._value;
 
 		if (w->isDying() && w->isVisible()) {
 			w->undraw();
@@ -1339,8 +1340,8 @@ void MacIndy3Gui::drawVerbs() {
 		}
 	}
 
-	for (Common::HashMap<int, VerbWidget *>::iterator i = _widgets.begin(); i != _widgets.end(); ++i) {
-		VerbWidget *w = i->_value;
+	for (auto &it: _widgets) {
+		VerbWidget *w = it._value;
 
 		if (w->hasVerb())
 			w->draw();
@@ -1376,10 +1377,12 @@ void MacIndy3Gui::handleEvent(Common::Event &event) {
 	// responds to an event, and marks itself as wanting to be redraw,
 	// we do that redrawing immediately, not on the next update.
 
-	for (Common::HashMap<int, VerbWidget *>::iterator i = _widgets.begin(); i != _widgets.end(); ++i) {
-		if (i->_value->handleEvent(event)) {
-			if (i->_value->getRedraw()) {
-				i->_value->draw();
+	for (auto &it: _widgets) {
+		Widget *w = it._value;
+
+		if (w->handleEvent(event)) {
+			if (w->getRedraw()) {
+				w->draw();
 				copyDirtyRectsToScreen();
 			}
 			break;
@@ -1423,8 +1426,8 @@ void MacIndy3Gui::hide() {
 	_leftButtonIsPressed = false;
 	_timer = 0;
 
-	for (Common::HashMap<int, VerbWidget *>::iterator i = _widgets.begin(); i != _widgets.end(); ++i)
-		i->_value->reset();
+	for (auto &it: _widgets)
+		it._value->reset();
 
 	if (isVerbGuiAllowed()) {
 		_surface->fillRect(Common::Rect(0, 288, 640, 400), kBlack);


Commit: ee120bbd520a630e79ef685dd16af4d533c7578b
    https://github.com/scummvm/scummvm/commit/ee120bbd520a630e79ef685dd16af4d533c7578b
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Fix double-clicking on Indy 3 Mac inventory items

If the timer is already running, time it out before starting a new one.

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index e28088b64c7..3fce12a5b08 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -923,6 +923,8 @@ bool MacIndy3Gui::Inventory::Slot::handleEvent(Common::Event &event) {
 
 	if (_bounds.contains(event.mouse)) {
 		setRedraw(true);
+		if (hasTimer())
+			timeOut();
 		setTimer(WIDGET_TIMER_JIFFIES);
 		return true;
 	}


Commit: b84fb46c492b8794565e41398a57b3e82386c655
    https://github.com/scummvm/scummvm/commit/b84fb46c492b8794565e41398a57b3e82386c655
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Fix Indy 3 Mac scrollbar clicking behavior to be like the original

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 3fce12a5b08..90c7aabc515 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -985,22 +985,25 @@ bool MacIndy3Gui::Inventory::ScrollBar::handleEvent(Common::Event &event) {
 	if (!_enabled || event.type != Common::EVENT_LBUTTONDOWN)
 		return false;
 
-	// I'm guessing that clicking on the scrollbar is supposed to scroll one
-	// page at a time, but I haven't had enough inventory items for this to
-	// mean anything other than scrolling to the top or the bottom.
+	// Clicking on the scrollbar scrolls it to the top or the bottom, not
+	// one page as one might suspect. Though you're rarely carrying enough
+	// objects for this to make a difference.
 	//
-	// The direction depends on if you click above or below the handle. The
-	// original also allowed you to click on the handle itself, but the
-	// behavior was hard to predict so we ignore those completely.
+	// The direction depends on if you click above or below the handle.
+	// Clicking on the handle also works, though the behavior strikes me
+	// as a bit unintuitive. If you click on Y coordinate pos + 5, nothing
+	// happens at all.
 
 	if (_bounds.contains(event.mouse)) {
 		int pos = _bounds.top + getHandlePosition();
-		int numSlots = ARRAYSIZE(_slots);
 
-		if (event.mouse.y < pos)
-			moveInvOffset(-numSlots);
-		else if (event.mouse.y >= pos + 8)
-			moveInvOffset(numSlots);
+		if (event.mouse.y <= pos + 4)
+			_invOffset = 0;
+		else if (event.mouse.y >= pos + 6)
+			_invOffset = _invCount - ARRAYSIZE(_slots);
+
+		_gui->setInventoryScrollOffset(_invOffset);
+		setRedraw(true);
 	}
 
 	return false;


Commit: 508ade605c109e33e00c37a8d16527a379a7fccc
    https://github.com/scummvm/scummvm/commit/508ade605c109e33e00c37a8d16527a379a7fccc
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Indy 3 Mac GUI cleanup

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 90c7aabc515..34985d23c31 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -717,7 +717,7 @@ bool MacIndy3Gui::Inventory::handleEvent(Common::Event &event) {
 		// hold down the mouse button to repeat.
 
 		if (b->handleEvent(event)) {
-			_scrollBar->moveInvOffset((b->_direction == kScrollUp) ? -1 : 1);
+			_scrollBar->scroll(b->_direction);
 			return true;
 		}
 	}
@@ -741,7 +741,7 @@ bool MacIndy3Gui::Inventory::handleMouseHeld(Common::Point &pressed, Common::Poi
 		ScrollButton *b = _scrollButtons[i];
 
 		if (b->handleMouseHeld(pressed, held)) {
-			_scrollBar->moveInvOffset((b->_direction == kScrollUp) ? -1 : 1);
+			_scrollBar->scroll(b->_direction);
 			return true;
 		}
 	}
@@ -1020,10 +1020,15 @@ void MacIndy3Gui::Inventory::ScrollBar::setInventoryParameters(int invCount, int
 	_invOffset = invOffset;
 }
 
-void MacIndy3Gui::Inventory::ScrollBar::moveInvOffset(int offset) {
-	int newOffset = _invOffset + offset;
+void MacIndy3Gui::Inventory::ScrollBar::scroll(ScrollDirection dir) {
+	int newOffset = _invOffset;
 	int maxOffset = _invCount - ARRAYSIZE(_slots);
 
+	if (dir == kScrollUp)
+		newOffset--;
+	else
+		newOffset++;
+
 	if (newOffset < 0)
 		newOffset = 0;
 	else if (newOffset > maxOffset)
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 7d42e89ff82..a78eaa85171 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -213,7 +213,7 @@ private:
 			ScrollBar(int x, int y, int width, int height);
 
 			void setInventoryParameters(int invCount, int invOffset);
-			void moveInvOffset(int offset);
+			void scroll(ScrollDirection dir);
 			int getHandlePosition();
 
 			void reset();


Commit: 38852234ece6d32efc3a75426e0a258463ee4d3a
    https://github.com/scummvm/scummvm/commit/38852234ece6d32efc3a75426e0a258463ee4d3a
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Fix printing of '...' character in Indy 3 Mac GUI

The SCUMM engine uses "^" to represent "..." so it has to be remapped to
the appropriate Macintosh character 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 34985d23c31..7ab6bdc5bc9 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -417,6 +417,12 @@ void MacIndy3Gui::Widget::markScreenAsDirty(Common::Rect r) const {
 	_gui->markScreenAsDirty(r);
 }
 
+byte MacIndy3Gui::Widget::translateChar(byte c) const {
+	if (c == '^')
+		return 0xC9;
+	return c;
+}
+
 void MacIndy3Gui::Widget::fill(Common::Rect r) const {
 	_gui->fill(r);
 }
@@ -626,12 +632,13 @@ void MacIndy3Gui::Button::draw() {
 		}
 
 		for (uint i = 0; i < _text.size() && x < _bounds.right; i++) {
+			byte c = translateChar(_text[i]);
 			if (x >= _bounds.left) {
 				if (_enabled)
-					outlineFont->drawChar(_surface, _text[i], x, y, kBlack);
-				boldFont->drawChar(_surface, _text[i], x + 1, y, color);
+					outlineFont->drawChar(_surface, c, x, y, kBlack);
+				boldFont->drawChar(_surface, c, x + 1, y, color);
 			}
-			x += boldFont->getCharWidth(_text[i]);
+			x += boldFont->getCharWidth(c);
 		}
 	}
 }
@@ -964,8 +971,10 @@ void MacIndy3Gui::Inventory::Slot::draw() {
 		int x = _bounds.left + 4;
 
 		for (uint i = 0; i < _name.size() && x < _bounds.right; i++) {
-			font->drawChar(_surface, _name[i], x, y, fg);
-			x += font->getCharWidth(_name[i]);
+			byte c = translateChar(_name[i]);
+
+			font->drawChar(_surface, c, x, y, fg);
+			x += font->getCharWidth(c);
 		}
 	}
 }
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index a78eaa85171..ea2ac5dbeb1 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -151,6 +151,8 @@ private:
 		virtual void draw();
 		virtual void undraw();
 
+		byte translateChar(byte c) const;
+
 		// Primitives
 		void fill(Common::Rect r) const;
 		void drawBitmap(Common::Rect r, const uint16 *bitmap, Color color) const;


Commit: a11092859c583219c51b13387197a7288eb06f70
    https://github.com/scummvm/scummvm/commit/a11092859c583219c51b13387197a7288eb06f70
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Fix Mac Loom regression

Changed paths:
    engines/scumm/gfx_mac.cpp


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 7ab6bdc5bc9..76f8b92d646 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -56,7 +56,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 (vs->number == kVerbVirtScreen && _macIndy3Gui->isVerbGuiActive())
+	if (_macIndy3Gui && vs->number == kVerbVirtScreen && _macIndy3Gui->isVerbGuiActive())
 		return;
 
 	const byte *pixels = vs->getPixels(x, top);


Commit: 12738d88565347fb4712ce3c558b4346239506f3
    https://github.com/scummvm/scummvm/commit/12738d88565347fb4712ce3c558b4346239506f3
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Create _macIndy3Gui earlier, not in resetScumm()

Since this is before the Mac font manager is created, load fonts when
requested rather than pre-loading them all.

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


diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index 76f8b92d646..638ecb49d43 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -1173,11 +1173,6 @@ void MacIndy3Gui::Inventory::ScrollButton::draw() {
 
 MacIndy3Gui::MacIndy3Gui(OSystem *system, ScummEngine *vm) :
 	_system(system), _vm(vm), _surface(vm->_macScreen), _visible(false) {
-	Graphics::MacFontManager *mfm = _vm->_macFontManager;
-
-	_fonts[kRegular] = mfm->getFont(Graphics::MacFont(Graphics::kMacFontGeneva, 9));
-	_fonts[kBold] = mfm->getFont(Graphics::MacFont(Graphics::kMacFontGeneva, 9, Graphics::kMacFontBold));
-	_fonts[kOutline] = mfm->getFont(Graphics::MacFont(Graphics::kMacFontGeneva, 9, Graphics::kMacFontBold | Graphics::kMacFontOutline | Graphics::kMacFontCondense));
 
 	// There is one widget for every verb in the game. Verbs include the
 	// inventory widget and conversation options.
@@ -1186,6 +1181,9 @@ MacIndy3Gui::MacIndy3Gui(OSystem *system, ScummEngine *vm) :
 	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
@@ -1257,11 +1255,43 @@ bool MacIndy3Gui::isVerbGuiActive() const {
 	return _visible && isVerbGuiAllowed();
 }
 
-void MacIndy3Gui::resetAfterLoad() {
+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;
 
 	for (auto &it: _widgets)
 		it._value->reset();
+}
+
+void MacIndy3Gui::resetAfterLoad() {
+	reset();
 
 	// In the DOS version, verb ID 102-106 were used for the visible
 	// inventory items, and 107-108 for inventory arrow buttons. In the
@@ -1441,12 +1471,10 @@ void MacIndy3Gui::hide() {
 
 	debug(1, "MacIndy3Gui: Hiding");
 
-	_visible = false;
 	_leftButtonIsPressed = false;
 	_timer = 0;
 
-	for (auto &it: _widgets)
-		it._value->reset();
+	reset();
 
 	if (isVerbGuiAllowed()) {
 		_surface->fillRect(Common::Rect(0, 288, 640, 400), kBlack);
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index ea2ac5dbeb1..6c44db20492 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -81,8 +81,9 @@ public:
 	// the verb area to clear the power meters and text.
 
 	bool isVerbGuiActive() const;
-	const Graphics::Font *getFont(int n) const { return _fonts[n]; }
+	const Graphics::Font *getFont(FontId fontId);
 
+	void reset();
 	void resetAfterLoad();
 	void update(int delta);
 	void handleEvent(Common::Event &event);
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 9a71635125c..071dca20fb1 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -1159,6 +1159,7 @@ Common::Error ScummEngine::init() {
 
 					_macIndy3TextBox = new Graphics::Surface();
 					_macIndy3TextBox->create(448, 47, Graphics::PixelFormat::createFormatCLUT8());
+					_macIndy3Gui = new MacIndy3Gui(_system, this);
 					break;
 				}
 			}
@@ -1675,9 +1676,9 @@ void ScummEngine::resetScumm() {
 		_macScreen->fillRect(Common::Rect(_macScreen->w, _macScreen->h), 0);
 	}
 
-	if (_macIndy3TextBox) {
+	if (_macIndy3Gui) {
 		_macIndy3TextBox->fillRect(Common::Rect(_macIndy3TextBox->w, _macIndy3TextBox->h), 0);
-		_macIndy3Gui = new MacIndy3Gui(_system, this);
+		_macIndy3Gui->reset();
 	}
 
 	if (_game.version == 0) {


Commit: 2867f9715bac122e8bd870e271b77487ccd7b6d4
    https://github.com/scummvm/scummvm/commit/2867f9715bac122e8bd870e271b77487ccd7b6d4
Author: Torbjörn Andersson (eriktorbjorn at users.sourceforge.net)
Date: 2023-10-16T17:40:29+02:00

Commit Message:
SCUMM: Minor Indy 3 Mac GUI cleanups from bluegr's review

I take it as a good sign that this is the only feedback so far. :-)

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


diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp
index 0ab392fccda..509ea4f7eaf 100644
--- a/engines/scumm/charset.cpp
+++ b/engines/scumm/charset.cpp
@@ -1581,7 +1581,7 @@ CharsetRendererMac::CharsetRendererMac(ScummEngine *vm, const Common::String &fo
 
 	mfm->loadFonts(fontFile);
 
-	Common::String fontFamily = (_vm->_game.id == GID_LOOM) ? "Loom" : "Indy";
+	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++) {
diff --git a/engines/scumm/gfx_mac.h b/engines/scumm/gfx_mac.h
index 6c44db20492..5ab6d3dd52c 100644
--- a/engines/scumm/gfx_mac.h
+++ b/engines/scumm/gfx_mac.h
@@ -72,7 +72,7 @@ public:
 
 	// 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 scren. From
+	// it necessarily is. Active means that there are verbs on screen. From
 	// the outside, only the latter is relevant.
 	//
 	// One case where this makes a difference is when boxing with the




More information about the Scummvm-git-logs mailing list