diff --git a/Changes.txt b/Changes.txt index f82eefac1..95620c946 100644 --- a/Changes.txt +++ b/Changes.txt @@ -1,3 +1,12 @@ +5.1 to 5.2: (MMM d, 2018) + + * Extra functionality for Time Machine dialog (start/stop recording; + minor fixes; TODO button and initial key repeats...) + + * Fixes for collision corner cases (during HBlank) + + * UI modernization (new widget look, dialog titles added, dialogs refactored) + 5.0.2 to 5.1: (February 4, 2018) * Added "Time Machine" mode, which automatically creates save states diff --git a/src/common/Stack.hxx b/src/common/Stack.hxx index c7fee5221..b3a84c7ef 100644 --- a/src/common/Stack.hxx +++ b/src/common/Stack.hxx @@ -44,20 +44,11 @@ class FixedStack bool full() const { return _size >= CAPACITY; } T top() const { return _stack[_size - 1]; } - T get(uInt32 pos) { return _stack[pos]; }; + T get(uInt32 pos) { return _stack[pos]; } void push(const T& x) { _stack[_size++] = x; } T pop() { return std::move(_stack[--_size]); } uInt32 size() const { return _size; } - void replace(const T& oldItem, const T& newItem) { - for(uInt32 i = 0; i < _size; ++i) { - if(_stack[i] == oldItem) { - _stack[i] = newItem; - return; - } - } - } - // Apply the given function to every item in the stack // We do it this way so the stack API can be preserved, // and no access to individual elements is allowed outside diff --git a/src/common/Version.hxx b/src/common/Version.hxx index e1c46ca47..ac15e5f7b 100644 --- a/src/common/Version.hxx +++ b/src/common/Version.hxx @@ -18,7 +18,7 @@ #ifndef VERSION_HXX #define VERSION_HXX -#define STELLA_VERSION "5.1" +#define STELLA_VERSION "5.2_pre" #define STELLA_BUILD "4138" #endif diff --git a/src/debugger/gui/PaddleWidget.cxx b/src/debugger/gui/PaddleWidget.cxx index 42f6fe0a4..943f65855 100644 --- a/src/debugger/gui/PaddleWidget.cxx +++ b/src/debugger/gui/PaddleWidget.cxx @@ -26,8 +26,7 @@ PaddleWidget::PaddleWidget(GuiObject* boss, const GUI::Font& font, bool leftport = isLeftPort(); const string& label = getHeader(); - const int fontWidth = font.getMaxCharWidth(), - fontHeight = font.getFontHeight(), + const int fontHeight = font.getFontHeight(), lineHeight = font.getLineHeight(); int xpos = x, ypos = y, lwidth = font.getStringWidth("Right (Paddles)"); diff --git a/src/emucore/EventHandler.cxx b/src/emucore/EventHandler.cxx index bd1316f07..76d045e06 100644 --- a/src/emucore/EventHandler.cxx +++ b/src/emucore/EventHandler.cxx @@ -301,19 +301,19 @@ void EventHandler::handleKeyEvent(StellaKey key, StellaMod mod, bool state) { case KBDK_LEFT: // Alt-left(-shift) rewinds 1(10) states enterTimeMachineMenuMode((StellaModTest::isShift(mod) && state) ? 10 : 1, false); - return; + break; case KBDK_RIGHT: // Alt-right(-shift) unwinds 1(10) states enterTimeMachineMenuMode((StellaModTest::isShift(mod) && state) ? 10 : 1, true); - return; + break; case KBDK_DOWN: // Alt-down rewinds to start of list enterTimeMachineMenuMode(1000, false); - return; + break; case KBDK_UP: // Alt-up rewinds to end of list enterTimeMachineMenuMode(1000, true); - return; + break; // These can work in pause mode too case KBDK_EQUALS: @@ -2151,13 +2151,11 @@ void EventHandler::enterTimeMachineMenuMode(uInt32 numWinds, bool unwind) // add one extra state if we are in Time Machine mode // TODO: maybe remove this state if we leave the menu at this new state myOSystem.state().addExtraState("enter Time Machine dialog"); // force new state - if(numWinds) - myOSystem.state().windStates(numWinds, unwind); if(numWinds) - myOSystem.state().windStates(numWinds, unwind); + // hande winds and display wind message (numWinds != 0) in time machine dialog + myOSystem.timeMachine().setEnterWinds(unwind ? numWinds : -numWinds); - // TODO: display last wind message (numWinds != 0) in time machine dialog enterMenuMode(EventHandlerState::TIMEMACHINE); } diff --git a/src/emucore/FBSurface.cxx b/src/emucore/FBSurface.cxx index ef93e0dfc..a1daac66f 100644 --- a/src/emucore/FBSurface.cxx +++ b/src/emucore/FBSurface.cxx @@ -268,14 +268,12 @@ void FBSurface::frameRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h, break; case FrameStyle::Dashed: - uInt32 i, skip, lwidth = 1; - - for(i = x; i < x + w; i += 2) + for(uInt32 i = x; i < x + w; i += 2) { hLine(i, y, i, color); hLine(i, y + h - 1, i, color); } - for(i = y; i < y + h; i += 2) + for(uInt32 i = y; i < y + h; i += 2) { vLine(x, i, i, color); vLine(x + w - 1, i, i, color); diff --git a/src/emucore/FrameBuffer.cxx b/src/emucore/FrameBuffer.cxx index aa1946e39..ad517f37c 100644 --- a/src/emucore/FrameBuffer.cxx +++ b/src/emucore/FrameBuffer.cxx @@ -385,9 +385,8 @@ void FrameBuffer::drawFrameStats() int xPos = XPOS; myStatsMsg.surface->invalidate(); - string bsinfo = info.BankSwitch + - (myOSystem.settings().getBool("dev.settings") ? "| Developer" : ""); - // draw shadowed text + + // draw scanlines color = myOSystem.console().tia().scanlinesLastFrame() != myLastScanlines ? uInt32(kDbgColorRed) : myStatsMsg.color; std::snprintf(msg, 30, "%3u", myOSystem.console().tia().scanlinesLastFrame()); @@ -395,16 +394,20 @@ void FrameBuffer::drawFrameStats() myStatsMsg.w, color, TextAlign::Left, 0, true, kBGColor); xPos += font().getStringWidth(msg); + // draw frequency std::snprintf(msg, 30, " => %s", info.DisplayFormat.c_str()); myStatsMsg.surface->drawString(font(), msg, xPos, YPOS, myStatsMsg.w, myStatsMsg.color, TextAlign::Left, 0, true, kBGColor); xPos += font().getStringWidth(msg); std::snprintf(msg, 30, " @ %5.2ffps", myOSystem.console().getFramerate()); + myStatsMsg.surface->drawString(font(), msg, xPos, YPOS, myStatsMsg.w, myStatsMsg.color, TextAlign::Left, 0, true, kBGColor); // draw bankswitching type + string bsinfo = info.BankSwitch + + (myOSystem.settings().getBool("dev.settings") ? "| Developer" : ""); myStatsMsg.surface->drawString(font(), bsinfo, XPOS, YPOS + font().getFontHeight(), myStatsMsg.w, myStatsMsg.color, TextAlign::Left, 0, true, kBGColor); @@ -1016,7 +1019,7 @@ uInt32 FrameBuffer::ourGUIColors[3][kNumColors-256] = { 0x20a020, 0x00ff00, // scrollbar 0x20a020, 0x00ff00, 0x404040, 0x686868, 0x404040, // slider 0xc80000, 0x00ff00, 0xc8c8ff, 0xc80000, // debugger - 0x20a020, 0x20a020, 0x000000, 0x686868, 0x404040 // other + 0x00ff00, 0x20a020, 0x000000, 0x686868, 0x404040 // other }, // Light { 0x808080, 0x000000, 0xc0c0c0, 0xe1e1e1, 0x333333, // base diff --git a/src/emucore/OSystem.cxx b/src/emucore/OSystem.cxx index 93d21928e..4b7f52512 100644 --- a/src/emucore/OSystem.cxx +++ b/src/emucore/OSystem.cxx @@ -213,6 +213,56 @@ void OSystem::setConfigPaths() mySettings->setValue("propsfile", node.getShortPath()); } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +PropertiesSet& OSystem::propSet(const string& md5) +{ + FilesystemNode node = FilesystemNode(); + + return propSet(md5, node); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +PropertiesSet& OSystem::propSet(const string& md5, const FilesystemNode& node) +{ + if(md5 == EmptyString) + return *myPropSet; + else if(md5 == myGamePropSetMD5) + return *myGamePropSet; + else if (!node.exists()) + return *myPropSet; + + // Get a valid set of game specific properties + Properties props; + string path = myBaseDir + node.getNameWithExt(".pro"); + + // Create a properties set based on ROM name + FilesystemNode propNode = FilesystemNode(path); + myGamePropertiesFile = propNode.getPath(); + + myGamePropSet = make_unique(myGamePropertiesFile); + + // Check if game specific property file exists and has matching md5 + if(myGamePropSet->size() && myGamePropSet->getMD5(md5, props)) + { + myGamePropSetMD5 = md5; + return *myGamePropSet; + } + else + { + myGamePropSetMD5 = ""; + return *myPropSet; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void OSystem::saveGamePropSet(const string& md5) +{ + if(myGamePropSet->size() && md5 == myGamePropSetMD5) + { + myGamePropSet->save(myGamePropertiesFile); + } +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void OSystem::setBaseDir(const string& basedir) { @@ -428,8 +478,7 @@ void OSystem::logMessage(const string& message, uInt8 level) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -unique_ptr -OSystem::openConsole(const FilesystemNode& romfile, string& md5) +unique_ptr OSystem::openConsole(const FilesystemNode& romfile, string& md5) { unique_ptr console; @@ -441,8 +490,15 @@ OSystem::openConsole(const FilesystemNode& romfile, string& md5) // Get a valid set of properties, including any entered on the commandline // For initial creation of the Cart, we're only concerned with the BS type Properties props; - myPropSet->getMD5(md5, props); + // Load and use game specific props if existing + FilesystemNode node = FilesystemNode(romfile); + + string path = myBaseDir + node.getNameWithExt(".pro"); + PropertiesSet& propset = propSet(md5, romfile); + propset.getMD5(md5, props); + + // Local helper method auto CMDLINE_PROPS_UPDATE = [&](const string& name, PropertyType prop) { const string& s = mySettings->getString(name); @@ -462,12 +518,12 @@ OSystem::openConsole(const FilesystemNode& romfile, string& md5) // and that the md5 (and hence the cart) has changed if(props.get(Cartridge_MD5) != cartmd5) { - if(!myPropSet->getMD5(cartmd5, props)) + if(!propset.getMD5(cartmd5, props)) { // Cart md5 wasn't found, so we create a new props for it props.set(Cartridge_MD5, cartmd5); props.set(Cartridge_Name, props.get(Cartridge_Name)+cart->multiCartID()); - myPropSet->insert(props, false); + propset.insert(props, false); } } diff --git a/src/emucore/OSystem.hxx b/src/emucore/OSystem.hxx index 04a733481..37b275bde 100644 --- a/src/emucore/OSystem.hxx +++ b/src/emucore/OSystem.hxx @@ -107,7 +107,13 @@ class OSystem @return The properties set object */ - PropertiesSet& propSet() const { return *myPropSet; } + PropertiesSet& propSet(const string& md5 = EmptyString); + PropertiesSet& propSet(const string& md5, const FilesystemNode& node); + + /** + Save the game specific property file. + */ + void saveGamePropSet(const string& md5); /** Get the console of the system. The console won't always exist, @@ -427,6 +433,12 @@ class OSystem // Pointer to the PropertiesSet object unique_ptr myPropSet; + // Pointer to the game's PropertiesSet object + unique_ptr myGamePropSet; + + // MD5 of the currently loaded game PropertiesSet object + string myGamePropSetMD5; + // Pointer to the (currently defined) Console object unique_ptr myConsole; @@ -480,6 +492,7 @@ class OSystem string myConfigFile; string myPaletteFile; string myPropertiesFile; + string myGamePropertiesFile; FilesystemNode myRomFile; string myRomMD5; diff --git a/src/emucore/PropsSet.hxx b/src/emucore/PropsSet.hxx index 10f7e99ad..187110f63 100644 --- a/src/emucore/PropsSet.hxx +++ b/src/emucore/PropsSet.hxx @@ -112,6 +112,11 @@ class PropertiesSet */ void print() const; + /** + Return the size of the myExternalProps list + */ + uInt32 size() { return myExternalProps.size(); } + private: using PropsList = std::map; diff --git a/src/emucore/tia/Ball.cxx b/src/emucore/tia/Ball.cxx index c44301064..48f6946bf 100644 --- a/src/emucore/tia/Ball.cxx +++ b/src/emucore/tia/Ball.cxx @@ -40,6 +40,7 @@ void Ball::reset() myIsEnabledNew = false; myIsEnabled = false; myIsDelaying = false; + myIsVisible = false; myHmmClocks = 0; myCounter = 0; myIsMoving = false; @@ -49,8 +50,7 @@ void Ball::reset() myIsRendering = false; myDebugEnabled = false; myRenderCounter = 0; - - updateEnabled(); + myIsEnabled = false; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -62,7 +62,10 @@ void Ball::enabl(uInt8 value) if (myIsEnabledNew != enabledNewOldValue && !myIsDelaying) { myTIA->flushLineCache(); + updateEnabled(); + collision = (myIsVisible && myIsEnabled) ? myCollisionMaskEnabled : myCollisionMaskDisabled; + myTIA->updateCollision(); } } @@ -173,9 +176,8 @@ bool Ball::movementTick(uInt32 clock, bool apply) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Ball::tick(bool isReceivingMclock) { - collision = (myIsRendering && myRenderCounter >= 0 && myIsEnabled) ? - myCollisionMaskEnabled : - myCollisionMaskDisabled; + myIsVisible = myIsRendering && myRenderCounter >= 0; + collision = (myIsVisible && myIsEnabled) ? myCollisionMaskEnabled : myCollisionMaskDisabled; bool starfieldEffect = myIsMoving && isReceivingMclock; @@ -207,6 +209,13 @@ void Ball::tick(bool isReceivingMclock) myCounter = 0; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Ball::nextLine() +{ + myIsVisible = myIsRendering && myRenderCounter >= 0; + collision = (myIsVisible && myIsEnabled) ? myCollisionMaskEnabled : myCollisionMaskDisabled; +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Ball::setENABLOld(bool enabled) { @@ -233,6 +242,9 @@ void Ball::shuffleStatus() void Ball::updateEnabled() { myIsEnabled = !myIsSuppressed && (myIsDelaying ? myIsEnabledOld : myIsEnabledNew); + + collision = (myIsVisible && myIsEnabled) ? myCollisionMaskEnabled : myCollisionMaskDisabled; + myTIA->updateCollision(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -293,6 +305,7 @@ bool Ball::save(Serializer& out) const out.putBool(myIsEnabled); out.putBool(myIsSuppressed); out.putBool(myIsDelaying); + out.putBool(myIsVisible); out.putByte(myHmmClocks); out.putByte(myCounter); @@ -335,6 +348,7 @@ bool Ball::load(Serializer& in) myIsEnabled = in.getBool(); myIsSuppressed = in.getBool(); myIsDelaying = in.getBool(); + myIsVisible = in.getBool(); myHmmClocks = in.getByte(); myCounter = in.getByte(); diff --git a/src/emucore/tia/Ball.hxx b/src/emucore/tia/Ball.hxx index 7ee6bdb24..f6807e792 100644 --- a/src/emucore/tia/Ball.hxx +++ b/src/emucore/tia/Ball.hxx @@ -62,6 +62,8 @@ class Ball : public Serializable void tick(bool isReceivingMclock = true); + void nextLine(); + bool isOn() const { return (collision & 0x8000); } uInt8 getColor() const { return myColor; } @@ -105,6 +107,7 @@ class Ball : public Serializable bool myIsEnabled; bool myIsSuppressed; bool myIsDelaying; + bool myIsVisible; uInt8 myHmmClocks; uInt8 myCounter; diff --git a/src/emucore/tia/Missile.cxx b/src/emucore/tia/Missile.cxx index 9ad2a49d2..93f824f1b 100644 --- a/src/emucore/tia/Missile.cxx +++ b/src/emucore/tia/Missile.cxx @@ -47,12 +47,12 @@ void Missile::reset() myWidth = 1; myEffectiveWidth = 1; myIsRendering = false; + myIsVisible = false; myRenderCounter = 0; myColor = myObjectColor = myDebugColor = 0; myDebugEnabled = false; collision = myCollisionMaskDisabled; - - updateEnabled(); + myIsEnabled = false; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -62,9 +62,11 @@ void Missile::enam(uInt8 value) myEnam = (value & 0x02) > 0; - if (oldEnam != myEnam) myTIA->flushLineCache(); + if (oldEnam != myEnam) { + myTIA->flushLineCache(); - updateEnabled(); + updateEnabled(); + } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -166,41 +168,43 @@ bool Missile::movementTick(uInt8 clock, uInt8 hclock, bool apply) if (clock == myHmmClocks) myIsMoving = false; - if (myIsMoving && apply) tick(hclock); + if (myIsMoving && apply) tick(hclock, false); return myIsMoving; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Missile::tick(uInt8 hclock) +void Missile::tick(uInt8 hclock, bool isReceivingMclock) { - const bool render = + myIsVisible = myIsRendering && - (myRenderCounter >= 0 || (myIsMoving && myRenderCounter == -1 && myWidth < 4 && ((hclock + 1) % 4 == 3))) && - myIsEnabled; + (myRenderCounter >= 0 || (myIsMoving && isReceivingMclock && myRenderCounter == -1 && myWidth < 4 && ((hclock + 1) % 4 == 3))); - collision = render ? myCollisionMaskEnabled : myCollisionMaskDisabled; + collision = (myIsVisible && myIsEnabled) ? myCollisionMaskEnabled : myCollisionMaskDisabled; if (myDecodes[myCounter] && !myResmp) { myIsRendering = true; myRenderCounter = Count::renderCounterOffset; } else if (myIsRendering) { - if (myIsMoving && myRenderCounter == -1) { + if (myRenderCounter == -1) { + if (myIsMoving && isReceivingMclock) { + switch ((hclock + 1) % 4) { + case 3: + myEffectiveWidth = myWidth == 1 ? 2 : myWidth; + if (myWidth < 4) myRenderCounter++; + break; - switch ((hclock + 1) % 4) { - case 3: - myEffectiveWidth = myWidth == 1 ? 2 : myWidth; - if (myWidth < 4) myRenderCounter++; - break; + case 2: + myEffectiveWidth = 0; + break; - case 2: - myEffectiveWidth = 0; - break; - - default: - myEffectiveWidth = myWidth; - break; + default: + myEffectiveWidth = myWidth; + break; + } + } else { + myEffectiveWidth = myWidth; } } @@ -210,6 +214,13 @@ void Missile::tick(uInt8 hclock) if (++myCounter >= 160) myCounter = 0; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Missile::nextLine() +{ + myIsVisible = myIsRendering && (myRenderCounter >= 0); + collision = (myIsVisible && myIsEnabled) ? myCollisionMaskEnabled : myCollisionMaskDisabled; +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Missile::setColor(uInt8 color) { @@ -246,6 +257,9 @@ void Missile::applyColorLoss() void Missile::updateEnabled() { myIsEnabled = !myIsSuppressed && myEnam && !myResmp; + + collision = (myIsVisible && myIsEnabled) ? myCollisionMaskEnabled : myCollisionMaskDisabled; + myTIA->updateCollision(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -308,6 +322,7 @@ bool Missile::save(Serializer& out) const out.putByte(myEffectiveWidth); out.putByte(myLastMovementTick); + out.putBool(myIsVisible); out.putBool(myIsRendering); out.putByte(myRenderCounter); @@ -350,6 +365,7 @@ bool Missile::load(Serializer& in) myEffectiveWidth = in.getByte(); myLastMovementTick = in.getByte(); + myIsVisible = in.getBool(); myIsRendering = in.getBool(); myRenderCounter = in.getByte(); diff --git a/src/emucore/tia/Missile.hxx b/src/emucore/tia/Missile.hxx index c89b9f40a..57f0a3eb2 100644 --- a/src/emucore/tia/Missile.hxx +++ b/src/emucore/tia/Missile.hxx @@ -50,7 +50,9 @@ class Missile : public Serializable bool movementTick(uInt8 clock, uInt8 hclock, bool apply); - void tick(uInt8 hclock); + void tick(uInt8 hclock, bool isReceivingMclock = true); + + void nextLine(); void setColor(uInt8 color); @@ -103,6 +105,7 @@ class Missile : public Serializable uInt8 myLastMovementTick; bool myIsRendering; + bool myIsVisible; Int8 myRenderCounter; const uInt8* myDecodes; diff --git a/src/emucore/tia/Player.cxx b/src/emucore/tia/Player.cxx index 480707f1f..70c6fbcaf 100644 --- a/src/emucore/tia/Player.cxx +++ b/src/emucore/tia/Player.cxx @@ -52,9 +52,9 @@ void Player::reset() mySampleCounter = 0; myDividerPending = 0; myDividerChangeCounter = -1; + myPattern = 0; setDivider(1); - updatePattern(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -309,6 +309,15 @@ void Player::tick() if (++myCounter >= 160) myCounter = 0; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Player::nextLine() +{ + if (!myIsRendering || myRenderCounter < myRenderCounterTripPoint) + collision = myCollisionMaskDisabled; + else + collision = (myPattern & (1 << mySampleCounter)) ? myCollisionMaskEnabled : myCollisionMaskDisabled; +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Player::shufflePatterns() { @@ -372,6 +381,11 @@ void Player::updatePattern() ((myPattern & 0x80) >> 7) ); } + + if (myIsRendering && myRenderCounter >= myRenderCounterTripPoint) { + collision = (myPattern & (1 << mySampleCounter)) ? myCollisionMaskEnabled : myCollisionMaskDisabled; + myTIA->updateCollision(); + } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/emucore/tia/Player.hxx b/src/emucore/tia/Player.hxx index 60b025e31..78a59e37a 100644 --- a/src/emucore/tia/Player.hxx +++ b/src/emucore/tia/Player.hxx @@ -62,6 +62,9 @@ class Player : public Serializable bool movementTick(uInt32 clock, bool apply); void tick(); + + void nextLine(); + uInt8 getClock() const { return myCounter; } bool isOn() const { return (collision & 0x8000); } diff --git a/src/emucore/tia/Playfield.cxx b/src/emucore/tia/Playfield.cxx index 3ec2d730f..9d3adba74 100644 --- a/src/emucore/tia/Playfield.cxx +++ b/src/emucore/tia/Playfield.cxx @@ -204,6 +204,12 @@ void Playfield::tick(uInt32 x) collision = currentPixel ? myCollisionMaskEnabled : myCollisionMaskDisabled; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Playfield::nextLine() +{ + collision = myCollisionMaskDisabled; +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Playfield::applyColors() { diff --git a/src/emucore/tia/Playfield.hxx b/src/emucore/tia/Playfield.hxx index 7dde0ad0c..d851f8d28 100644 --- a/src/emucore/tia/Playfield.hxx +++ b/src/emucore/tia/Playfield.hxx @@ -59,6 +59,8 @@ class Playfield : public Serializable void tick(uInt32 x); + void nextLine(); + bool isOn() const { return (collision & 0x8000); } uInt8 getColor() const; diff --git a/src/emucore/tia/TIA.cxx b/src/emucore/tia/TIA.cxx index df6c7c4de..50eacf054 100644 --- a/src/emucore/tia/TIA.cxx +++ b/src/emucore/tia/TIA.cxx @@ -1273,7 +1273,6 @@ void TIA::nextLine() cloneLastLine(); } - myPlayfield.tick(0); myHctr = 0; if (!myMovementInProgress && myLinesSinceChange < 2) myLinesSinceChange++; @@ -1282,6 +1281,12 @@ void TIA::nextLine() myHctrDelta = 0; myFrameManager->nextLine(); + myMissile0.nextLine(); + myMissile1.nextLine(); + myPlayer0.nextLine(); + myPlayer1.nextLine(); + myBall.nextLine(); + myPlayfield.nextLine(); if (myFrameManager->isRendering() && myFrameManager->getY() == 0) flushLineCache(); diff --git a/src/emucore/tia/TIA.hxx b/src/emucore/tia/TIA.hxx index 9ab8350c6..9dd86ff56 100644 --- a/src/emucore/tia/TIA.hxx +++ b/src/emucore/tia/TIA.hxx @@ -415,6 +415,11 @@ class TIA : public Device */ void flushLineCache(); + /** + * Update the collision bitfield. + */ + void updateCollision(); + /** Create a new delayQueueIterator for the debugger. */ @@ -511,11 +516,6 @@ class TIA : public Device */ void applyRsync(); - /** - * Update the collision bitfield. - */ - void updateCollision(); - /** * Render the current pixel into the framebuffer. */ diff --git a/src/gui/AudioDialog.cxx b/src/gui/AudioDialog.cxx index 54fc3f241..74c15668f 100644 --- a/src/gui/AudioDialog.cxx +++ b/src/gui/AudioDialog.cxx @@ -42,9 +42,7 @@ AudioDialog::AudioDialog(OSystem& osystem, DialogContainer& parent, const int INDENT = 20; const int lineHeight = font.getLineHeight(), fontWidth = font.getMaxCharWidth(), - fontHeight = font.getFontHeight(), - buttonWidth = font.getStringWidth("Defaults") + 20, - buttonHeight = font.getLineHeight() + 4; + fontHeight = font.getFontHeight(); int xpos, ypos; int lwidth = font.getStringWidth("Sample Size (*) "), pwidth = font.getStringWidth("512 bytes"); diff --git a/src/gui/ComboDialog.cxx b/src/gui/ComboDialog.cxx index 3ac85213c..35b76ddb6 100644 --- a/src/gui/ComboDialog.cxx +++ b/src/gui/ComboDialog.cxx @@ -33,10 +33,7 @@ ComboDialog::ComboDialog(GuiObject* boss, const GUI::Font& font, myComboEvent(Event::NoType) { const int lineHeight = font.getLineHeight(), - fontWidth = font.getMaxCharWidth(), - fontHeight = font.getFontHeight(), - buttonWidth = font.getStringWidth("Defaults") + 20, - buttonHeight = font.getLineHeight() + 4; + fontWidth = font.getMaxCharWidth(); int xpos, ypos; WidgetArray wid; diff --git a/src/gui/CommandDialog.cxx b/src/gui/CommandDialog.cxx index ad6735cc1..6269cecb3 100644 --- a/src/gui/CommandDialog.cxx +++ b/src/gui/CommandDialog.cxx @@ -27,13 +27,10 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CommandDialog::CommandDialog(OSystem& osystem, DialogContainer& parent) - : Dialog(osystem, parent) + : Dialog(osystem, parent, osystem.frameBuffer().font(), "Commands") { - const GUI::Font& font = instance().frameBuffer().font(); - initTitle(font, "Commands"); - - const int buttonWidth = font.getStringWidth("Right Diff B") + 20, - buttonHeight = font.getLineHeight() + 6, + const int buttonWidth = _font.getStringWidth("Right Diff B") + 20, + buttonHeight = _font.getLineHeight() + 6, rowHeight = buttonHeight + 8; // Set real dimensions @@ -46,7 +43,7 @@ CommandDialog::CommandDialog(OSystem& osystem, DialogContainer& parent) auto ADD_CD_BUTTON = [&](const string& label, int cmd) { - ButtonWidget* bw = new ButtonWidget(this, font, xoffset, yoffset, + ButtonWidget* bw = new ButtonWidget(this, _font, xoffset, yoffset, buttonWidth, buttonHeight, label, cmd); xoffset += buttonWidth + 8; return bw; diff --git a/src/gui/ContextMenu.cxx b/src/gui/ContextMenu.cxx index ba1ff7761..1085d0c32 100644 --- a/src/gui/ContextMenu.cxx +++ b/src/gui/ContextMenu.cxx @@ -28,7 +28,7 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ContextMenu::ContextMenu(GuiObject* boss, const GUI::Font& font, const VariantList& items, int cmd, int width) - : Dialog(boss->instance(), boss->parent()), + : Dialog(boss->instance(), boss->parent(), font), CommandSender(boss), _rowHeight(font.getLineHeight()), _firstEntry(0), @@ -39,7 +39,6 @@ ContextMenu::ContextMenu(GuiObject* boss, const GUI::Font& font, _isScrolling(false), _scrollUpColor(kColor), _scrollDnColor(kColor), - _font(font), _cmd(cmd), _xorig(0), _yorig(0), diff --git a/src/gui/ContextMenu.hxx b/src/gui/ContextMenu.hxx index 4ce617a34..9118f56e0 100644 --- a/src/gui/ContextMenu.hxx +++ b/src/gui/ContextMenu.hxx @@ -120,7 +120,6 @@ class ContextMenu : public Dialog, public CommandSender bool _isScrolling; uInt32 _scrollUpColor, _scrollDnColor; - const GUI::Font& _font; int _cmd; uInt32 _xorig, _yorig; diff --git a/src/gui/Dialog.cxx b/src/gui/Dialog.cxx index 02eb8703d..8bba62a5f 100644 --- a/src/gui/Dialog.cxx +++ b/src/gui/Dialog.cxx @@ -44,13 +44,10 @@ * ... */ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Dialog::Dialog(OSystem& instance, DialogContainer& parent, const GUI::Font& font, const string& title, - int x, int y, int w, int h) +Dialog::Dialog(OSystem& instance, DialogContainer& parent, const GUI::Font& font, + const string& title, int x, int y, int w, int h) : GuiObject(instance, parent, *this, x, y, w, h), - _font(&font), - _title(title), - _th(0), + _font(font), _mouseWidget(nullptr), _focusedWidget(nullptr), _dragWidget(nullptr), @@ -58,30 +55,19 @@ Dialog::Dialog(OSystem& instance, DialogContainer& parent, const GUI::Font& font _cancelWidget(nullptr), _visible(false), _processCancel(false), + _title(title), + _th(0), _surface(nullptr), _tabID(0), _flags(WIDGET_ENABLED | WIDGET_BORDER | WIDGET_CLEARBG) { - initTitle(font, title); + setTitle(title); } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Dialog::Dialog(OSystem& instance, DialogContainer& parent, int x, int y, int w, int h) - : GuiObject(instance, parent, *this, x, y, w, h), - _font(nullptr), - _title(""), - _th(0), - _fh(0), - _mouseWidget(nullptr), - _focusedWidget(nullptr), - _dragWidget(nullptr), - _okWidget(nullptr), - _cancelWidget(nullptr), - _visible(false), - _processCancel(false), - _surface(nullptr), - _tabID(0), - _flags(WIDGET_ENABLED | WIDGET_BORDER | WIDGET_CLEARBG) + : Dialog(instance, parent, instance.frameBuffer().font(), "", x, y, w, h) { } @@ -133,27 +119,16 @@ void Dialog::close(bool refresh) parent().removeDialog(); } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Dialog::initTitle(const GUI::Font& font, const string& title) -{ - _font = &font; - _fh = font.getLineHeight(); - setTitle(title); -} - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Dialog::setTitle(const string& title) { - if(_font != nullptr) - { - _title = title; - _h -= _th; - if(title.empty()) - _th = 0; - else - _th = _fh + 4; - _h += _th; - } + _title = title; + _h -= _th; + if(title.empty()) + _th = 0; + else + _th = _font.getLineHeight() + 4; + _h += _th; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -326,8 +301,8 @@ void Dialog::drawDialog() { // dialog is still on top if e.g a ContextMenu is opened bool onTop = parent().myDialogStack.top() == this - || parent().myDialogStack.get(parent().myDialogStack.size() - 2) == this - && !parent().myDialogStack.top()->hasTitle(); + || (parent().myDialogStack.get(parent().myDialogStack.size() - 2) == this + && !parent().myDialogStack.top()->hasTitle()); if(_flags & WIDGET_CLEARBG) { @@ -336,7 +311,7 @@ void Dialog::drawDialog() if(_th) { s.fillRect(_x, _y, _w, _th, onTop ? kColorTitleBar : kColorTitleBarLo); - s.drawString(*_font, _title, _x + 10, _y + 2 + 1, _font->getStringWidth(_title), + s.drawString(_font, _title, _x + 10, _y + 2 + 1, _font.getStringWidth(_title), onTop ? kColorTitleText : kColorTitleTextLo); } } diff --git a/src/gui/Dialog.hxx b/src/gui/Dialog.hxx index bfa275caf..1701bc66e 100644 --- a/src/gui/Dialog.hxx +++ b/src/gui/Dialog.hxx @@ -46,8 +46,8 @@ class Dialog : public GuiObject public: Dialog(OSystem& instance, DialogContainer& parent, int x = 0, int y = 0, int w = 0, int h = 0); - Dialog(OSystem& instance, DialogContainer& parent, const GUI::Font& font, const string& title, - int x = 0, int y = 0, int w = 0, int h = 0); + Dialog(OSystem& instance, DialogContainer& parent, const GUI::Font& font, + const string& title = "", int x = 0, int y = 0, int w = 0, int h = 0); virtual ~Dialog(); @@ -129,8 +129,6 @@ class Dialog : public GuiObject void processCancelWithoutWidget(bool state) { _processCancel = state; } - void initTitle(const GUI::Font& font, const string& title); - private: void buildCurrentFocusList(int tabID = -1); bool handleNavEvent(Event::Type e); @@ -138,18 +136,19 @@ class Dialog : public GuiObject bool cycleTab(int direction); protected: + const GUI::Font& _font; + Widget* _mouseWidget; Widget* _focusedWidget; Widget* _dragWidget; Widget* _defaultWidget; Widget* _okWidget; Widget* _cancelWidget; + bool _visible; bool _processCancel; string _title; int _th; - const GUI::Font* _font; - int _fh; Common::FixedStack> mySurfaceStack; diff --git a/src/gui/GameInfoDialog.cxx b/src/gui/GameInfoDialog.cxx index 519a3acfb..3c736f571 100644 --- a/src/gui/GameInfoDialog.cxx +++ b/src/gui/GameInfoDialog.cxx @@ -47,7 +47,6 @@ GameInfoDialog::GameInfoDialog( const int lineHeight = font.getLineHeight(), fontWidth = font.getMaxCharWidth(), fontHeight = font.getFontHeight(), - buttonWidth = font.getStringWidth("Defaults") + 20, buttonHeight = font.getLineHeight() + 4; const int vBorder = 8; const int hBorder = 8; @@ -349,7 +348,7 @@ void GameInfoDialog::loadConfig() const string& md5 = instance().launcher().selectedRomMD5(); if(md5 != "") { - instance().propSet().getMD5(md5, myGameProperties); + instance().propSet(md5).getMD5(md5, myGameProperties); myPropertiesLoaded = true; loadView(); } @@ -487,10 +486,13 @@ void GameInfoDialog::saveConfig() myPPBlend->getValueLabel()); // Determine whether to add or remove an entry from the properties set + const string& md5 = myGameProperties.get(Cartridge_MD5); if(myDefaultsSelected) - instance().propSet().removeMD5(myGameProperties.get(Cartridge_MD5)); + instance().propSet(md5).removeMD5(myGameProperties.get(Cartridge_MD5)); else - instance().propSet().insert(myGameProperties); + instance().propSet(md5).insert(myGameProperties); + + instance().saveGamePropSet(myGameProperties.get(Cartridge_MD5)); // In any event, inform the Console if(instance().hasConsole()) @@ -502,7 +504,7 @@ void GameInfoDialog::setDefaults() { // Load the default properties string md5 = myGameProperties.get(Cartridge_MD5); - instance().propSet().getMD5(md5, myGameProperties, true); + instance().propSet(md5).getMD5(md5, myGameProperties, true); // Reload the current dialog loadView(); diff --git a/src/gui/GlobalPropsDialog.cxx b/src/gui/GlobalPropsDialog.cxx index 23de11795..b6ba86764 100644 --- a/src/gui/GlobalPropsDialog.cxx +++ b/src/gui/GlobalPropsDialog.cxx @@ -36,7 +36,6 @@ GlobalPropsDialog::GlobalPropsDialog(GuiObject* boss, const GUI::Font& font) const int lineHeight = font.getLineHeight(), fontWidth = font.getMaxCharWidth(), fontHeight = font.getFontHeight(), - buttonWidth = font.getStringWidth("Defaults") + 20, buttonHeight = font.getLineHeight() + 4; int xpos, ypos; int lwidth = font.getStringWidth("Right Difficulty "), diff --git a/src/gui/InputDialog.cxx b/src/gui/InputDialog.cxx index d3a6549fb..f6cd9599b 100644 --- a/src/gui/InputDialog.cxx +++ b/src/gui/InputDialog.cxx @@ -45,7 +45,6 @@ InputDialog::InputDialog(OSystem& osystem, DialogContainer& parent, { const int lineHeight = font.getLineHeight(), fontWidth = font.getMaxCharWidth(), - buttonWidth = font.getStringWidth("Defaults") + 20, buttonHeight = font.getLineHeight() + 4; const int vBorder = 4; int xpos, ypos, tabID; diff --git a/src/gui/LauncherDialog.cxx b/src/gui/LauncherDialog.cxx index 69d20b00c..c163e2a6f 100644 --- a/src/gui/LauncherDialog.cxx +++ b/src/gui/LauncherDialog.cxx @@ -349,7 +349,7 @@ void LauncherDialog::loadRomInfo() // Get the properties for this entry Properties props; - instance().propSet().getMD5WithInsert(node, myGameList->md5(item), props); + instance().propSet(myGameList->md5(item), node).getMD5WithInsert(node, myGameList->md5(item), props); myRomInfoWidget->setProperties(props); } diff --git a/src/gui/OptionsDialog.cxx b/src/gui/OptionsDialog.cxx index 77d9bba6d..584e414b0 100644 --- a/src/gui/OptionsDialog.cxx +++ b/src/gui/OptionsDialog.cxx @@ -48,16 +48,13 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OptionsDialog::OptionsDialog(OSystem& osystem, DialogContainer& parent, GuiObject* boss, int max_w, int max_h, stellaMode mode) - : Dialog(osystem, parent), + : Dialog(osystem, parent, osystem.frameBuffer().font(), "Options"), myMode(mode), _boss(boss) { - const GUI::Font& font = instance().frameBuffer().font(); - initTitle(font, "Options"); - - const int buttonWidth = font.getStringWidth("Game Properties" + ELLIPSIS) + 20, - buttonHeight = font.getLineHeight() + 6, - rowHeight = font.getLineHeight() + 10; + const int buttonWidth = _font.getStringWidth("Game Properties" + ELLIPSIS) + 20, + buttonHeight = _font.getLineHeight() + 6, + rowHeight = _font.getLineHeight() + 10; const int VBORDER = 10 + _th; _w = 2 * buttonWidth + 30; @@ -69,7 +66,7 @@ OptionsDialog::OptionsDialog(OSystem& osystem, DialogContainer& parent, auto ADD_OD_BUTTON = [&](const string& label, int cmd) { - ButtonWidget* bw = new ButtonWidget(this, font, xoffset, yoffset, + ButtonWidget* bw = new ButtonWidget(this, _font, xoffset, yoffset, buttonWidth, buttonHeight, label, cmd); yoffset += rowHeight; return bw; @@ -128,21 +125,21 @@ OptionsDialog::OptionsDialog(OSystem& osystem, DialogContainer& parent, addCancelWidget(b); // Now create all the dialogs attached to each menu button - myVideoDialog = make_unique(osystem, parent, font, max_w, max_h); - myAudioDialog = make_unique(osystem, parent, font); - myInputDialog = make_unique(osystem, parent, font, max_w, max_h); - myUIDialog = make_unique(osystem, parent, font); - mySnapshotDialog = make_unique(osystem, parent, font, max_w, max_h); - myConfigPathDialog = make_unique(osystem, parent, font, boss, max_w, max_h); - myRomAuditDialog = make_unique(osystem, parent, font, max_w, max_h); - myGameInfoDialog = make_unique(osystem, parent, font, this); + myVideoDialog = make_unique(osystem, parent, _font, max_w, max_h); + myAudioDialog = make_unique(osystem, parent, _font); + myInputDialog = make_unique(osystem, parent, _font, max_w, max_h); + myUIDialog = make_unique(osystem, parent, _font); + mySnapshotDialog = make_unique(osystem, parent, _font, max_w, max_h); + myConfigPathDialog = make_unique(osystem, parent, _font, boss, max_w, max_h); + myRomAuditDialog = make_unique(osystem, parent, _font, max_w, max_h); + myGameInfoDialog = make_unique(osystem, parent, _font, this); #ifdef CHEATCODE_SUPPORT - myCheatCodeDialog = make_unique(osystem, parent, font); + myCheatCodeDialog = make_unique(osystem, parent, _font); #endif - myLoggerDialog = make_unique(osystem, parent, font, max_w, max_h); - myDeveloperDialog = make_unique(osystem, parent, font, max_w, max_h); - myHelpDialog = make_unique(osystem, parent, font); - myAboutDialog = make_unique(osystem, parent, font); + myLoggerDialog = make_unique(osystem, parent, _font, max_w, max_h); + myDeveloperDialog = make_unique(osystem, parent, _font, max_w, max_h); + myHelpDialog = make_unique(osystem, parent, _font); + myAboutDialog = make_unique(osystem, parent, _font); addToFocusList(wid); diff --git a/src/gui/TabWidget.cxx b/src/gui/TabWidget.cxx index d456c35d4..55dd450de 100644 --- a/src/gui/TabWidget.cxx +++ b/src/gui/TabWidget.cxx @@ -263,11 +263,6 @@ void TabWidget::drawWidget(bool hilite) FBSurface& s = dialog().surface(); - const int left1 = _x + 1; - const int right1 = _x + kTabLeftOffset + _activeTab * (_tabWidth + kTabSpacing); - const int left2 = right1 + _tabWidth; - const int right2 = _x + _w - 2; - // Iterate over all tabs and draw them int i, x = _x + kTabLeftOffset; for (i = 0; i < int(_tabs.size()); ++i) diff --git a/src/gui/TimeLineWidget.cxx b/src/gui/TimeLineWidget.cxx index 657d24053..a3074ab72 100644 --- a/src/gui/TimeLineWidget.cxx +++ b/src/gui/TimeLineWidget.cxx @@ -25,6 +25,9 @@ #include "TimeLineWidget.hxx" +const int HANDLE_W = 3; +const int HANDLE_H = 3; // size above/below the slider + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - TimeLineWidget::TimeLineWidget(GuiObject* boss, const GUI::Font& font, int x, int y, int w, int h, @@ -85,7 +88,7 @@ void TimeLineWidget::setStepValues(const IntArray& steps) if(steps.size() > _stepValue.capacity()) _stepValue.reserve(2 * steps.size()); - double scale = (_w - _labelWidth - 2) / double(steps.back()); + double scale = (_w - _labelWidth - 2 - HANDLE_W*0) / double(steps.back()); // Skip the very last value; we take care of it outside the end of the loop for(uInt32 i = 0; i < steps.size() - 1; ++i) @@ -93,7 +96,7 @@ void TimeLineWidget::setStepValues(const IntArray& steps) // Due to integer <-> double conversion, the last value is sometimes // slightly less than the maximum value; we assign it manually to fix this - _stepValue.push_back(_w - _labelWidth - 2); + _stepValue.push_back(_w - _labelWidth - 2 - HANDLE_W*0); } else _stepValue.push_back(0); @@ -142,48 +145,64 @@ void TimeLineWidget::drawWidget(bool hilite) { FBSurface& s = _boss->dialog().surface(); -#ifndef FLAT_UI // Draw the label, if any if(_labelWidth > 0) s.drawString(_font, _label, _x, _y + 2, _labelWidth, - isEnabled() ? kTextColor : kColor, TextAlign::Right); + isEnabled() ? kTextColor : kColor, TextAlign::Left); + + int p = valueToPos(_value), + x = _x + _labelWidth, + w = _w - _labelWidth; + + // Frame the handle + const int HANDLE_W2 = (HANDLE_W + 1) / 2; + s.hLine(x + p - HANDLE_W2, _y + 0, x + p - HANDLE_W2 + HANDLE_W, kColorInfo); + s.vLine(x + p - HANDLE_W2, _y + 1, _y + _h - 2, kColorInfo); + s.hLine(x + p - HANDLE_W2 + 1, _y + _h - 1, x + p - HANDLE_W2 + 1 + HANDLE_W, kBGColor); + s.vLine(x + p - HANDLE_W2 + 1 + HANDLE_W, _y + 1, _y + _h - 2, kBGColor); + // Frame the box + s.hLine(x, _y + HANDLE_H, x + w - 2, kColorInfo); + s.vLine(x, _y + HANDLE_H, _y + _h - 2 - HANDLE_H, kColorInfo); + s.hLine(x + 1, _y + _h - 1 - HANDLE_H, x + w - 1, kBGColor); + s.vLine(x + w - 1, _y + 1 + HANDLE_H, _y + _h - 2 - HANDLE_H, kBGColor); - // Draw the box - s.frameRect(_x + _labelWidth, _y, _w - _labelWidth, _h, kColor); // Fill the box - s.fillRect(_x + _labelWidth + 1, _y + 1, _w - _labelWidth - 2, _h - 2, - !isEnabled() ? kBGColorHi : kWidColor); + s.fillRect(x + 1, _y + 1 + HANDLE_H, w - 2, _h - 2 - HANDLE_H * 2, + !isEnabled() ? kSliderBGColorLo : hilite ? kSliderBGColorHi : kSliderBGColor); // Draw the 'bar' - int vp = valueToPos(_value); - s.fillRect(_x + _labelWidth + 1, _y + 1, vp, _h - 2, + s.fillRect(x + 1, _y + 1 + HANDLE_H, p, _h - 2 - HANDLE_H * 2, !isEnabled() ? kColor : hilite ? kSliderColorHi : kSliderColor); - // add 4 tickmarks for 5 intervals + // Add 4 tickmarks for 5 intervals int numTicks = std::min(5, int(_stepValue.size())); for(int i = 1; i < numTicks; ++i) { int idx = int((_stepValue.size() * i + numTicks / 2) / numTicks); if(idx > 1) { - int tp = valueToPos(idx - 1); - s.vLine(_x + _labelWidth + tp, _y + _h / 2, _y + _h - 2, tp > vp ? kSliderColor : kWidColor); + int xt = x + valueToPos(idx - 1); + uInt32 color; + + if(isEnabled()) + { + if(xt > x + p) + color = hilite ? kSliderColorHi : kSliderColor; + else + color = hilite ? kSliderBGColorHi : kSliderBGColor; + } + else + { + if(xt > x + p) + color = kColor; + else + color = kSliderBGColorLo; + } + s.vLine(xt, _y + _h / 2, _y + _h - 2 - HANDLE_H, color); } } -#else - // Draw the label, if any - if(_labelWidth > 0) - s.drawString(_font, _label, _x, _y + 2, _labelWidth, - isEnabled() ? kTextColor : kColor, TextAlign::Left); - - // Draw the box - s.frameRect(_x + _labelWidth, _y, _w - _labelWidth, _h, isEnabled() && hilite ? kSliderColorHi : kShadowColor); - // Fill the box - s.fillRect(_x + _labelWidth + 1, _y + 1, _w - _labelWidth - 2, _h - 2, - !isEnabled() ? kBGColorHi : kWidColor); - // Draw the 'bar' - s.fillRect(_x + _labelWidth + 2, _y + 2, valueToPos(_value), _h - 4, + // Draw the handle + s.fillRect(x + p + 1 - HANDLE_W2, _y + 1, HANDLE_W, _h - 2, !isEnabled() ? kColor : hilite ? kSliderColorHi : kSliderColor); -#endif } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/gui/TimeMachine.cxx b/src/gui/TimeMachine.cxx index 42ea08d49..6df6cd5cf 100644 --- a/src/gui/TimeMachine.cxx +++ b/src/gui/TimeMachine.cxx @@ -47,12 +47,25 @@ void TimeMachine::requestResize() { myWidth = newWidth; Dialog* oldPtr = myBaseDialog; + Int32 enterWinds = static_cast(myBaseDialog)->getEnterWinds(); delete myBaseDialog; myBaseDialog = new TimeMachineDialog(myOSystem, *this, myWidth); + setEnterWinds(enterWinds); Dialog* newPtr = myBaseDialog; // Update the container stack; it may contain a reference to the old pointer if(oldPtr != newPtr) - myDialogStack.replace(oldPtr, newPtr); + { + myDialogStack.applyAll([&oldPtr,&newPtr](Dialog*& d){ + if(d == oldPtr) + d = newPtr; + }); + } } } + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void TimeMachine::setEnterWinds(Int32 numWinds) +{ + static_cast(myBaseDialog)->setEnterWinds(numWinds); +} diff --git a/src/gui/TimeMachine.hxx b/src/gui/TimeMachine.hxx index c8f381bd9..c39568d41 100644 --- a/src/gui/TimeMachine.hxx +++ b/src/gui/TimeMachine.hxx @@ -39,6 +39,11 @@ class TimeMachine : public DialogContainer */ void requestResize() override; + /** + Set number of winds when entering the dialog. + */ + void setEnterWinds(Int32 numWinds); + private: int myWidth; diff --git a/src/gui/TimeMachineDialog.cxx b/src/gui/TimeMachineDialog.cxx index b0bb761cc..658e61f98 100644 --- a/src/gui/TimeMachineDialog.cxx +++ b/src/gui/TimeMachineDialog.cxx @@ -34,102 +34,140 @@ #include "Base.hxx" using Common::Base; + +const int BUTTON_W = 14, BUTTON_H = 14; + +static uInt32 RECORD[BUTTON_H] = +{ + 0b00000111100000, + 0b00011111111000, + 0b00111111111100, + 0b01111111111110, + 0b01111111111110, + 0b11111111111111, + 0b11111111111111, + 0b11111111111111, + 0b11111111111111, + 0b01111111111110, + 0b01111111111110, + 0b00111111111100, + 0b00011111111000, + 0b00000111100000 +}; + +static uInt32 STOP[BUTTON_H] = +{ + 0b11111111111111, + 0b11111111111111, + 0b11111111111111, + 0b11111111111111, + 0b11111111111111, + 0b11111111111111, + 0b11111111111111, + 0b11111111111111, + 0b11111111111111, + 0b11111111111111, + 0b11111111111111, + 0b11111111111111, + 0b11111111111111, + 0b11111111111111 +}; + +static uInt32 PLAY[BUTTON_H] = +{ + 0b11000000000000, + 0b11110000000000, + 0b11111100000000, + 0b11111111000000, + 0b11111111110000, + 0b11111111111100, + 0b11111111111111, + 0b11111111111111, + 0b11111111111100, + 0b11111111110000, + 0b11111111000000, + 0b11111100000000, + 0b11110000000000, + 0b11000000000000 +}; +static uInt32 REWIND_ALL[BUTTON_H] = +{ + 0, + 0b11000011000011, + 0b11000111000111, + 0b11001111001111, + 0b11011111011111, + 0b11111111111111, + 0b11111111111111, + 0b11111111111111, + 0b11111111111111, + 0b11011111011111, + 0b11001111001111, + 0b11000111000111, + 0b11000011000011, + 0 +}; +static uInt32 REWIND_1[BUTTON_H] = +{ + 0, + 0b00000110001110, + 0b00001110001110, + 0b00011110001110, + 0b00111110001110, + 0b01111110001110, + 0b11111110001110, + 0b11111110001110, + 0b01111110001110, + 0b00111110001110, + 0b00011110001110, + 0b00001110001110, + 0b00000110001110, + 0 +}; +static uInt32 UNWIND_1[BUTTON_H] = +{ + 0, + 0b01110001100000, + 0b01110001110000, + 0b01110001111000, + 0b01110001111100, + 0b01110001111110, + 0b01110001111111, + 0b01110001111111, + 0b01110001111110, + 0b01110001111100, + 0b01110001111000, + 0b01110001110000, + 0b01110001100000, + 0 +}; +static uInt32 UNWIND_ALL[BUTTON_H] = +{ + 0, + 0b11000011000011, + 0b11100011100011, + 0b11110011110011, + 0b11111011111011, + 0b11111111111111, + 0b11111111111111, + 0b11111111111111, + 0b11111111111111, + 0b11111011111011, + 0b11110011110011, + 0b11100011100011, + 0b11000011000011, + 0 +}; + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - TimeMachineDialog::TimeMachineDialog(OSystem& osystem, DialogContainer& parent, int width) - : Dialog(osystem, parent) + : Dialog(osystem, parent), + _enterWinds(0) { - const int BUTTON_W = 16, BUTTON_H = 14; - - static uInt32 PLAY[BUTTON_H] = - { - 0b0110000000000000, - 0b0111100000000000, - 0b0111111000000000, - 0b0111111110000000, - 0b0111111111100000, - 0b0111111111111000, - 0b0111111111111110, - 0b0111111111111110, - 0b0111111111111000, - 0b0111111111100000, - 0b0111111110000000, - 0b0111111000000000, - 0b0111100000000000, - 0b0110000000000000 - }; - static uInt32 REWIND_ALL[BUTTON_H] = - { - 0, - 0b0110000110000110, - 0b0110001110001110, - 0b0110011110011110, - 0b0110111110111110, - 0b0111111111111110, - 0b0111111111111110, - 0b0111111111111110, - 0b0111111111111110, - 0b0110111110111110, - 0b0110011110011110, - 0b0110001110001110, - 0b0110000110000110, - 0 - }; - static uInt32 REWIND_1[BUTTON_H] = - { - 0, - 0b0000001100011100, - 0b0000011100011100, - 0b0000111100011100, - 0b0001111100011100, - 0b0011111100011100, - 0b0111111100011100, - 0b0111111100011100, - 0b0011111100011100, - 0b0001111100011100, - 0b0000111100011100, - 0b0000011100011100, - 0b0000001100011100, - 0 - }; - static uInt32 UNWIND_1[BUTTON_H] = - { - 0, - 0b0011100011000000, - 0b0011100011100000, - 0b0011100011110000, - 0b0011100011111000, - 0b0011100011111100, - 0b0011100011111110, - 0b0011100011111110, - 0b0011100011111100, - 0b0011100011111000, - 0b0011100011110000, - 0b0011100011100000, - 0b0011100011000000, - 0 - }; - static uInt32 UNWIND_ALL[BUTTON_H] = - { - 0, - 0b0110000110000110, - 0b0111000111000110, - 0b0111100111100110, - 0b0111110111110110, - 0b0111111111111110, - 0b0111111111111110, - 0b0111111111111110, - 0b0111111111111110, - 0b0111110111110110, - 0b0111100111100110, - 0b0111000111000110, - 0b0110000110000110, - 0 - }; - const GUI::Font& font = instance().frameBuffer().font(); const int H_BORDER = 6, BUTTON_GAP = 4, V_BORDER = 4; - const int buttonWidth = BUTTON_W + 8, + const int buttonWidth = BUTTON_W + 10, buttonHeight = BUTTON_H + 10, rowHeight = font.getLineHeight(); @@ -146,41 +184,45 @@ TimeMachineDialog::TimeMachineDialog(OSystem& osystem, DialogContainer& parent, ypos = V_BORDER; // Add index info - myCurrentIdxWidget = new StaticTextWidget(this, font, xpos, ypos, " ", TextAlign::Left, kBGColor); + myCurrentIdxWidget = new StaticTextWidget(this, font, xpos, ypos, "1000", TextAlign::Left, kBGColor); myCurrentIdxWidget->setTextColor(kColorInfo); - myLastIdxWidget = new StaticTextWidget(this, font, _w - H_BORDER - font.getStringWidth("8888"), ypos, - " ", TextAlign::Right, kBGColor); + myLastIdxWidget = new StaticTextWidget(this, font, _w - H_BORDER - font.getStringWidth("1000"), ypos, + "1000", TextAlign::Right, kBGColor); myLastIdxWidget->setTextColor(kColorInfo); // Add timeline - const uInt32 tl_h = myCurrentIdxWidget->getHeight() / 2, - tl_x = xpos + myCurrentIdxWidget->getWidth() + 8, - tl_y = ypos + (myCurrentIdxWidget->getHeight() - tl_h) / 2 - 1, - tl_w = myLastIdxWidget->getAbsX() - tl_x - 8; + const uInt32 tl_h = myCurrentIdxWidget->getHeight() / 2 + 6, + tl_x = xpos + myCurrentIdxWidget->getWidth() + 8, + tl_y = ypos + (myCurrentIdxWidget->getHeight() - tl_h) / 2 - 1, + tl_w = myLastIdxWidget->getAbsX() - tl_x - 8; myTimeline = new TimeLineWidget(this, font, tl_x, tl_y, tl_w, tl_h, "", 0, kTimeline); myTimeline->setMinValue(0); ypos += rowHeight; // Add time info - myCurrentTimeWidget = new StaticTextWidget(this, font, xpos, ypos + 3, "04:32 59", TextAlign::Left, kBGColor); + myCurrentTimeWidget = new StaticTextWidget(this, font, xpos, ypos + 3, "00:00.00", TextAlign::Left, kBGColor); myCurrentTimeWidget->setTextColor(kColorInfo); - myLastTimeWidget = new StaticTextWidget(this, font, _w - H_BORDER - font.getStringWidth("XX:XX XX"), ypos + 3, - "12:25 59", TextAlign::Right, kBGColor); + myLastTimeWidget = new StaticTextWidget(this, font, _w - H_BORDER - font.getStringWidth("00:00.00"), ypos + 3, + "00:00.00", TextAlign::Right, kBGColor); myLastTimeWidget->setTextColor(kColorInfo); xpos = myCurrentTimeWidget->getRight() + BUTTON_GAP * 4; // Add buttons + myToggleWidget = new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight, STOP, + BUTTON_W, BUTTON_H, kToggle); + xpos += buttonWidth + BUTTON_GAP; + + myPlayWidget = new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight, PLAY, + BUTTON_W, BUTTON_H, kPlay); + xpos += buttonWidth + BUTTON_GAP * 4; + myRewindAllWidget = new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight, REWIND_ALL, BUTTON_W, BUTTON_H, kRewindAll); xpos += buttonWidth + BUTTON_GAP; myRewind1Widget = new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight, REWIND_1, BUTTON_W, BUTTON_H, kRewind1); - xpos += buttonWidth + BUTTON_GAP*2; - - myPlayWidget = new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight, PLAY, - BUTTON_W, BUTTON_H, kPlay); - xpos += buttonWidth + BUTTON_GAP*2; + xpos += buttonWidth + BUTTON_GAP; myUnwind1Widget = new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight, UNWIND_1, BUTTON_W, BUTTON_H, kUnwind1); @@ -188,7 +230,7 @@ TimeMachineDialog::TimeMachineDialog(OSystem& osystem, DialogContainer& parent, myUnwindAllWidget = new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight, UNWIND_ALL, BUTTON_W, BUTTON_H, kUnwindAll); - xpos = myUnwindAllWidget->getRight() + BUTTON_GAP * 3; + xpos = myUnwindAllWidget->getRight() + BUTTON_GAP * 4; // Add message myMessageWidget = new StaticTextWidget(this, font, xpos, ypos + 3, " ", @@ -224,8 +266,10 @@ void TimeMachineDialog::loadConfig() surface().applyAttributes(); } - handleWinds(); myMessageWidget->setLabel(""); + handleWinds(_enterWinds); + _enterWinds = 0; + handleToggle(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -278,6 +322,11 @@ void TimeMachineDialog::handleCommand(CommandSender* sender, int cmd, break; } + case kToggle: + instance().state().toggleTimeMachine(); + handleToggle(); + break; + case kPlay: instance().eventHandler().leaveMenuMode(); break; @@ -368,3 +417,11 @@ void TimeMachineDialog::handleWinds(Int32 numWinds) myUnwindAllWidget->setEnabled(!r.atLast()); myUnwind1Widget->setEnabled(!r.atLast()); } + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void TimeMachineDialog::handleToggle() +{ + myToggleWidget->setBitmap(instance().state().mode() == StateManager::Mode::Off ? RECORD : STOP, + BUTTON_W, BUTTON_H); +} + diff --git a/src/gui/TimeMachineDialog.hxx b/src/gui/TimeMachineDialog.hxx index adeadfd8a..de23cf095 100644 --- a/src/gui/TimeMachineDialog.hxx +++ b/src/gui/TimeMachineDialog.hxx @@ -31,6 +31,10 @@ class TimeMachineDialog : public Dialog TimeMachineDialog(OSystem& osystem, DialogContainer& parent, int width); virtual ~TimeMachineDialog() = default; + /** set/get number of winds when entering the dialog */ + void setEnterWinds(Int32 numWinds) { _enterWinds = numWinds; } + Int32 getEnterWinds() { return _enterWinds; } + private: void loadConfig() override; void handleKeyDown(StellaKey key, StellaMod mod) override; @@ -43,11 +47,14 @@ class TimeMachineDialog : public Dialog string getTimeString(uInt64 cycles); /** re/unwind and update display */ void handleWinds(Int32 numWinds = 0); + /** toggle Time Machine mode */ + void handleToggle(); private: enum { kTimeline = 'TMtl', + kToggle = 'TMtg', kPlay = 'TMpl', kRewindAll = 'TMra', kRewind10 = 'TMr1', @@ -59,6 +66,7 @@ class TimeMachineDialog : public Dialog TimeLineWidget* myTimeline; + ButtonWidget* myToggleWidget; ButtonWidget* myPlayWidget; ButtonWidget* myRewindAllWidget; ButtonWidget* myRewind1Widget; @@ -72,6 +80,8 @@ class TimeMachineDialog : public Dialog StaticTextWidget* myLastIdxWidget; StaticTextWidget* myMessageWidget; + Int32 _enterWinds; + private: // Following constructors and assignment operators not supported TimeMachineDialog() = delete; diff --git a/src/gui/UIDialog.cxx b/src/gui/UIDialog.cxx index 4b11a9f8a..029db08ad 100644 --- a/src/gui/UIDialog.cxx +++ b/src/gui/UIDialog.cxx @@ -41,7 +41,6 @@ UIDialog::UIDialog(OSystem& osystem, DialogContainer& parent, const int lineHeight = font.getLineHeight(), fontWidth = font.getMaxCharWidth(), fontHeight = font.getFontHeight(), - buttonWidth = font.getStringWidth("Defaults") + 20, buttonHeight = font.getLineHeight() + 4; const int VBORDER = 8; const int HBORDER = 10; @@ -49,7 +48,6 @@ UIDialog::UIDialog(OSystem& osystem, DialogContainer& parent, int lwidth, pwidth = font.getStringWidth("Standard"); WidgetArray wid; VariantList items; - ButtonWidget* b; const GUI::Size& ds = instance().frameBuffer().desktopSize(); // Set real dimensions diff --git a/src/gui/VideoDialog.cxx b/src/gui/VideoDialog.cxx index efde54ac9..d110cb028 100644 --- a/src/gui/VideoDialog.cxx +++ b/src/gui/VideoDialog.cxx @@ -44,7 +44,6 @@ VideoDialog::VideoDialog(OSystem& osystem, DialogContainer& parent, const int lineHeight = font.getLineHeight(), fontWidth = font.getMaxCharWidth(), fontHeight = font.getFontHeight(), - buttonWidth = font.getStringWidth("Defaults") + 20, buttonHeight = font.getLineHeight() + 4; int xpos, ypos, tabID; int lwidth = font.getStringWidth("TIA Palette "), diff --git a/src/gui/Widget.cxx b/src/gui/Widget.cxx index 47a43cce7..89d3c61af 100644 --- a/src/gui/Widget.cxx +++ b/src/gui/Widget.cxx @@ -435,6 +435,15 @@ void ButtonWidget::handleMouseUp(int x, int y, MouseButton b, int clickCount) } } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void ButtonWidget::setBitmap(uInt32* bitmap, int bmw, int bmh) +{ + _bitmap = bitmap; + _bmh = bmh; + _bmw = bmw; + _useBitmap = true; +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void ButtonWidget::drawWidget(bool hilite) { @@ -635,10 +644,10 @@ SliderWidget::SliderWidget(GuiObject* boss, const GUI::Font& font, _valueMax(100), _isDragging(false), _labelWidth(labelWidth), - _valueLabelGap(valueLabelGap), - _valueLabelWidth(valueLabelWidth), _valueLabel(""), _valueUnit(valueUnit), + _valueLabelGap(valueLabelGap), + _valueLabelWidth(valueLabelWidth), _numIntervals(0) { _flags = WIDGET_ENABLED | WIDGET_TRACK_MOUSE; diff --git a/src/gui/Widget.hxx b/src/gui/Widget.hxx index 5d94d0215..0e10ea2a9 100644 --- a/src/gui/Widget.hxx +++ b/src/gui/Widget.hxx @@ -229,6 +229,8 @@ class ButtonWidget : public StaticTextWidget, public CommandSender void setCmd(int cmd) { _cmd = cmd; } int getCmd() const { return _cmd; } + /* Sets/changes the button's bitmap **/ + void setBitmap(uInt32* bitmap, int bmw, int bmh); protected: void handleMouseUp(int x, int y, MouseButton b, int clickCount) override; @@ -349,8 +351,8 @@ class SliderWidget : public ButtonWidget int _labelWidth; string _valueLabel; string _valueUnit; - int _valueLabelWidth; int _valueLabelGap; + int _valueLabelWidth; int _numIntervals; private: