Screen transition in MAUI

14 posts / 0 new
Last post
offe
offe's picture
Offline
Joined: 7 Jan 2010
Posts:
Screen transition in MAUI

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()Wink 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 != Innocent
		{
			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.Innocent {
			return 0.0;
		} else if (t < 0.5) {
			return 2*t*t;
		} else if (t < 1.Innocent {
			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.Innocent
		{
			MAUtil::Environment::getEnvironment().removeTimer(this);
			this->toScreen->show();
			delete gScreen;
			gScreen = 0;
			delete gWidget; // I.e. this
			gWidget = 0;
		}
		else
		{
			this->requestRepaint();
		}
	}

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
fatalerror
fatalerror's picture
Offline
Mobile Conjurer
Joined: 2 Oct 2009
Posts:

Hello offe,

All I have to say to that is, that is simply awesome!
I haven't run it yet, but I surely will tomorow.

Regards,
Ali

Sam Pickard
rival's picture
Offline
Mobile Archmage
Joined: 19 Mar 2009
Posts:

Top banana. Are you going to build in different effects, like showing from beneath the old screen, or moving over the top of the old screen?

offe
offe's picture
Offline
Joined: 7 Jan 2010
Posts:

Actually I don't plan to do more "effects". I mainly wanted to get some input on how robust this approach is.

It should be simple to add more effects. If you try and have problems, I can try to help you.

Sam Pickard
rival's picture
Offline
Mobile Archmage
Joined: 19 Mar 2009
Posts:

I've done a quick update. Offe can probably make this better, this is just me hacking it around a bit.

As well as the iPhone side-by-side effect, I've added a mode to reveal the new screen from below the old screen, and one to push the new screen over the top of the old screen. If you use these to navigate through the application, you get a nice effect like dealing cards onto a pile and then taking them off again.

I've also made a small change where it stores the screen width for the duration of the effect, rather than looking it up on each redraw.

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 <MAUI/Widget.h>
    #include <MAUI/Screen.h>
    #include <MAUtil/Environment.h>

	enum TransitionType
	{
		SIDEBYSIDE,
		REVEALFROMBELOW,
		MOVEOVERTOP
	};

    class ScreenTransition : public MAUI::Widget, public MAUtil::TimerListener
    {
    public:
       ~ScreenTransition()
       {}
       static void makeTransition(MAUI::Screen* fromScreen, MAUI::Screen* toScreen, int direction, int duration, TransitionType type = SIDEBYSIDE);
    private:
       ScreenTransition()
       : Widget(0, 0, 0, 0, NULL)
       {
    	    scrW = EXTENT_X(maGetScrSize());
       }

    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;
       int scrW;

       static TransitionType currentType;
       double alpha; //0.0 = fromScreen, 1.0 = toScreen
    };

    #endif /* 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.
    */

    #import "ScreenTransition.h"
    #import <MAUtil/Graphics.h>

       MAUI::Screen* ScreenTransition::gScreen = 0;
       ScreenTransition* ScreenTransition::gWidget = 0;
       TransitionType ScreenTransition::currentType = SIDEBYSIDE;

       void ScreenTransition::makeTransition(MAUI::Screen* fromScreen, MAUI::Screen* toScreen, int direction, int duration, TransitionType type)
       {
          if (gScreen != Innocent
          {
             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);
          currentType = type;

          // 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()
       {
      	   if(currentType == REVEALFROMBELOW)
    	   {
     		  toMainWidget->setEnabled(true);
     		  toMainWidget->draw();
     	      toMainWidget->setEnabled(false);

     		  Gfx_translate((int)(-direction*alpha*scrW), 0);
 			  fromMainWidget->setEnabled(true);
 			  fromMainWidget->draw();
 			  fromMainWidget->setEnabled(false);
    	   }
    	   else if(currentType == MOVEOVERTOP)
    	   {
  			  fromMainWidget->setEnabled(true);
  			  fromMainWidget->draw();
  			  fromMainWidget->setEnabled(false);

  			  Gfx_translate((int)(-direction*alpha*scrW) + scrW, 0);
     		  toMainWidget->setEnabled(true);
     		  toMainWidget->draw();
     	      toMainWidget->setEnabled(false);
    	   }
		   else
		   {
			   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.Innocent {
             return 0.0;
          } else if (t < 0.5) {
             return 2*t*t;
          } else if (t < 1.Innocent {
             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.Innocent
          {
             MAUtil::Environment::getEnvironment().removeTimer(this);
             this->toScreen->show();
             delete gScreen;
             gScreen = 0;
             delete gWidget; // I.e. this
             gWidget = 0;
          }
          else
          {
             this->requestRepaint();
          }
       }
andreimpi
andreimpi's picture
Offline
Joined: 31 May 2010
Posts:

Hi Guys,

Very tiny for my first post, however you have to start somewhere. I expanded on rival's updates and added from bottom up, top down, left to right and the necessary reveals on top of those. I prefer fade ins from bottom up when soft-keys are used so figured I would share.

Regards

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 <MAUI/Widget.h>
#include <MAUI/Screen.h>
#include <MAUtil/Environment.h>

enum TransitionType
{
  SIDEBYSIDE,
  REVEALFROMLEFT,
  MOVEOVERRIGHT,
  MOVEFROMBELOW,
  REVEALFROMTOP,
  MOVEFROMTOP,
  REVEALFROMBELOW
};

class ScreenTransition : public MAUI::Widget, public MAUtil::TimerListener
{
public:
   ~ScreenTransition()
   {}
   static void makeTransition(MAUI::Screen* fromScreen, MAUI::Screen* toScreen, int direction, int duration, TransitionType type = SIDEBYSIDE);
private:
   ScreenTransition()
   : Widget(0, 0, 0, 0, NULL)
   {
	   scrW = EXTENT_X(maGetScrSize());
	   scrH = EXTENT_Y(maGetScrSize());
   }

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;
   int scrW;
   int scrH;

   static TransitionType currentType;
   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 <MAUtil/Graphics.h>

MAUI::Screen* ScreenTransition::gScreen = 0;
ScreenTransition* ScreenTransition::gWidget = 0;
TransitionType ScreenTransition::currentType = SIDEBYSIDE;

void ScreenTransition::makeTransition(MAUI::Screen* fromScreen, MAUI::Screen* toScreen, int direction, int duration, TransitionType type)
{
  if (gScreen != Innocent
  {
	 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);
  currentType = type;

  // 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()
{
	if(currentType == REVEALFROMLEFT)
	{
		toMainWidget->setEnabled(true);
		toMainWidget->draw();
		toMainWidget->setEnabled(false);

		Gfx_translate((int)(-direction*alpha*scrW), 0);
		fromMainWidget->setEnabled(true);
		fromMainWidget->draw();
		fromMainWidget->setEnabled(false);
	}
	else if(currentType == MOVEOVERRIGHT)
	{
		fromMainWidget->setEnabled(true);
		fromMainWidget->draw();
		fromMainWidget->setEnabled(false);

		Gfx_translate((int)(-direction*alpha*scrW) + scrW, 0);
		toMainWidget->setEnabled(true);
		toMainWidget->draw();
		toMainWidget->setEnabled(false);
	}
	else if(currentType == SIDEBYSIDE)
	{
		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);
	}
	else if(currentType == MOVEFROMBELOW)
	{
		fromMainWidget->setEnabled(true);
		fromMainWidget->draw();
		fromMainWidget->setEnabled(false);

		Gfx_translate(0, (int)(-direction*alpha*scrH + scrH));
		toMainWidget->setEnabled(true);
		toMainWidget->draw();
		toMainWidget->setEnabled(false);
	}
	else if(currentType == REVEALFROMTOP)
	{
		toMainWidget->setEnabled(true);
		toMainWidget->draw();
		toMainWidget->setEnabled(false);

		Gfx_translate(0, (int)(-direction*alpha*scrH));
		fromMainWidget->setEnabled(true);
		fromMainWidget->draw();
		fromMainWidget->setEnabled(false);
	}
	else if(currentType == MOVEFROMTOP)
	{
		fromMainWidget->setEnabled(true);
		fromMainWidget->draw();
		fromMainWidget->setEnabled(false);

		Gfx_translate(0, (int)(direction*alpha*scrH - scrH));
		toMainWidget->setEnabled(true);
		toMainWidget->draw();
		toMainWidget->setEnabled(false);
	}
	else if(currentType == REVEALFROMBELOW)
	{
		toMainWidget->setEnabled(true);
		toMainWidget->draw();
		toMainWidget->setEnabled(false);

		Gfx_translate(0, (int)(direction*alpha*scrH));
		fromMainWidget->setEnabled(true);
		fromMainWidget->draw();
		fromMainWidget->setEnabled(false);
	}
}

double ScreenTransition::square_sigmoid(double t)
{
  if (t < 0.Innocent {
	 return 0.0;
  } else if (t < 0.5) {
	 return 2*t*t;
  } else if (t < 1.Innocent {
	 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.Innocent
  {
	 MAUtil::Environment::getEnvironment().removeTimer(this);
	 this->toScreen->show();
	 delete gScreen;
	 gScreen = 0;
	 delete gWidget; // I.e. this
	 gWidget = 0;
  }
  else
  {
	 this->requestRepaint();
  }
}
madison
madison's picture
Offline
Joined: 25 Oct 2010
Posts:

Dear guys,
How can I implement the "turn page" effects?
regards,

Sam Pickard
rival's picture
Offline
Mobile Archmage
Joined: 19 Mar 2009
Posts:

Hi,

Doing a page turn effect will be very difficult if you want to see the screen deform as if it is a page turning, the you have to start with rebuilding the current screen into a bitmap. You will then need to apply the transformations. It is possible, but I think that you will get a poor return for the amount of work you'll have to do.

madison
madison's picture
Offline
Joined: 25 Oct 2010
Posts:
rival wrote:

you have to start with rebuilding the current screen into a bitmap.

Dear Sam,
Your are always so kind, thank you. But now, I want to know how to rebuild the current screen into a bitmap and then how to implement that page turn effect?
I am looking forward to your reply. The code example will be more appreciated.
Best wishes!

Sam Pickard
rival's picture
Offline
Mobile Archmage
Joined: 19 Mar 2009
Posts:

Madison,

I've no idea, you'd need to ask the MoSync SDK developers. The widgets currently make direct calls to the operating system to paint themselves. I think that you'd probably have to write a new UI system to do it, or have a very fixed UI. If it were fixed, then you could create a bitmap in advance in Photoshop, but it would have to exactly match the UI on the device.

Anthony Hartley
Hartley's picture
Offline
Mobile Wizard
Joined: 25 Sep 2007
Posts:

I think there might be a way that you can do this effect by cheating.

Getting the reversed text on the other side of the paper is a bit difficult, if you
dont need that, I would suggest drawing a triangle that gets bigger over time, do this at the bottom right of the screen. If you do this really fast no one will notice the lack of inverted/mirrored text.

Do you know what I mean ?

/Tony

madison
madison's picture
Offline
Joined: 25 Oct 2010
Posts:

Thank you, Sam and Hartley!
I got it by cheating as you said. The code will be posted several days. Please give me suggestion as you can.

madison
madison's picture
Offline
Joined: 25 Oct 2010
Posts:
rival wrote:

Hi,

Doing a page turn effect will be very difficult if you want to see the screen deform as if it is a page turning, the you have to start with rebuilding the current screen into a bitmap. You will then need to apply the transformations. It is possible, but I think that you will get a poor return for the amount of work you'll have to do.

Dear Sam,
How can I copy the current screen into a bitmap?

Sam Pickard
rival's picture
Offline
Mobile Archmage
Joined: 19 Mar 2009
Posts:

There isn't any easy way of doing it at all that I know of. You would have to re-write the widget system, replacing system calls painting the UI to screen to a bitmap instead. Unless anyone else knows of another way (like capturing the screen buffer to a bitmap for instance) then I think it is beyond any return you'll get for one visual effect. Sorry.