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