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 
33 #ifndef KYRA_WIDGET_INCLUDED
34 #define KYRA_WIDGET_INCLUDED
35 
36 #ifdef _MSC_VER
37 #pragma warning( disable : 4530 )
38 #pragma warning( disable : 4786 )
39 #endif
40 
41 #include <string>
42 #include "SDL.h"
43 #include "../util/gllist.h"
44 #include "../../grinliz/glpublisher.h"
45 #include "../engine/engine.h"
46 #include "../engine/color.h"
47 
48 
49 /** An "activated" event means a switch has turned on, a button has been depressed, etc.
50 */
51 struct KrWidgetEventActivated
52 {
53 	int type;
54 };
55 
56 /** A "Deactivated" event occurs when something that was activated is no longer.
57 */
58 struct KrWidgetEventDeactivated
59 {
60 	int type;
61 };
62 
63 /** A "Command" event has a 'command' and 'argument' string. Usually generated
64 	by the console or a similar text based widget that recognizes a set of commands.
65 	When a string is split, the first word is the "command" and all that follows is
66 	the "arg". If the widget recognized the command - it is in the "command list" -
67 	recognized will be set to "true".
68 */
69 struct KrWidgetEventCommand
70 {
71 	int type;
72 	bool recognized;
73 	const char* command;
74 	const char* arg;
75 };
76 
77 /** A "Selection" event is a choice from a set of items. A list box selection for
78 	example. It has an 'index' which is a numerical id of the selection, and a 'text'
79 	which is the string value. Some widgets don't have text selection, in which case
80 	'text' will be null. An ImageListBox will have text==0 for its SELECTION events.
81 */
82 struct KrWidgetEventSelection
83 {
84 	int type;
85 	int index;
86 	const char* text;
87 };
88 
89 union KrWidgetEvent
90 {
91 	enum
92 	{
93 		NO_EVENT,
94 		ACTIVATED,
95 		DEACTIVATED,
96 		COMMAND,
97 		SELECTION,
98 	};
99 
100 	int type;
101 	KrWidgetEventActivated		activated;		// ACTIVATED
102 	KrWidgetEventDeactivated	deactivated;	// DEACTIVATED
103 	KrWidgetEventCommand		command;		// COMMAND
104 	KrWidgetEventSelection		selection;		// SELECTION
105 };
106 
107 
108 /** An abstract class, the HandleWidgetEvent when a widget
109 	listener receives an event. The events widgets publish
110 	are documented on a per-widget basis.
111 
112 	The class is abstract, so it can be used as a mix in and
113 	there are no multiple inheritance issues.
114 
115 	The HandleWidgetEvent is a strange method with a lot of
116 	paremeters. An obvious solution would be to send a WidgetEvent
117 	class that is abstract and polymorphic...but Kyra doesn't use/require
118 	dynamic cast, so implementing that is less useful than it
119 	seems.
120 */
121 
122 class IKrWidgetListener : public grinliz::Listener< IKrWidgetListener >
123 {
124   public:
125 
126 
127 	virtual bool HandleWidgetEvent(	KrWidget* source, const KrWidgetEvent& event ) = 0;
128 };
129 
130 
131 /**	A scheme defines a color system for widgets. Each widget
132 	can have it's own scheme, or an entire widget set can
133 	share the same one. (Or at least a copy of the same one.)
134 
135 	A scheme consists of a font (some widgets allow this to
136 	be null), a primary color, and a secondary color that
137 	should visually match the primary color, and a cursor
138 	color. All other colors	used are calculated from the
139 	primary and secondary.
140 */
141 struct KrScheme
142 {
143 	KrScheme( KrFontResource* font );
144 
145 	KrRGBA CalcBrightLine() const;
146 	KrRGBA CalcShadowLine() const;
147 
148 	KrColorTransform CalcHiPrimary() const;
149 	KrColorTransform CalcHiSec() const;
150 	KrColorTransform CalcDark() const;
151 	KrColorTransform CalcDarkSec() const;
152 
153 	KrRGBA				primary;
154 	KrRGBA				cursor;
155 	KrColorTransform	secondary;
156 	KrFontResource*		font;
157 
158 	enum
159 	{
160 		BRIGHT = 60,
161 		DARK   = 60
162 	};
163 };
164 
165 
166 struct KrBevelElement
167 {
168   public:
169 	KrBevelElement( int w, int h, const KrScheme& );
170 	~KrBevelElement();
171 
172 	void AddToTree( KrEngine*, KrImNode* parent );
173 	void DrawIn();
174 	void DrawOut();
175 
176 	int width;
177 	int height;
178 	KrBoxResource *vertDR, *vertLR, *horDR, *horLR;
179 	KrBox *vertD, *vertL, *horD, *horL;
180 };
181 
182 
183 /**	This is the base of any Kyra widget. A widget is much like any
184 	other Kyra image object. You new it, add it to the KrImageTree,
185 	and it is drawn as part of the Draw() pass.
186 
187 	Widgets don't have resources, but do use schemes which are similar.
188 
189 	Widgets get their events from an event manager. If you use widgets,
190 	you must therefore send events to the KrEventManager class. Widgets
191 	generally need SDL_EnableUNICODE( true ) to function correctly.
192 
193 	Widgets broadcast their events to "listeners." To register a class
194 	as a listener, call AddListener(). The class will then receive
195 	notification of events. The event each widget broadcast is documented
196 	on a per-widget basis.
197 
198 	A widget subclass can implement any of the following properties:
199 
200 	- MouseListeners receieve and can respond to mouse events.
201 	- KeyListeners get keyboard focus and key messages. They grab
202 	  key focus with the "tab" key or if clicked on.
203 	- Selectable widgets have state. They can be on/off, up/down, or whatever.
204 	  A KrToggleButton is a selectable widget.
205 	- Groupable widgets are in a group, and are aware of other widgets
206 	  in that group. Radio Buttons are groupable.
207 	- Some widgets can respond to accelerator keys. Use SetAccelerator
208 	  to specify a hotkey to pass to the widget.
209 
210 	Widgets can be nested and you can query for parents. However, Kyra regards
211 	the widget model as flat and doesn't pay attention to the nesting, with
212 	one important exception: a key event that isn't handled by a widget
213 	will be passed through its parent chain. For example, a KrConsole
214 	uses  a KrTextWidget. If the KrTextWidget doesn't handle a
215 	particular key, it will get passed up to the KrConsole.
216 
217 	WARNING: You may want to use Widgets in window 0, if you are using multiple Kyra
218 	windows. They are not fully tested in higher window numbers. You may see placement
219 	or bounding errors in higher window numbers.
220 
221 	Notes for implementing your own widgets:
222 
223 	- All Widgets are of the type ToWidget(). To get a particular flavor,
224 	  the ToExtended() method can be used.
225 	- Widgets should initialize themselves in the AddedtoTree() method. When
226 	  the engine calls your AddedtoTree() method, you can add children because
227 	  you are already in the Tree(). Be sure to call the parent
228 	  KrWidget::AddedtoTree() as well!
229 	- If you have been added to the Tree(), your Engine() pointer will be non-null.
230 */
231 class KrWidget : public KrImNode, public IKrWidgetListener
232 {
233   public:
234 	virtual ~KrWidget();
ToWidget()235 	virtual KrWidget* ToWidget()	{ return this; }
236 
237 	/** All widgets publish events through this member.
238 	*/
239 	grinliz::Publisher< IKrWidgetListener > widgetPublish;
240 
241 	enum {
242 		LEFT_MOUSE   = 1,
243 		RIGHT_MOUSE  = 2,	// not currently supported
244 		MIDDLE_MOUSE = 4,	// not currently supported
245 
246 		LEFT_UP   = 0,
247 		LEFT_DOWN = 1,
248 		RIGHT_UP   = 2,		// not currently supported
249 		RIGHT_DOWN = 3,		// not currently supported
250 		MIDDLE_UP  = 4,		// not currently supported
251 		MIDDLE_DOWN = 5		// not currently supported
252 	};
253 
WidgetType()254 	virtual const char* WidgetType() { return "Widget"; }
255 
256 	/**	IsMouseListener returns whether this is a mouse listener or not, and which buttons
257 		are listened to. A return value of 0 is no listening. Else
258 		it can return an OR mask of the buttons ( LEFT_MOUSE, RIGHT_MOUSE,
259 		MIDDLE_MOUSE ) it wants to listen for mouse clicks.
260 
261 		The simple case is to only listen to the left mouse, in which case
262 		return LEFT_MOUSE (1). The click messages can then be treated like
263 		a boolean for the left mouse (1 is down, 0 is up.)
264 
265 		Currently, with version 2.0, only the LEFT_MOUSE is supported.
266 
267 
268 		MouseIn is called when a mouse moves in to the widget. The 'down' parameter
269 		reflects the state of the left mouse button. The 'in' reflects whether
270 		it is moving to the widget (true) or away from the widget (false).
271 
272 
273 		MouseMove reports when the mouse moves over this widget.
274 
275 
276 		MouseClick is called when the mouse is clicked on this widget.
277 		The 'click' param will have a single value (not OR mask) of
278 		LEFT_UP, LEFT_DOWN, RIGHT_UP, etc. with x and y coordinates
279 		of the action.
280 
281 		In the simple case that you are only listening to the left mouse,
282 		the parameter will be essentially a boolean: 1 for the left mouse
283 		down, 0 for the left mouse up.
284 	*/
IsMouseListener()285 	virtual int  IsMouseListener()						{ return 0; }
MouseIn(bool down,bool in)286 	virtual void MouseIn( bool down, bool in )			{}					///< @sa IsMouseListener
MouseMove(bool down,int x,int y)287 	virtual void MouseMove( bool down, int x, int y )	{}					///< @sa IsMouseListener
MouseClick(int click,int x,int y)288 	virtual bool MouseClick( int click, int x, int y )	{ return false; }	///< @sa IsMouseListener
289 
IsKeyListener()290 	virtual bool IsKeyListener()						{ return false; }
KeyFocus(bool focus)291 	virtual void KeyFocus( bool focus )					{}
KeyEvent(const SDL_Event & key)292 	virtual bool KeyEvent( const SDL_Event& key )		{ return false; }
293 
IsSelectable()294 	virtual bool IsSelectable()							{ return false; }
Selected(bool selected)295 	virtual void Selected( bool selected )				{}
296 
297 //	virtual bool IsGroup()								{ return false; }
298 
Accelerate(bool down)299 	virtual void Accelerate( bool down )				{}
300 	void SetAccelerator( int keymod, int keysym );
301 
302 	/// Handle widget events, return true if handled, false if not ours
HandleWidgetEvent(KrWidget * source,const KrWidgetEvent & event)303 	virtual bool HandleWidgetEvent(	KrWidget* source, const KrWidgetEvent& event )	{ return false; }
304 
305 	/// Find the parent of the widget that is also a widget.
306 	KrWidget* ParentWidget();
307 
308   protected:
309 	KrWidget( const KrScheme& scheme );
310 
311 	int groupId;
312 	KrScheme scheme;
313 };
314 
315 #endif
316