1 /*--License:
2 	Kyra Sprite Engine
3 	Copyright Lee Thomason (Grinning Lizard Software) 2001-2005
4 	www.grinninglizard.com/kyra
5 	www.sourceforge.net/projects/kyra
6 
7 	Kyra is provided under the LGPL.
8 
9 	I kindly request you display a splash screen (provided in the HTML documentation)
10 	to promote Kyra and acknowledge the software and everyone who has contributed to it,
11 	but it is not required by the license.
12 
13 --- LGPL License --
14 
15     This library is free software; you can redistribute it and/or
16     modify it under the terms of the GNU Lesser General Public
17     License as published by the Free Software Foundation; either
18     version 2.1 of the License, or (at your option) any later version.
19 
20     This library is distributed in the hope that it will be useful,
21     but WITHOUT ANY WARRANTY; without even the implied warranty of
22     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23     Lesser General Public License for more details.
24 
25     You should have received a copy of the GNU Lesser General Public
26     License along with this library; if not, write to the Free Software
27     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
28 
29 	The full text of the license can be found in lgpl.txt
30 */
31 
32 #include "../engine/kyra.h"
33 #include "SDL.h"
34 #include "../spriteed/CONSOLEFONT.h"
35 #include "../guiExtended/KrImageListBox.h"
36 #include <stdlib.h>
37 
38 /*	This simple program demonstrates the use of all the default widgets.
39 	It should be build as part of your Kyra build.
40 
41 	Start with "main()" below, reading through how it works. Then refer up to
42 	the class files that main uses as they occur.
43 */
44 
45 
46 /*	This creates a subclass of the textwidget. If it receives an activation
47 	message, it will change the text to "activated", or "de-activated".
48 	When made the listener of a button, it will print "activated" or "de-activated"
49 	as the button is pressed or released with the mouse, or when the button
50 	hot key is pressed or released.
51 */
52 
53 /*
54 class MyTextWidget : public KrTextWidget
55 {
56   public:
57 	MyTextWidget(	const char* _message,
58 					int width, int height,
59 					bool drawBorder,
60 					bool drawBackground,
61 					bool editable,
62 					const KrScheme& scheme
63 				 )
64 		: KrTextWidget( width, height, drawBorder, drawBackground, editable, scheme ), message( _message )	{}
65 
66 
67 	virtual bool HandleWidgetEvent(	KrWidget* source, const KrWidgetEvent& event )
68 	{
69 		SetTextChar( message );
70 		return true;
71 	}
72 
73 	private:
74 		const char* message;
75 };
76 */
77 
78 /*	A console window. It listens to all the other widgets, and prints out the
79 	event messages that travel through the system.
80 */
81 
82 class MyConsole : public KrConsole
83 {
84   public:
MyConsole(int width,int height,int lineSpacing,const KrScheme & scheme)85   	MyConsole(	int width, int height,
86 				int lineSpacing,
87 				const KrScheme& scheme ) : KrConsole( width, height, lineSpacing, scheme )
88 	{}
89 
90 
HandleWidgetEvent(KrWidget * source,const KrWidgetEvent & event)91 	virtual bool HandleWidgetEvent(	KrWidget* source, const KrWidgetEvent& event )
92 	{
93 		char buf[ 256 ];
94 		//const char* eventDesc[] = { "NONE", "ACTIVATED", "DEACTIVATED", "COMMAND", "SELECTION" };
95 
96 		switch ( event.type )
97 		{
98 			case KrWidgetEvent::ACTIVATED:
99 				sprintf( buf, "ACTIVATED %s source=0x%x\n", source->WidgetType(), (uintptr_t)source );
100 				break;
101 
102 			case KrWidgetEvent::DEACTIVATED:
103 				sprintf( buf, "DEACTIVATED %s source=0x%x\n", source->WidgetType(), (uintptr_t)source );
104 				break;
105 
106 			case KrWidgetEvent::COMMAND:
107 				sprintf( buf, "COMMAND %s source=0x%x command=%s arg=%s\n", source->WidgetType(), (uintptr_t)source, event.command.command, event.command.arg );
108 				break;
109 
110 			case KrWidgetEvent::SELECTION:
111 				sprintf( buf, "SELECTION %s source=0x%x id=%d text=%s\n", source->WidgetType(), (uintptr_t)source, event.selection.index, event.selection.text ? event.selection.text : "(null)" );
112 				break;
113 
114 			default:
115 				sprintf( buf, "ERROR" );
116 				break;
117 		}
118 		PushText( buf );
119 		return true;
120 	}
121 };
122 
123 
124 #define SDL_TIMER_EVENT ( SDL_USEREVENT + 0 )
125 const int TIMER_INTERVAL = 60;
126 
127 /*	A timer callback, used to test widgets on a timer.
128 */
TimerCallback(Uint32 interval)129 Uint32 TimerCallback(Uint32 interval)
130 {
131 	SDL_Event event;
132 	event.type = SDL_TIMER_EVENT;
133 
134 	SDL_PeepEvents( &event, 1, SDL_ADDEVENT, 0 );
135 	return TIMER_INTERVAL;
136 }
137 
138 
139 class GUI : public IKrWidgetListener
140 {
141   private:
142 	KrTextWidget *text0, *text1;
143 	KrPushButton *push0, *push1;
144 	KrListBox *listBox0, *listBox1;
145 	KrProgress* progressBar;
146 
147   public:
GUI(KrEngine * engine,KrFontResource * consoleFontRes)148 	GUI( KrEngine* engine, KrFontResource* consoleFontRes )
149 	{
150 		/*	In order to test the icon / decal functionality of the buttons,
151 			grab some sprites from the test directory. In order to not
152 			go crazy with compile & make ordering, make the icons optional.
153 		*/
154 		bool useIcons = false;
155 		if ( engine->Vault()->LoadDatFile( "../tests/space.dat" ) )
156 		{
157 			useIcons = true;
158 		}
159 
160 		/*	Generally, you'll want all your widgets to share a color scheme.
161 			This uses the default. However, you can reach in and set a primary and
162 			matching secondary color to customize the look of the widgets.
163 		*/
164 		KrScheme scheme( consoleFontRes );
165 
166 		/*	--- Toggle Buttons ---
167 
168 			2 toggle buttons in the lower left of the screen. Create them,
169 			with size and scheme, add to the Tree() and you're ready to go!
170 			Like all KrImage's, they are positioned with SetPos().
171 
172 			The top button (0) responds to a control-2, and the bottom
173 			button (1) responds to control-3.
174 
175 			Toggle buttons automatically group with their siblings on the Tree.
176 		*/
177 		KrToggleButton* toggle0 = new KrToggleButton( 30, 30, scheme );
178 		engine->Tree()->AddNode( 0, toggle0 );
179 		toggle0->SetPos( 10, 150 );
180 
181 		KrToggleButton* toggle1 = new KrToggleButton( 30, 30, scheme );
182 		engine->Tree()->AddNode( 0, toggle1 );
183 		toggle1->SetPos( 10, 190 );
184 
185 		toggle0->SetAccelerator( KMOD_CTRL, SDLK_2 );
186 		toggle1->SetAccelerator( KMOD_CTRL, SDLK_3 );
187 
188 		KrImNode* group = new KrImNode();
189 		engine->Tree()->AddNode( 0, group );
190 
191 		KrToggleButton *toggle2 = new KrToggleButton( 30, 30, scheme );
192 		engine->Tree()->AddNode( group, toggle2 );
193 		toggle2->SetPos( 60, 150 );
194 
195 		KrToggleButton *toggle3 = new KrToggleButton( 30, 30, scheme );
196 		engine->Tree()->AddNode( group, toggle3 );
197 		toggle3->SetPos( 60, 190 );
198 
199 		/* --- Left List Box ---
200 
201 		This list box is underfilled. Each line you added is added to the bottom
202 		of the box. An index is returned that identifies it in the future. A SELECTION
203 		event will return the index, allow you to know what the user selected.
204 
205 		*/
206 		listBox0 = new KrListBox( 150, 200, scheme, false );
207 		listBox0->SetPos( 330, 10 );
208 		engine->Tree()->AddNode( 0, listBox0 );
209 		listBox0->AddTextChar( "item 0" );
210 		listBox0->AddTextChar( "item 1" );
211 		listBox0->AddTextChar( "item 2" );
212 		listBox0->AddTextChar( "item 3" );
213 
214 		/* --- Right List Box --
215 
216 		This list box is overfilled. Which means you need to be able to scroll it
217 		to see all the contents.
218 
219 		How to do this?
220 
221 		The push buttons, below, will scroll the selection up or down when they are
222 		pressed.
223 		*/
224 
225 		listBox1 = new KrListBox( 150, 200, scheme, true );
226 		listBox1->SetPos( 500, 10 );
227 		engine->Tree()->AddNode( 0, listBox1 );
228 		for( int i=0; i<20; ++i )
229 		{
230 			char buf[ 256 ];
231 			sprintf( buf, "item %d", i );
232 			listBox1->AddTextChar( buf );
233 		}
234 
235 		/* --- Push Buttons ---
236 
237 		These push buttons are subclasses of the default push button. When pressed,
238 		they will scroll the list box up or down.
239 
240 		The buttons will respond to control-0 and control-1.
241 
242 		See the ScrollButtonConnector comments for how this system works.
243 		*/
244 		push0 = new KrPushButton( 50, 50, scheme );
245 		engine->Tree()->AddNode( 0, push0 );
246 		push0->SetPos( 10, 10 );
247 
248 		push1 = new KrPushButton( 50, 50, scheme );
249 		push1->SetPos( 10, 70 );
250 		engine->Tree()->AddNode( 0, push1 );
251 
252 		push0->SetAccelerator( KMOD_CTRL, SDLK_0 );
253 		push1->SetAccelerator( KMOD_CTRL, SDLK_1 );
254 
255 		push0->widgetPublish.AddListener( this );
256 		push1->widgetPublish.AddListener( this );
257 
258 		/* --- Text Widgets ---
259 		*/
260 		text0 = new KrTextWidget( 200, 50, true, true, true, scheme );
261 		text0->SetPos( 100, 10 );
262 		engine->Tree()->AddNode( 0, text0 );
263 
264 		text1 = new KrTextWidget( 200, 50, true, true, true, scheme );
265 		text1->SetPos( 100, 70 );
266 		engine->Tree()->AddNode( 0, text1 );
267 
268 		/* --- Push buttons with icons ---
269 
270 		Here to test the icon functionality, with oddly sized buttons. The Medium ship
271 		resource is a completely arbritrary choice.
272 
273 		*/
274 
275 		KrPushButton* iconButton0 = 0;
276 		KrPushButton* iconButton1 = 0;
277 		KrPushButton* graphicButton = 0;
278 
279 		if ( useIcons )
280 		{
281 			KrSprite* sprite = 0;
282 			KrSpriteResource* logoRes = engine->Vault()->GetSpriteResource( "LOGO" );
283 			GLASSERT( logoRes );
284 
285 			// Button 0
286 			iconButton0 = new KrPushButton( 70, 50, scheme );
287 			sprite = new KrSprite( logoRes );
288 
289 			iconButton0->SetIcon( sprite );
290 			engine->Tree()->AddNode( 0, iconButton0 );
291 			iconButton0->SetPos( 10, 240 );
292 			iconButton0->SetTextChar( "Button" );
293 
294 			// Button 1
295 			iconButton1 = new KrPushButton( 30, 50, scheme );
296 			sprite = new KrSprite( logoRes );
297 
298 			engine->Tree()->AddNode( 0, iconButton1 );
299 			iconButton1->SetIcon( sprite );
300 			iconButton1->SetPos( 90, 240 );
301 
302 			// A user drawn button.
303 			KrSpriteResource* buttonRes = engine->Vault()->GetSpriteResource( "BUTTON" );
304 			KrSprite* buttonSprite = new KrSprite( buttonRes );
305 
306 			graphicButton = new KrPushButton( buttonSprite, scheme );
307 			engine->Tree()->AddNode( 0, graphicButton );
308 			graphicButton->SetPos( 160, 240 );
309 
310 			/*
311 			KrImageListBox* imageBox = new KrImageListBox( 90, 200, 60, scheme );
312 			imageBox->SetPos( 670, 10 );
313 			engine->Tree()->AddNode( 0, imageBox );
314 			for( int i=0; i<5; ++i )
315 				imageBox->AddImage( new KrSprite( logoRes ), "test" );
316 			*/
317 		}
318 
319 		/* --- Progress Bar --- */
320 		progressBar = new KrProgress( 200, 50, scheme );
321 		progressBar->SetPos( 10, 450 );
322 		progressBar->SetMaxValue( 122 );
323 		engine->Tree()->AddNode( 0, progressBar );
324 
325 		/* --- Eavsdropping console ---
326 
327 		This console window is set up as a listener of everyone, and will print
328 		every event published to the system.
329 		*/
330 		MyConsole* console = new MyConsole( 600, 100, 1, scheme );
331 		console->SetPos( 10, 300 );
332 		engine->Tree()->AddNode( 0, console );
333 
334 		push0->widgetPublish.AddListener( console );
335 		push1->widgetPublish.AddListener( console );
336 		text0->widgetPublish.AddListener( console );
337 		text1->widgetPublish.AddListener( console );
338 		toggle0->widgetPublish.AddListener( console );
339 		toggle1->widgetPublish.AddListener( console );
340 		listBox0->widgetPublish.AddListener( console );
341 		listBox1->widgetPublish.AddListener( console );
342 		if ( useIcons )
343 		{
344 			iconButton0->widgetPublish.AddListener( console );
345 			iconButton1->widgetPublish.AddListener( console );
346 			graphicButton->widgetPublish.AddListener( console );
347 		}
348 
349 		console->Print( "1. Left toggle buttons group, Right toggle buttons group." );
350 		console->Print( "2. Push buttons scroll both list boxes. Left underfilled, right overfilled." );
351 		console->Print( "3. Tab key should focus change between text fields." );
352 	}
353 
HandleWidgetEvent(KrWidget * source,const KrWidgetEvent & event)354 	bool HandleWidgetEvent(	KrWidget* source, const KrWidgetEvent& event )
355 	{
356 		if ( source == push0 && event.type == KrWidgetEvent::ACTIVATED ) {
357 			listBox0->MoveUpOne();
358 			listBox1->MoveUpOne();
359 		}
360 		else if ( source == push1 && event.type == KrWidgetEvent::ACTIVATED ) {
361 			listBox0->MoveDownOne();
362 			listBox1->MoveDownOne();
363 		}
364 		return true;
365 	}
366 
Tick()367 	void Tick()
368 	{
369 		unsigned int value = progressBar->GetValue();
370 		if ( value == progressBar->GetMaxValue() ) {
371 			value = 0;
372 		}
373 		else {
374 			value += 1;
375 		}
376 		progressBar->SetValue( value );
377 	}
378 
379 };
380 
381 
382 /*	A Kyra main loop, that displays all the widgets on a test screen. Note that the
383 	setup code for Kyra and SDL is skimmed over -- check out tutorial1 for a
384 	full explanation there.
385 */
main(int argc,char * argv[])386 int main( int argc, char *argv[] )
387 {
388 	const SDL_version* sdlVersion = SDL_Linked_Version();
389 	if ( sdlVersion->minor < 2 )
390 	{
391 		printf( "SDL version must be at least 1.2.0\n" );
392 		GLASSERT( 0 );
393 		exit( 254 );
394 	}
395 
396 	/* Initialize the SDL library */
397 	if ( SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_NOPARACHUTE) < 0 ) {
398 		GLOUTPUT(( "Couldn't initialize SDL: %s\n",SDL_GetError() ));
399 		exit(255);
400 	}
401 
402 	SDL_WM_SetCaption( "Kyra GuiTest", 0 );
403 
404 	int screenX = 800;
405 	int screenY = 600;
406 
407 	SDL_Surface* screen = SDL_SetVideoMode( screenX, screenY, 32, SDL_SWSURFACE );
408 	SDL_EnableUNICODE( true );
409 
410 	/*	The 'if (screen)' is a little strange. But some Kyra classes are created
411 		inside the 'if' case, and it's a way to make sure their destructors are
412 		called before "SDL_Quit()".
413 	*/
414 	if ( screen )
415 	{
416 		/*	The console font is compiled in. CONSOLEFONT.CPP contains a font bmp file
417 			in a big array. CreateFixedFontResource is a simple way to load in such
418 			a file. Fonts are so basic it's nice to not have to load them from a dat
419 			when developing and prototyping, although you certainly could.
420 		*/
421 		KrFontResource* consoleFontRes =  KrEncoder::CreateFixedFontResource( "CONSOLE", CONSOLEFONT_DAT, CONSOLEFONT_SIZE );
422 		KrEngine* engine = new KrEngine( screen );
423 
424 		GUI* gui = new GUI( engine, consoleFontRes );
425 
426 		/*	Now the main loop logic. Stay in an event loop, listening,
427 			and Draw() after every event. To make sure that the EventManager
428 			gets all the mouse and keyboard events, all events are sent. (You
429 			could be more selective, but it doesn't hurt to send everything.)
430 
431 			The KrEventManager is a singleton. It doesn't need to be created.
432 		*/
433 		engine->Draw();
434 
435 		SDL_Event event;
436 		SDL_SetTimer( TIMER_INTERVAL, TimerCallback );
437 
438 		while ( SDL_WaitEvent( &event ) )
439 		{
440 			if ( event.type == SDL_QUIT )
441 			{
442 				SDL_SetTimer( 0, 0 );
443 				break;
444 			}
445 
446 			if ( event.type == SDL_TIMER_EVENT )
447 			{
448 				// The progress bar is on a timer. Move it
449 				// every time pulse.
450 				gui->Tick();
451 			}
452 
453 			KrEventManager::Instance()->HandleEvent( event, engine );
454 			engine->Draw();
455 		}
456 
457 		/*	Clean up! The only thing of interest here is how to destroy the
458 			event manager. Note that if you call KrEventManager::Instance()
459 			after the delete KrEventManager::Instance(), this will re-create
460 			the event manager, which would be (presumably) a memory leak.
461 		*/
462 		delete gui;
463 		delete engine;
464 		engine = 0;
465 		delete consoleFontRes;
466 		consoleFontRes = 0;
467 		delete KrEventManager::Instance();
468 	}
469 	SDL_Quit();
470 
471 	return 0;
472 }
473 
474