xref: /haiku/src/tests/servers/app/look_and_feel/LookAndFeel.cpp (revision 17889a8c70dbb3d59c1412f6431968753c767bab)
1 /*
2  * Copyright 2005-2009, Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Axel Dörfler, axeld@pinc-software.de
7  */
8 
9 
10 #include <Alert.h>
11 #include <Application.h>
12 #include <Button.h>
13 #include <MenuField.h>
14 #include <MenuItem.h>
15 #include <PopUpMenu.h>
16 #include <String.h>
17 #include <Window.h>
18 
19 #include <WindowPrivate.h>
20 
21 #include <stdio.h>
22 #include <string.h>
23 
24 
25 const uint32 kMsgUpdateLook = 'uplk';
26 const uint32 kMsgUpdateFeel = 'upfe';
27 const uint32 kMsgUpdateFlags = 'upfl';
28 
29 const uint32 kMsgAddWindow = 'adwn';
30 const uint32 kMsgAddSubsetWindow = 'adsw';
31 
32 
33 int32 gNormalWindowCount = 0;
34 
35 
36 class Window : public BWindow {
37 	public:
38 		Window(BRect frame, window_look look, window_feel feel);
39 		virtual ~Window();
40 
41 		virtual void MessageReceived(BMessage* message);
42 		virtual bool QuitRequested();
43 
44 	private:
45 		BMessage* AddWindowMessage(window_look look, window_feel feel);
46 		BString TitleForFeel(window_feel feel);
47 		void _UpdateFlagsMenuLabel();
48 
49 		BMenuField*	fFlagsField;
50 };
51 
52 
53 Window::Window(BRect frame, window_look look, window_feel feel)
54 	: BWindow(frame, TitleForFeel(feel).String(), look, feel,
55 			B_ASYNCHRONOUS_CONTROLS)
56 {
57 	BRect rect(Bounds());
58 	BView *view = new BView(rect, NULL, B_FOLLOW_ALL, B_WILL_DRAW);
59 	view->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
60 	AddChild(view);
61 
62 	if (!IsModal() && !IsFloating())
63 		gNormalWindowCount++;
64 
65 	float labelWidth = view->StringWidth("Flags:") + 5.f;
66 
67 	BPopUpMenu* menu = new BPopUpMenu("looks");
68 	const struct { const char* name; int32 look; } looks[] = {
69 		{"Titled", B_TITLED_WINDOW_LOOK}, {"Document", B_DOCUMENT_WINDOW_LOOK},
70 		{"Floating", B_FLOATING_WINDOW_LOOK}, {"Modal", B_MODAL_WINDOW_LOOK},
71 		{"Bordered", B_BORDERED_WINDOW_LOOK}, {"No Border", B_NO_BORDER_WINDOW_LOOK},
72 		{"Left Titled", kLeftTitledWindowLook}, {"Desktop", kDesktopWindowLook}
73 	};
74 	for (uint32 i = 0; i < sizeof(looks) / sizeof(looks[0]); i++) {
75 		BMessage* message = new BMessage(kMsgUpdateLook);
76 		message->AddInt32("look", looks[i].look);
77 		BMenuItem* item = new BMenuItem(looks[i].name, message);
78 		if (looks[i].look == (int32)Look())
79 			item->SetMarked(true);
80 
81 		menu->AddItem(item);
82 	}
83 
84 	rect.InsetBy(10, 10);
85 	BMenuField* menuField = new BMenuField(rect, "look", "Look:", menu);
86 	menuField->ResizeToPreferred();
87 	menuField->SetDivider(labelWidth);
88 	view->AddChild(menuField);
89 
90 	menu = new BPopUpMenu("feels");
91 	const struct { const char* name; int32 feel; } feels[] = {
92 		{"Normal", B_NORMAL_WINDOW_FEEL},
93 		{"Modal Subset", B_MODAL_SUBSET_WINDOW_FEEL},
94 		{"App Modal", B_MODAL_APP_WINDOW_FEEL},
95 		{"All Modal", B_MODAL_ALL_WINDOW_FEEL},
96 		{"Floating Subset", B_FLOATING_SUBSET_WINDOW_FEEL},
97 		{"App Floating", B_FLOATING_APP_WINDOW_FEEL},
98 		{"All Floating", B_FLOATING_ALL_WINDOW_FEEL},
99 		{"Menu", kMenuWindowFeel},
100 		{"WindowScreen", kWindowScreenFeel},
101 		{"Desktop", kDesktopWindowFeel},
102 	};
103 	for (uint32 i = 0; i < sizeof(feels) / sizeof(feels[0]); i++) {
104 		BMessage* message = new BMessage(kMsgUpdateFeel);
105 		message->AddInt32("feel", feels[i].feel);
106 		BMenuItem* item = new BMenuItem(feels[i].name, message);
107 		if (feels[i].feel == (int32)Feel())
108 			item->SetMarked(true);
109 
110 		menu->AddItem(item);
111 	}
112 
113 	rect.OffsetBy(0, menuField->Bounds().Height() + 10);
114 	menuField = new BMenuField(rect, "feel", "Feel:", menu);
115 	menuField->ResizeToPreferred();
116 	menuField->SetDivider(labelWidth);
117 	view->AddChild(menuField);
118 
119 	menu = new BPopUpMenu("none", false, false);
120 	const struct { const char* name; uint32 flag; } flags[] = {
121 		{"Not Zoomable", B_NOT_ZOOMABLE},
122 		{"Not Closable", B_NOT_CLOSABLE},
123 		{"Not Movable", B_NOT_MOVABLE},
124 		{"Not Horizontally Resizable", B_NOT_H_RESIZABLE},
125 		{"Not Vertically Resizable", B_NOT_V_RESIZABLE},
126 		{"Outline Resize", B_OUTLINE_RESIZE},
127 		{"Accept First Click", B_WILL_ACCEPT_FIRST_CLICK},
128 		{"Not Anchored On Activate", B_NOT_ANCHORED_ON_ACTIVATE},
129 		{"Avoid Front", B_AVOID_FRONT},
130 #if defined(__HAIKU__) || defined(HAIKU_TARGET_PLATFORM_LIBBE_TEST)
131 		{"Same Position In All Workspaces", B_SAME_POSITION_IN_ALL_WORKSPACES},
132 #endif
133 	};
134 	for (uint32 i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
135 		BMessage* message = new BMessage(kMsgUpdateFlags);
136 		message->AddInt32("flag", flags[i].flag);
137 		BMenuItem* item = new BMenuItem(flags[i].name, message);
138 
139 		menu->AddItem(item);
140 	}
141 
142 	rect.OffsetBy(0, menuField->Bounds().Height() + 10);
143 	fFlagsField = new BMenuField(rect, "flags", "Flags:", menu);
144 	fFlagsField->ResizeToPreferred();
145 	fFlagsField->SetDivider(labelWidth);
146 	view->AddChild(fFlagsField);
147 
148 	// normal
149 
150 	rect.OffsetBy(0, menuField->Bounds().Height() + 10);
151 	BButton* button = new BButton(rect, "normal", "Add Normal Window",
152 		AddWindowMessage(B_TITLED_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL),
153 		B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP,
154 		B_WILL_DRAW | B_NAVIGABLE | B_FULL_UPDATE_ON_RESIZE);
155 	float width, height;
156 	button->GetPreferredSize(&width, &height);
157 	button->ResizeTo(rect.Width(), height);
158 	view->AddChild(button);
159 
160 	// modal
161 
162 	rect = button->Frame();
163 	rect.OffsetBy(0, rect.Height() + 5);
164 	button = new BButton(rect, "modal_subset", "Add Modal Subset",
165 		AddWindowMessage(B_MODAL_WINDOW_LOOK, B_MODAL_SUBSET_WINDOW_FEEL),
166 		B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP,
167 		B_WILL_DRAW | B_NAVIGABLE | B_FULL_UPDATE_ON_RESIZE);
168 	view->AddChild(button);
169 
170 	rect.OffsetBy(0, rect.Height() + 5);
171 	button = new BButton(rect, "app_modal", "Add Application Modal",
172 		AddWindowMessage(B_MODAL_WINDOW_LOOK, B_MODAL_APP_WINDOW_FEEL),
173 		B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP,
174 		B_WILL_DRAW | B_NAVIGABLE | B_FULL_UPDATE_ON_RESIZE);
175 	view->AddChild(button);
176 
177 	rect.OffsetBy(0, rect.Height() + 5);
178 	button = new BButton(rect, "all_modal", "Add All Modal",
179 		AddWindowMessage(B_MODAL_WINDOW_LOOK, B_MODAL_ALL_WINDOW_FEEL),
180 		B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP,
181 		B_WILL_DRAW | B_NAVIGABLE | B_FULL_UPDATE_ON_RESIZE);
182 	view->AddChild(button);
183 
184 	// floating
185 
186 	rect = button->Frame();
187 	rect.OffsetBy(0, rect.Height() + 5);
188 	button = new BButton(rect, "floating_subset", "Add Floating Subset",
189 		AddWindowMessage(B_FLOATING_WINDOW_LOOK, B_FLOATING_SUBSET_WINDOW_FEEL),
190 		B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP,
191 		B_WILL_DRAW | B_NAVIGABLE | B_FULL_UPDATE_ON_RESIZE);
192 	view->AddChild(button);
193 
194 	rect.OffsetBy(0, rect.Height() + 5);
195 	button = new BButton(rect, "app_floating", "Add Application Floating",
196 		AddWindowMessage(B_FLOATING_WINDOW_LOOK, B_FLOATING_APP_WINDOW_FEEL),
197 		B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP,
198 		B_WILL_DRAW | B_NAVIGABLE | B_FULL_UPDATE_ON_RESIZE);
199 	view->AddChild(button);
200 
201 	rect.OffsetBy(0, rect.Height() + 5);
202 	button = new BButton(rect, "all_floating", "Add All Floating",
203 		AddWindowMessage(B_FLOATING_WINDOW_LOOK, B_FLOATING_ALL_WINDOW_FEEL),
204 		B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP,
205 		B_WILL_DRAW | B_NAVIGABLE | B_FULL_UPDATE_ON_RESIZE);
206 	view->AddChild(button);
207 
208 	// close
209 
210 	rect.OffsetBy(0, rect.Height() + 15);
211 	button = new BButton(rect, "close", "Close Window",
212 		new BMessage(B_QUIT_REQUESTED), B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP,
213 		B_WILL_DRAW | B_NAVIGABLE | B_FULL_UPDATE_ON_RESIZE);
214 	button->ResizeToPreferred();
215 	button->MoveTo((rect.Width() - button->Bounds().Width()) / 2, rect.top);
216 	view->AddChild(button);
217 
218 	ResizeTo(Bounds().Width(), button->Frame().bottom + 10);
219 }
220 
221 
222 Window::~Window()
223 {
224 }
225 
226 
227 void
228 Window::MessageReceived(BMessage* message)
229 {
230 	switch (message->what) {
231 		case kMsgUpdateLook:
232 		{
233 			int32 look;
234 			if (message->FindInt32("look", &look) != B_OK)
235 				break;
236 
237 			SetLook((window_look)look);
238 			break;
239 		}
240 
241 		case kMsgUpdateFeel:
242 		{
243 			int32 feel;
244 			if (message->FindInt32("feel", &feel) != B_OK)
245 				break;
246 
247 			if (!IsModal() && !IsFloating())
248 				gNormalWindowCount--;
249 
250 			SetFeel((window_feel)feel);
251 			SetTitle(TitleForFeel((window_feel)feel).String());
252 
253 			if (!IsModal() && !IsFloating())
254 				gNormalWindowCount++;
255 			break;
256 		}
257 
258 		case kMsgUpdateFlags:
259 		{
260 			uint32 flag;
261 			if (message->FindInt32("flag", (int32*)&flag) != B_OK)
262 				break;
263 
264 			BMenuItem* item;
265 			if (message->FindPointer("source", (void**)&item) != B_OK)
266 				break;
267 
268 			item->SetMarked(!item->IsMarked());
269 
270 			uint32 flags = Flags();
271 			if (item->IsMarked())
272 				flags |= flag;
273 			else
274 				flags &= ~flag;
275 
276 			SetFlags(flags);
277 			_UpdateFlagsMenuLabel();
278 			break;
279 		}
280 
281 		case kMsgAddWindow:
282 		case kMsgAddSubsetWindow:
283 		{
284 			int32 look, feel;
285 			if (message->FindInt32("look", &look) != B_OK
286 				|| message->FindInt32("feel", &feel) != B_OK)
287 				break;
288 
289 			BWindow* window = new Window(Frame().OffsetByCopy(20, 20),
290 				(window_look)look, (window_feel)feel);
291 
292 			if (message->what == kMsgAddSubsetWindow) {
293 				status_t status = window->AddToSubset(this);
294 				if (status != B_OK) {
295 					char text[512];
296 					snprintf(text, sizeof(text),
297 						"Window could not be added to subset:\n\n\%s", strerror(status));
298 					(new BAlert("Alert", text, "OK"))->Go(NULL);
299 
300 					delete window;
301 					break;
302 				}
303 			}
304 
305 			window->Show();
306 			break;
307 		}
308 
309 		default:
310 			BWindow::MessageReceived(message);
311 	}
312 }
313 
314 
315 bool
316 Window::QuitRequested()
317 {
318 	if (!IsModal() && !IsFloating())
319 		gNormalWindowCount--;
320 
321 	if (gNormalWindowCount < 1)
322 		be_app->PostMessage(B_QUIT_REQUESTED);
323 
324 	return true;
325 }
326 
327 
328 BMessage*
329 Window::AddWindowMessage(window_look look, window_feel feel)
330 {
331 	BMessage* message = new BMessage(kMsgAddWindow);
332 
333 	if (feel == B_FLOATING_SUBSET_WINDOW_FEEL
334 		|| feel == B_MODAL_SUBSET_WINDOW_FEEL)
335 		message->what = kMsgAddSubsetWindow;
336 
337 	message->AddInt32("look", look);
338 	message->AddInt32("feel", feel);
339 
340 	return message;
341 }
342 
343 
344 BString
345 Window::TitleForFeel(window_feel feel)
346 {
347 	BString title = "Look&Feel - ";
348 
349 	switch ((uint32)feel) {
350 		case B_NORMAL_WINDOW_FEEL:
351 			title += "Normal";
352 			break;
353 
354 		// modal feels
355 
356 		case B_MODAL_SUBSET_WINDOW_FEEL:
357 			title += "Modal Subset";
358 			break;
359 		case B_MODAL_APP_WINDOW_FEEL:
360 			title += "Application Modal";
361 			break;
362 		case B_MODAL_ALL_WINDOW_FEEL:
363 			title += "All Modal";
364 			break;
365 
366 		// floating feels
367 
368 		case B_FLOATING_SUBSET_WINDOW_FEEL:
369 			title += "Floating Subset";
370 			break;
371 		case B_FLOATING_APP_WINDOW_FEEL:
372 			title += "Application Floating";
373 			break;
374 		case B_FLOATING_ALL_WINDOW_FEEL:
375 			title += "All Floating";
376 			break;
377 
378 		// special/private feels
379 
380 		case kMenuWindowFeel:
381 			title += "Menu";
382 			break;
383 		case kWindowScreenFeel:
384 			title += "WindowScreen";
385 			break;
386 		case kDesktopWindowFeel:
387 			title += "Desktop";
388 			break;
389 	}
390 
391 	return title;
392 }
393 
394 
395 void
396 Window::_UpdateFlagsMenuLabel()
397 {
398 	BString label;
399 	BMenu* menu = fFlagsField->Menu();
400 
401 	for (int32 i = 0; i < menu->CountItems(); i++) {
402 		BMenuItem* item = menu->ItemAt(i);
403 
404 		if (item->IsMarked()) {
405 			if (label != "")
406 				label += " + ";
407 			label += item->Label();
408 		}
409 	}
410 
411 	if (label == "")
412 		label = "none";
413 
414 	menu->Superitem()->SetLabel(label.String());
415 }
416 
417 
418 //	#pragma mark -
419 
420 
421 class Application : public BApplication {
422 	public:
423 		Application();
424 
425 		virtual void ReadyToRun();
426 };
427 
428 
429 Application::Application()
430 	: BApplication("application/x-vnd.haiku-looknfeel")
431 {
432 }
433 
434 
435 void
436 Application::ReadyToRun()
437 {
438 	Window *window = new Window(BRect(100, 100, 400, 420),
439 		B_TITLED_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL);
440 	window->Show();
441 }
442 
443 
444 //	#pragma mark -
445 
446 
447 int
448 main(int argc, char **argv)
449 {
450 	Application app;// app;
451 
452 	app.Run();
453 	return 0;
454 }
455