Cool window effects

From CEGUI Wiki - Crazy Eddie's GUI System (Open Source)
Jump to: navigation, search

Inactive windows become transparent

Please discuss this snippet within the cool window effects - Inactive windows become trans. thread.

Do you know about the cool effects that the composite extension can create on linux ? There are many. I wanted to implement one into my game: make the inactive windows become transparent and only the active one be 100% opaque. (see the links in this sentence for screenshots.) This effect can be interesting if you want your users to concentrate 100% on the active window and the inactive ones aren't important at this moment. But what if the user has to see two windows or more at the same time (and with full opacity) ? We'll see how to handle this.

The theory

There is more then one way to achieve this, but I'll only present the one I chose. CEGUI has something that is called global events. These are events that are fired independently on the widget they apply to. I'll try with an example: sometimes you need to know when a button is clicked, no matter what button it is, you want to know it for EVERY button. This is what global events are for.

We'll subscribe to two global events: "a window gets activated" and "a window gets closed". When a window gets activated, we add it to a stack which we use to keep track of the windows and the order they where activated. We also make the last active window become transparent. When a window gets closed, we remove it from the stack and activate the one that is on the top of the stack now. (that is the window that was active just before the one we just closed.)

We keep the root window away from it, that means we never make the root window transparent, because this would make the tooltips become transparent. You need to have an empty root window that is a container for your other windows.

The praxis (code)

Now let's go ! First, we need to register callback functions to the two global events:

CEGUI::GlobalEventSet::getSingleton( ).subscribeEvent( CEGUI::Window::EventNamespace + "/" + CEGUI::Window::EventActivated,
                                                       CEGUI::Event::Subscriber(&onWindowActivated) );
CEGUI::GlobalEventSet::getSingleton( ).subscribeEvent( CEGUI::Window::EventNamespace + "/" + CEGUI::Window::EventDestructionStarted,
                                                       CEGUI::Event::Subscriber(&onWindowClosed) );

We also need a stack to keep track of the windows. I chose to use the std::list type instead of the std::stack, because we need to be able to either delete all references of one element or to search the stack. So here it is:

std::list<CEGUI::Window *> pWinHistory;

Also, don't forget to put your root window just before setting it as the root window:

pWinHistory.push_back(pMyRootWindow);
CEGUI::System::getSingleton().setGUISheet(pMyRootWindow);

One thing you need to know about the root window is that you need an empty root window, containing one or more child windows that contain all GUI items. Your root window will never become transparent, thus you need an empty "container" root window.

And now we implement our two callback functions. I excessively commented the functions.

onWindowActivated:

/// Called everytime a window gets activated.
/** This function is called by CEGUI every time that a window gets the focus.
 *
 *  Currently, we use this function to keep track of the order that windows where
 *  active, so when closing a window we can activate the last one, as this isn't
 *  done automatically by CEGUI.
 *
 * \param e The event arguments.
 *
 * \return Always true.
 *
 * \author Pompei2
 */
bool onWindowActivated( const CEGUI::EventArgs &ea )
{
	try {
		const CEGUI::WindowEventArgs& we = static_cast<const CEGUI::WindowEventArgs&>(ea);
		CEGUI::Window *pLastWin = pWinHistory.back( );
 
		// We only work with FrameWindows.
		if( !we.window->testClassName( CEGUI::FrameWindow::EventNamespace ) )
			return true;
 
		// If it is the same window as before, ignore it.
		if( pLastWin == we.window )
			return true;
 
		if( pLastWin != CEGUI::System::getSingleton().getGUISheet() ) {
			// If every time a window gets activated we make the last active window become
			// transparent, this will result in all inactive windows being transparent.
			pLastWin->setProperty( "Alpha", "0.25" );
 
			// But we never make the root window transparent, as this would make all tooltips
			// become transparent, and we don't want this !
		}
 
		// We need the active window to not inherit the transparence of its parents.
		we.window->setProperty( "InheritsAlpha", "false" );
 
		// Finally, we add the new window to the stack.
 
		// One could also check if it's already present in the stack and if yes, just put it on the top.
		// You would have to do this if you want to set the transparency depending on the window's position
		// in the stack (see "Extending the effect").
		pWinHistory.push_back( we.window );
 	} catch( CEGUI::Exception& e ) {
		fprintf( stderr, "CEGUI error: %s\n", e.getMessage( ).c_str( ) );
		return true;
	}
 
	return true;
}

And onWindowClosed:

/// Called everytime a window gets closed.
/** This function is called by CEGUI just before a window gets closed.
 *
 *  Currently, we use this function to activate the previous window
 *  when closing this one, as it's not done automatically by CEGUI.
 *
 * \param e The event arguments.
 *
 * \return Always true.
 *
 * \author Pompei2
 */
bool onWindowClosed( const CEGUI::EventArgs &ea )
{
	try {
		const CEGUI::WindowEventArgs& we = static_cast<const CEGUI::WindowEventArgs&>(ea);
		CEGUI::Window *pLastWin = NULL;
 
		// We only work with FrameWindows.
		if( !we.window->testClassName( CEGUI::FrameWindow::EventNamespace ) )
			return true;
 
		// Delete the current window from the stack.
		// CARE: std::list::remove removes ALL occurences of we.window from the stack !
		// This is VERY important to know, as if you activate window A, then window B and then A again,
		// the stack will contain A twice: {A,B,A}.
		pWinHistory.remove( we.window );
 
		// Now we get the window that was active before the current one:
		pLastWin = m_pWinHistory.back( );
 
		// re-activate it (like windos, linux, .. all do).
		pLastWin->activate( );
 
		// And set it to be opaque again.
		// You could either do this here or you do this in the onWindowActivate function, the result is the same,
		// as the call to CEGUI::Window::activate above results in onWindowActivate to be called.
		pLastWin->setProperty( "Alpha", "1.0" );
	} catch( CEGUI::Exception& e ) {
		fprintf( stderr, "CEGUI error: %s\n", e.getMessage( ).c_str( ) );
		return true;
	}
 
	return true;
}

That's all.

One important thing you might not know yet, is that CEGUI DOESN'T activate a window that gets shown. you have to manually activate a window when you create it (and want it to be active, of course).

Extending the effect

  • You could also automatically activate a window as soon as it's shown. for this you could subscribe to the "a window gets shown" global event and then just call ea.window->activate( );

Edit: Hmm I tried this one but wasn't able to get it to work. If someone has success, please tell us how you did it in the forums.

  • You could try to make the window's transparency degree dependent on its position in the stack. That means the most recent windows get less transparent while the "oldest" windows get very transparent. To do this, just loop trough the stack - that would be a list - and set every window's transparency accordingly. Do this when a window gets active AND when a window gets closed. You would also need to never have the same window twice in the stack, as it would screw your results up.


--User:Pompei2 06:00, 6 May 2007 (PDT)




you can add more cool effects here.