Wouldn't it be neat if you could just replace this:
nextScreen->show();
with:
ScreenTransition::makeTransition(currentScreen, nextScreen);
And have a nice looking animated slide transition?
Well, now you can.
Try it out in the MAUIex example. Replace
screens[index]->show();
with
ScreenTransition::makeTransition(this, screens[index], 1, 400);
in the MAK_SOFTLEFT case in test.cpp.
And replace the
previous.show();
with
ScreenTransition::makeTransition(this, previous, -1, 400);
in the different other screens.
The implementation is a bit awkward, any suggestions for improvements are greatly appreciated.
Two things annoyed me with MAUI when doing this:
- There is no Engine::getMain() to mirror the Engine::setMain(Widget* main). So the "from screen" has to be given to makeTransition().
- Widget.draw(bool force) is recursive, but it does not use the force variable in the recursive calls to the children. I'm not sure, but I think this is actually a bug. Line 161 in Widget.cpp ((*it)->draw()
should be (*it)->draw(forceDraw);. As you can see in my drawWidget I have to enable, draw and disable the widgets as a work-around, not pretty!
I have only made a slide left or right transition (à la iPhone), but you should be able to make all kinds of transitions using the same approach.
Have fun!
-------------------- ScreenTransition.h --------------------------
/* Copyright (C) 2010 Oscar Lindberg
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License, version 2, as published by
the Free Software Foundation.
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; see the file COPYING. If not, write to the Free
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
*/
#ifndef SCREENTRANSITION_H_
#define SCREENTRANSITION_H_
#include
#include
#include
class ScreenTransition : public MAUI::Widget, public MAUtil::TimerListener
{
public:
~ScreenTransition()
{}
static void makeTransition(MAUI::Screen* fromScreen, MAUI::Screen* toScreen, int direction, int duration);
private:
ScreenTransition()
: Widget(0, 0, 0, 0, NULL)
{}
protected:
static MAUI::Screen* gScreen;
static ScreenTransition* gWidget;
void drawWidget();
double square_sigmoid(double t);
void updateAlpha(int now);
void runTimerEvent();
MAUI::Screen* toScreen;
MAUI::Widget* fromMainWidget;
MAUI::Widget* toMainWidget;
int duration;
int direction;
int start_time;
double alpha; //0.0 = fromScreen, 1.0 = toScreen
};
#endif /* SCREENTRANSITION_H_ */
---------------- ScreenTransition.cpp ---------------------
/* Copyright (C) 2010 Oscar Lindberg
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License, version 2, as published by
the Free Software Foundation.
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; see the file COPYING. If not, write to the Free
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
*/
#import "ScreenTransition.h"
#import
MAUI::Screen* ScreenTransition::gScreen = 0;
ScreenTransition* ScreenTransition::gWidget = 0;
void ScreenTransition::makeTransition(MAUI::Screen* fromScreen, MAUI::Screen* toScreen, int direction, int duration)
{
if (gScreen !=
{
return;
}
gScreen = new MAUI::Screen();
gWidget = new ScreenTransition();
gScreen->setMain(gWidget);
gWidget->fromMainWidget = fromScreen->getMain();
gWidget->toScreen = toScreen;
gWidget->toMainWidget = toScreen->getMain();
gWidget->direction = direction;
gWidget->duration = duration;
gWidget->start_time = maGetMilliSecondCount();
gWidget->updateAlpha(gWidget->start_time);
// This will tell the widget to fix stuff before being drawn
gWidget->toMainWidget->update();
gScreen->show();
MAUtil::Environment::getEnvironment().addTimer(gWidget, 20, -1);
}
void ScreenTransition::drawWidget()
{
int scrW = EXTENT_X(maGetScrSize());
int scrH = EXTENT_Y(maGetScrSize());
Gfx_translate((int)(-direction*alpha*scrW), 0);
fromMainWidget->setEnabled(true);
fromMainWidget->draw();
fromMainWidget->setEnabled(false);
Gfx_translate(direction*scrW, 0);
toMainWidget->setEnabled(true);
toMainWidget->draw();
toMainWidget->setEnabled(false);
}
double ScreenTransition::square_sigmoid(double t)
{
if (t < 0.
{
return 0.0;
} else if (t < 0.5) {
return 2*t*t;
} else if (t < 1.
{
float tm1 = (t-1.0);
return 1.0-2*tm1*tm1;
} else {
return 1.0;
}
}
void ScreenTransition::updateAlpha(int now)
{
alpha = double(now - this->start_time) / double(duration);
alpha = square_sigmoid(alpha);
}
void ScreenTransition::runTimerEvent()
{
updateAlpha(maGetMilliSecondCount());
if (alpha >= 1.
{
MAUtil::Environment::getEnvironment().removeTimer(this);
this->toScreen->show();
delete gScreen;
gScreen = 0;
delete gWidget; // I.e. this
gWidget = 0;
}
else
{
this->requestRepaint();
}
}