Added MessageBox class, used to present a small message to the user from

the UI.

Modified the ROM launcher to display a messagebox when running Stella for
the first time (which currently means when the 'romdir' hasn't been set).
This messagebox suggests setting the default ROM directory, and if the
user clicks on 'OK', only then is the ROM directory file browser
displayed.

Reworked some of the event handling methods in EventHandler to save
function calls on every event.


git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@2071 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
stephena 2010-07-19 16:36:37 +00:00
parent c3bcf47170
commit edb9a28eb1
8 changed files with 279 additions and 174 deletions

View File

@ -618,14 +618,61 @@ void EventHandler::poll(uInt64 time)
} }
case SDL_MOUSEMOTION: case SDL_MOUSEMOTION:
handleMouseMotionEvent(event); // Determine which mode we're in, then send the event to the appropriate place
if(myState == S_EMULATE)
{
if(myMouseEnabled)
{
int x = event.motion.xrel, y = event.motion.yrel;
myEvent->set(Event::MouseAxisXValue, x);
myEvent->set(Event::MouseAxisYValue, y);
}
}
else if(myOverlay)
{
int x = event.motion.x, y = event.motion.y;
myOverlay->handleMouseMotionEvent(x, y, 0);
}
break; // SDL_MOUSEMOTION break; // SDL_MOUSEMOTION
case SDL_MOUSEBUTTONUP: case SDL_MOUSEBUTTONUP:
case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONDOWN:
{ {
uInt8 state = event.button.type == SDL_MOUSEBUTTONDOWN ? 1 : 0; uInt8 state = event.button.type == SDL_MOUSEBUTTONDOWN ? 1 : 0;
handleMouseButtonEvent(event, state);
// Determine which mode we're in, then send the event to the appropriate place
if(myState == S_EMULATE)
{
if(myMouseEnabled)
myEvent->set(Event::MouseButtonValue, state);
}
else if(myOverlay)
{
// Take window zooming into account
Int32 x = event.button.x, y = event.button.y;
switch(event.button.button)
{
case SDL_BUTTON_LEFT:
myOverlay->handleMouseButtonEvent(
state ? EVENT_LBUTTONDOWN : EVENT_LBUTTONUP, x, y, state);
break;
case SDL_BUTTON_RIGHT:
myOverlay->handleMouseButtonEvent(
state ? EVENT_RBUTTONDOWN : EVENT_RBUTTONUP, x, y, state);
break;
case SDL_BUTTON_WHEELDOWN:
if(state)
myOverlay->handleMouseButtonEvent(EVENT_WHEELDOWN, x, y, 1);
break;
case SDL_BUTTON_WHEELUP:
if(state)
myOverlay->handleMouseButtonEvent(EVENT_WHEELUP, x, y, 1);
break;
default:
break;
}
}
break; // SDL_MOUSEBUTTONUP, SDL_MOUSEBUTTONDOWN break; // SDL_MOUSEBUTTONUP, SDL_MOUSEBUTTONDOWN
} }
@ -663,8 +710,17 @@ void EventHandler::poll(uInt64 time)
// Filter out buttons handled by OSystem // Filter out buttons handled by OSystem
if(!myOSystem->joyButtonHandled(button)) if(!myOSystem->joyButtonHandled(button))
handleJoyEvent(stick, button, state); {
// Handle buttons which switch eventhandler state
if(state && eventStateChange(myJoyTable[stick][button][kEmulationMode]))
return;
// Determine which mode we're in, then send the event to the appropriate place
if(myState == S_EMULATE)
handleEvent(myJoyTable[stick][button][kEmulationMode], state);
else if(myOverlay != NULL)
myOverlay->handleJoyEvent(stick, button, state);
}
break; // Regular button break; // Regular button
case JT_STELLADAPTOR_LEFT: case JT_STELLADAPTOR_LEFT:
@ -696,7 +752,46 @@ void EventHandler::poll(uInt64 time)
{ {
case JT_REGULAR: case JT_REGULAR:
if(myState == S_EMULATE) if(myState == S_EMULATE)
handleJoyAxisEvent(stick, axis, value); {
// Every axis event has two associated values, negative and positive
Event::Type eventAxisNeg = myJoyAxisTable[stick][axis][0][kEmulationMode];
Event::Type eventAxisPos = myJoyAxisTable[stick][axis][1][kEmulationMode];
// Check for analog events, which are handled differently
// We'll pass them off as Stelladaptor events, and let the controllers
// handle it
switch((int)eventAxisNeg)
{
case Event::PaddleZeroAnalog:
myEvent->set(Event::SALeftAxis0Value, value);
break;
case Event::PaddleOneAnalog:
myEvent->set(Event::SALeftAxis1Value, value);
break;
case Event::PaddleTwoAnalog:
myEvent->set(Event::SARightAxis0Value, value);
break;
case Event::PaddleThreeAnalog:
myEvent->set(Event::SARightAxis1Value, value);
break;
default:
{
// Otherwise, we know the event is digital
if(value > Joystick::deadzone())
handleEvent(eventAxisPos, 1);
else if(value < -Joystick::deadzone())
handleEvent(eventAxisNeg, 1);
else
{
// Turn off both events, since we don't know exactly which one
// was previously activated.
handleEvent(eventAxisNeg, 0);
handleEvent(eventAxisPos, 0);
}
break;
}
}
}
else if(myOverlay != NULL) else if(myOverlay != NULL)
{ {
// First, clamp the values to simulate digital input // First, clamp the values to simulate digital input
@ -745,7 +840,7 @@ void EventHandler::poll(uInt64 time)
break; break;
// Preprocess all hat events, converting to Stella JoyHat type // Preprocess all hat events, converting to Stella JoyHat type
// Generate two equivalent hat events representing combined direction // Generate multiple equivalent hat events representing combined direction
// when we get a diagonal hat event // when we get a diagonal hat event
if(value == SDL_HAT_CENTERED) if(value == SDL_HAT_CENTERED)
handleJoyHatEvent(stick, hat, EVENT_HATCENTER); handleJoyHatEvent(stick, hat, EVENT_HATCENTER);
@ -808,123 +903,6 @@ void EventHandler::poll(uInt64 time)
myEvent->set(Event::MouseAxisYValue, 0); myEvent->set(Event::MouseAxisYValue, 0);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EventHandler::handleMouseMotionEvent(SDL_Event& event)
{
// Determine which mode we're in, then send the event to the appropriate place
if(myState == S_EMULATE)
{
if(myMouseEnabled)
{
int x = event.motion.xrel, y = event.motion.yrel;
myEvent->set(Event::MouseAxisXValue, x);
myEvent->set(Event::MouseAxisYValue, y);
}
}
else if(myOverlay)
{
int x = event.motion.x, y = event.motion.y;
myOverlay->handleMouseMotionEvent(x, y, 0);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EventHandler::handleMouseButtonEvent(SDL_Event& event, int state)
{
// Determine which mode we're in, then send the event to the appropriate place
if(myState == S_EMULATE)
{
if(myMouseEnabled)
myEvent->set(Event::MouseButtonValue, state);
}
else if(myOverlay)
{
// Take window zooming into account
Int32 x = event.button.x, y = event.button.y;
MouseButton button;
switch(event.button.button)
{
case SDL_BUTTON_LEFT:
button = state ? EVENT_LBUTTONDOWN : EVENT_LBUTTONUP;
break;
case SDL_BUTTON_RIGHT:
button = state ? EVENT_RBUTTONDOWN : EVENT_RBUTTONUP;
break;
case SDL_BUTTON_WHEELDOWN:
if(state) button = EVENT_WHEELDOWN;
else return;
break;
case SDL_BUTTON_WHEELUP:
if(state) button = EVENT_WHEELUP;
else return;
break;
default:
return;
}
myOverlay->handleMouseButtonEvent(button, x, y, state);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EventHandler::handleJoyEvent(int stick, int button, int state)
{
#ifdef JOYSTICK_SUPPORT
if(state && eventStateChange(myJoyTable[stick][button][kEmulationMode]))
return;
// Determine which mode we're in, then send the event to the appropriate place
if(myState == S_EMULATE)
handleEvent(myJoyTable[stick][button][kEmulationMode], state);
else if(myOverlay != NULL)
myOverlay->handleJoyEvent(stick, button, state);
#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EventHandler::handleJoyAxisEvent(int stick, int axis, int value)
{
#ifdef JOYSTICK_SUPPORT
// Every axis event has two associated values, negative and positive
Event::Type eventAxisNeg = myJoyAxisTable[stick][axis][0][kEmulationMode];
Event::Type eventAxisPos = myJoyAxisTable[stick][axis][1][kEmulationMode];
// Check for analog events, which are handled differently
// We'll pass them off as Stelladaptor events, and let the controllers
// handle it
switch((int)eventAxisNeg)
{
case Event::PaddleZeroAnalog:
myEvent->set(Event::SALeftAxis0Value, value);
break;
case Event::PaddleOneAnalog:
myEvent->set(Event::SALeftAxis1Value, value);
break;
case Event::PaddleTwoAnalog:
myEvent->set(Event::SARightAxis0Value, value);
break;
case Event::PaddleThreeAnalog:
myEvent->set(Event::SARightAxis1Value, value);
break;
default:
{
// Otherwise, we know the event is digital
int deadzone = Joystick::deadzone();
if(value > -deadzone && value < deadzone)
{
// Turn off both events, since we don't know exactly which one
// was previously activated.
handleEvent(eventAxisNeg, 0);
handleEvent(eventAxisPos, 0);
}
else
handleEvent(value < 0 ? eventAxisNeg : eventAxisPos, 1);
break;
}
}
#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EventHandler::handleJoyHatEvent(int stick, int hat, JoyHat value) void EventHandler::handleJoyHatEvent(int stick, int hat, JoyHat value)
{ {
@ -1163,7 +1141,7 @@ void EventHandler::setActionMappings(EventMode mode)
if(myJoyTable[stick][button][mode] == event) if(myJoyTable[stick][button][mode] == event)
{ {
buf.str(""); buf.str("");
buf << "J" << stick << " B" << button; buf << "J" << stick << "/B" << button;
if(key == "") if(key == "")
key = key + buf.str(); key = key + buf.str();
else else
@ -1181,16 +1159,16 @@ void EventHandler::setActionMappings(EventMode mode)
if(myJoyAxisTable[stick][axis][dir][mode] == event) if(myJoyAxisTable[stick][axis][dir][mode] == event)
{ {
buf.str(""); buf.str("");
buf << "J" << stick << " axis " << axis; buf << "J" << stick << "/A" << axis;
if(eventIsAnalog(event)) if(eventIsAnalog(event))
{ {
dir = 2; // Immediately exit the inner loop after this iteration dir = 2; // Immediately exit the inner loop after this iteration
buf << " abs"; buf << "/+|-";
} }
else if(dir == 0) else if(dir == 0)
buf << " neg"; buf << "/-";
else else
buf << " pos"; buf << "/+";
if(key == "") if(key == "")
key = key + buf.str(); key = key + buf.str();
@ -1210,13 +1188,13 @@ void EventHandler::setActionMappings(EventMode mode)
if(myJoyHatTable[stick][hat][dir][mode] == event) if(myJoyHatTable[stick][hat][dir][mode] == event)
{ {
buf.str(""); buf.str("");
buf << "J" << stick << " hat " << hat; buf << "J" << stick << "/H" << hat;
switch(dir) switch(dir)
{ {
case EVENT_HATUP: buf << " up"; break; case EVENT_HATUP: buf << "/up"; break;
case EVENT_HATDOWN: buf << " down"; break; case EVENT_HATDOWN: buf << "/down"; break;
case EVENT_HATLEFT: buf << " left"; break; case EVENT_HATLEFT: buf << "/left"; break;
case EVENT_HATRIGHT: buf << " right"; break; case EVENT_HATRIGHT: buf << "/right"; break;
} }
if(key == "") if(key == "")
key = key + buf.str(); key = key + buf.str();

View File

@ -325,38 +325,6 @@ class EventHandler
void allowAllDirections(bool allow) { myAllowAllDirectionsFlag = allow; } void allowAllDirections(bool allow) { myAllowAllDirectionsFlag = allow; }
private: private:
/**
Send a mouse motion event to the handler.
@param event The mouse motion event generated by SDL
*/
void handleMouseMotionEvent(SDL_Event& event);
/**
Send a mouse button event to the handler.
@param event The mouse button event generated by SDL
*/
void handleMouseButtonEvent(SDL_Event& event, int state);
/**
Send a joystick button event to the handler
@param stick The joystick number
@param button The joystick button
@param state The state of the button (pressed or released)
*/
void handleJoyEvent(int stick, int button, int state);
/**
Send a joystick axis event to the handler
@param stick The joystick number
@param axis The joystick axis
@param value The value on the given axis
*/
void handleJoyAxisEvent(int stick, int axis, int value);
/** /**
Send a joystick hat event to the handler Send a joystick hat event to the handler

View File

@ -30,8 +30,6 @@ class EditTextWidget;
#include "Dialog.hxx" #include "Dialog.hxx"
#include "Command.hxx" #include "Command.hxx"
typedef Common::Array<EditTextWidget*> InputWidget;
class InputTextDialog : public Dialog, public CommandSender class InputTextDialog : public Dialog, public CommandSender
{ {
public: public:
@ -60,6 +58,8 @@ class InputTextDialog : public Dialog, public CommandSender
virtual void handleCommand(CommandSender* sender, int cmd, int data, int id); virtual void handleCommand(CommandSender* sender, int cmd, int data, int id);
private: private:
typedef Common::Array<EditTextWidget*> InputWidget;
InputWidget myInput; InputWidget myInput;
StaticTextWidget* myTitle; StaticTextWidget* myTitle;

View File

@ -35,6 +35,7 @@
#include "OptionsDialog.hxx" #include "OptionsDialog.hxx"
#include "GlobalPropsDialog.hxx" #include "GlobalPropsDialog.hxx"
#include "LauncherFilterDialog.hxx" #include "LauncherFilterDialog.hxx"
#include "MessageBox.hxx"
#include "OSystem.hxx" #include "OSystem.hxx"
#include "Props.hxx" #include "Props.hxx"
#include "PropsSet.hxx" #include "PropsSet.hxx"
@ -61,6 +62,7 @@ LauncherDialog::LauncherDialog(OSystem* osystem, DialogContainer* parent,
myMenu(NULL), myMenu(NULL),
myGlobalProps(NULL), myGlobalProps(NULL),
myFilters(NULL), myFilters(NULL),
myFirstRunMsg(NULL),
myRomDir(NULL), myRomDir(NULL),
mySelectedItem(0) mySelectedItem(0)
{ {
@ -237,11 +239,19 @@ void LauncherDialog::loadConfig()
// time running Stella; in this case, we should prompt the user // time running Stella; in this case, we should prompt the user
if(romdir == "") if(romdir == "")
{ {
if(!myRomDir) if(!myFirstRunMsg)
myRomDir = new BrowserDialog(this, instance().font(), _w, _h); {
StringList msg;
myRomDir->show("First startup -> Select ROM directory:", romdir, msg.push_back("This seems to be your first time running Stella.");
FilesystemNode::kListDirectoriesOnly, kStartupRomDirChosenCmd); msg.push_back("Before you can start a game, you need to");
msg.push_back("specify where your ROMs are located.");
msg.push_back("");
msg.push_back("Click 'OK' to select a default ROM directory,");
msg.push_back("or 'Cancel' to browse the filesystem manually.");
myFirstRunMsg = new MessageBox(this, instance().font(), msg,
kFirstRunMsgChosenCmd);
}
myFirstRunMsg->show();
} }
// Assume that if the list is empty, this is the first time that loadConfig() // Assume that if the list is empty, this is the first time that loadConfig()
@ -565,6 +575,15 @@ void LauncherDialog::handleCommand(CommandSender* sender, int cmd,
instance().eventHandler().quit(); instance().eventHandler().quit();
break; break;
case kFirstRunMsgChosenCmd:
// Show a file browser, starting from the users' home directory
if(!myRomDir)
myRomDir = new BrowserDialog(this, instance().font(), _w, _h);
myRomDir->show("Select ROM directory:", "~",
FilesystemNode::kListDirectoriesOnly, kStartupRomDirChosenCmd);
break;
case kStartupRomDirChosenCmd: case kStartupRomDirChosenCmd:
{ {
FilesystemNode dir(myRomDir->getResult()); FilesystemNode dir(myRomDir->getResult());

View File

@ -34,6 +34,7 @@ class BrowserDialog;
class OptionsDialog; class OptionsDialog;
class GlobalPropsDialog; class GlobalPropsDialog;
class LauncherFilterDialog; class LauncherFilterDialog;
class MessageBox;
class OSystem; class OSystem;
class Properties; class Properties;
class EditTextWidget; class EditTextWidget;
@ -103,6 +104,8 @@ class LauncherDialog : public Dialog
ContextMenu* myMenu; ContextMenu* myMenu;
GlobalPropsDialog* myGlobalProps; GlobalPropsDialog* myGlobalProps;
LauncherFilterDialog* myFilters; LauncherFilterDialog* myFilters;
MessageBox* myFirstRunMsg;
BrowserDialog* myRomDir; BrowserDialog* myRomDir;
int mySelectedItem; int mySelectedItem;
@ -119,6 +122,7 @@ class LauncherDialog : public Dialog
kOptionsCmd = 'OPTI', kOptionsCmd = 'OPTI',
kQuitCmd = 'QUIT', kQuitCmd = 'QUIT',
kFirstRunMsgChosenCmd = 'frmc',
kStartupRomDirChosenCmd = 'rmsc' kStartupRomDirChosenCmd = 'rmsc'
}; };
}; };

83
src/gui/MessageBox.cxx Normal file
View File

@ -0,0 +1,83 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2010 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id$
//============================================================================
#include "Dialog.hxx"
#include "OSystem.hxx"
#include "Version.hxx"
#include "Widget.hxx"
#include "MessageBox.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MessageBox::MessageBox(GuiObject* boss, const GUI::Font& font,
const StringList& text, int cmd)
: Dialog(&boss->instance(), &boss->parent(), 0, 0, 16, 16),
CommandSender(boss),
myCmd(cmd)
{
const int lineHeight = font.getLineHeight(),
fontWidth = font.getMaxCharWidth(),
fontHeight = font.getFontHeight();
int xpos, ypos;
WidgetArray wid;
// Set real dimensions
_w = 50 * fontWidth + 8;
_h = (text.size() + 2) * lineHeight + 20;
xpos = 10; ypos = 10;
for(uInt32 i = 0; i < text.size(); ++i)
{
new StaticTextWidget(this, font, xpos, ypos, _w - 20,
fontHeight, text[i], kTextAlignLeft);
ypos += fontHeight;
}
addOKCancelBGroup(wid, font);
addToFocusList(wid);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MessageBox::~MessageBox()
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void MessageBox::handleCommand(CommandSender* sender, int cmd, int data, int id)
{
switch(cmd)
{
case kOKCmd:
{
close();
// Send a signal to the calling class that 'OK' has been selected
// Since we aren't derived from a widget, we don't have a 'data' or 'id'
if(myCmd)
sendCommand(myCmd, 0, 0);
break;
}
default:
Dialog::handleCommand(sender, cmd, data, id);
break;
}
}

52
src/gui/MessageBox.hxx Normal file
View File

@ -0,0 +1,52 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2010 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id$
//============================================================================
#ifndef MESSAGE_BOX_HXX
#define MESSAGE_BOX_HXX
class GuiObject;
class StaticTextWidget;
#include "Dialog.hxx"
#include "Command.hxx"
#include "DialogContainer.hxx"
/**
* Show a simple message box containing the given text, with buttons
* prompting the user to accept or reject. If the user selects 'OK',
* the value of 'cmd' is returned.
*/
class MessageBox : public Dialog, public CommandSender
{
public:
MessageBox(GuiObject* boss, const GUI::Font& font, const StringList& text,
int cmd = 0);
virtual ~MessageBox();
/** Place the input dialog onscreen and center it */
void show() { parent().addDialog(this); }
private:
virtual void handleCommand(CommandSender* sender, int cmd, int data, int id);
private:
int myCmd;
};
#endif

View File

@ -25,6 +25,7 @@ MODULE_OBJS := \
src/gui/LauncherFilterDialog.o \ src/gui/LauncherFilterDialog.o \
src/gui/ListWidget.o \ src/gui/ListWidget.o \
src/gui/Menu.o \ src/gui/Menu.o \
src/gui/MessageBox.o \
src/gui/OptionsDialog.o \ src/gui/OptionsDialog.o \
src/gui/PopUpWidget.o \ src/gui/PopUpWidget.o \
src/gui/ProgressDialog.o \ src/gui/ProgressDialog.o \