⛏️ index : haiku.git


#include <stdio.h>
#include <stdlib.h>

#include <typeinfo>

#include <Application.h>
#include <Button.h>
#include <CardLayout.h>
#include <LayoutBuilder.h>
#include <LayoutUtils.h>
#include <ListView.h>
#include <MenuField.h>
#include <RadioButton.h>
#include <ScrollView.h>
#include <String.h>
#include <StringView.h>
#include <TextControl.h>
#include <View.h>
#include <Window.h>


static const rgb_color kBlack	= {0, 0, 0, 255};
static const rgb_color kRed		= {255, 0, 0, 255};

// message what codes
enum {
	MSG_TEST_SELECTED		= 'tsts',

	// used in tests
	MSG_TOGGLE_1			= 'tgl1',
	MSG_TOGGLE_2			= 'tgl2',

	MSG_FIXED_ASPECT_RATIO	= 'hwas',
	MSG_FIXED_SUM			= 'hwfs',
	MSG_FIXED_PRODUCT		= 'hwfp',
};

// HeightForWidthTestView types
enum {
	FIXED_SUM,
	FIXED_PRODUCT,
	FIXED_ASPECT_RATIO,
};


// TestView
class TestView : public BView {
public:
	TestView(const rgb_color& color = kBlack)
		: BView("test view", B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
		  fColor(color)
	{
	}

	void SetColor(const rgb_color& color)
	{
		fColor = color;
		Invalidate();
	}

	virtual void Draw(BRect updateRect)
	{
		SetHighColor(fColor);

		BRect bounds(Bounds());
		StrokeRect(bounds);

		BPoint rightBottom = bounds.RightBottom();
		StrokeLine(B_ORIGIN, rightBottom);
		StrokeLine(BPoint(rightBottom.x, 0), BPoint(0, rightBottom.y));
	}

	virtual BSize MinSize()
	{
		return BLayoutUtils::ComposeSize(ExplicitMinSize(), BSize(10, 10));
	}

	virtual BSize PreferredSize()
	{
		return BLayoutUtils::ComposeSize(ExplicitPreferredSize(),
			BSize(50, 50));
	}

private:
	rgb_color	fColor;
};


// HeightForWidthTestView
class HeightForWidthTestView : public TestView {
public:
	HeightForWidthTestView(uint32 type, float value)
		: fType(NULL)
	{
		SetType(type, value);
	}

	HeightForWidthTestView(const rgb_color& color, uint32 type, float value)
		: TestView(color),
		  fType(NULL)
	{
		SetType(type, value);
	}

	~HeightForWidthTestView()
	{
		delete fType;
	}

	void SetType(uint32 type, float value)
	{
		delete fType;

		switch (type) {
			case FIXED_SUM:
				fType = new FixedSumType((int)value);
				break;
			case FIXED_PRODUCT:
				fType = new FixedProductType((int)value);
				break;
			case FIXED_ASPECT_RATIO:
			default:
				fType = new FixedAspectRatioType(value);
				break;
		}

		InvalidateLayout();
	}

	BSize MinSize() {
		return BLayoutUtils::ComposeSize(ExplicitMinSize(), fType->MinSize());
	}

	BSize MaxSize() {
		return BLayoutUtils::ComposeSize(ExplicitMaxSize(), fType->MaxSize());
	}

	BSize PreferredSize() {
		return BLayoutUtils::ComposeSize(ExplicitPreferredSize(),
			fType->PreferredSize());
	}

	bool HasHeightForWidth() {
		return true;
	}

	void GetHeightForWidth(float width, float* minHeight, float* maxHeight,
			float* preferredHeight) {
		float dummy;
		fType->GetHeightForWidth(width,
			minHeight ? minHeight : &dummy,
			maxHeight ? maxHeight : &dummy,
			preferredHeight ? preferredHeight : &dummy);
	}

private:
	class HeightForWidthType {
	public:
		virtual ~HeightForWidthType()
		{
		}

		virtual BSize MinSize() = 0;
		virtual BSize MaxSize() = 0;
		virtual BSize PreferredSize() = 0;
		virtual void GetHeightForWidth(float width, float* minHeight,
			float* maxHeight, float* preferredHeight) = 0;
	};

	class FixedAspectRatioType : public HeightForWidthType {
	public:
		FixedAspectRatioType(float ratio)
			: fAspectRatio(ratio)
		{
		}
		
		virtual BSize MinSize()
		{
			return BSize(-1, -1);
		}

		virtual BSize MaxSize()
		{
			return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
		}

		virtual BSize PreferredSize()
		{
			float preferredWidth = 49;
			float dummy, preferredHeight;
			GetHeightForWidth(preferredWidth, &dummy, &dummy, &preferredHeight);
			return BSize(preferredWidth, preferredHeight);
		}

		virtual void GetHeightForWidth(float width, float* minHeight,
			float* maxHeight, float* preferredHeight)
		{
			float height = floor((width + 1) * fAspectRatio) - 1;
			*minHeight = height;
			*maxHeight = height;
			*preferredHeight = height;
		}

	private:
		float	fAspectRatio;
	};

	class FixedSumType : public HeightForWidthType {
	public:
		FixedSumType(float sum)
			: fSum(sum)
		{
		}

		virtual BSize MinSize()
		{
			return BSize(0, 0);
		}

		virtual BSize MaxSize()
		{
			return BSize(fSum - 2, fSum - 2);
		}

		virtual BSize PreferredSize()
		{
			float preferredWidth = floor(fSum / 2) - 1;
			float dummy, preferredHeight;
			GetHeightForWidth(preferredWidth, &dummy, &dummy, &preferredHeight);
			return BSize(preferredWidth, preferredHeight);
		}

		virtual void GetHeightForWidth(float width, float* minHeight,
			float* maxHeight, float* preferredHeight)
		{
			float height = fSum - (width + 1) - 1;
			*minHeight = height;
			*maxHeight = height;
			*preferredHeight = height;
		}

	private:
		float	fSum;
	};

	class FixedProductType : public HeightForWidthType {
	public:
		FixedProductType(float product)
			: fProduct(product)
		{
		}

		virtual BSize MinSize()
		{
			return BSize(0, 0);
		}

		virtual BSize MaxSize()
		{
			return BSize(fProduct - 1, fProduct - 1);
		}

		virtual BSize PreferredSize()
		{
			float preferredWidth = floor(sqrt(fProduct));
			float dummy, preferredHeight;
			GetHeightForWidth(preferredWidth, &dummy, &dummy, &preferredHeight);
			return BSize(preferredWidth, preferredHeight);
		}

		virtual void GetHeightForWidth(float width, float* minHeight,
			float* maxHeight, float* preferredHeight)
		{
			float height = floor(fProduct / (width + 1)) - 1;
			*minHeight = height;
			*maxHeight = height;
			*preferredHeight = height;
		}

	private:
		float	fProduct;
	};

private:
	HeightForWidthType*	fType;
};


// Test
struct Test : BHandler {
	BString	name;
	BView*	rootView;
	BString	description;

	Test(const char* name, const char* description)
		: BHandler(name),
		  name(name),
		  rootView(new BView(name, 0)),
		  description(description)
	{
	}

	virtual ~Test()
	{
	}

	virtual void RegisterListeners()
	{
	}
};


// GroupLayoutTest1
struct GroupLayoutTest1 : public Test {
	GroupLayoutTest1()
		:
		Test("Group", "Simple BGroupLayout.")
	{
		 BLayoutBuilder::Group<>(rootView, B_HORIZONTAL)
			// controls
			.AddGroup(B_VERTICAL)
				.Add(toggleRowButton = new BButton("Toggle Row",
						new BMessage(MSG_TOGGLE_1)))
				.Add(toggleViewButton = new BButton("Toggle View",
						new BMessage(MSG_TOGGLE_2)))
				.AddGlue()
				.End()

			// test views
			.AddGroup(B_VERTICAL)
				// row 1
				.AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING, 1)
					.Add(new TestView(), 1)
					.Add(toggledView = new TestView(), 2)
					.Add(new TestView(), 3)
					.End()

				// row 2
				.AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING, 2)
					.GetLayout(&toggledRow)
					.Add(new TestView())
					.Add(new TestView())
					.Add(new TestView())
					.End()

				// row 3
				.AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING, 3)
					.Add(new TestView(), 3)
					.Add(new TestView(), 2)
					.Add(new TestView(), 1);
	}

	virtual void RegisterListeners()
	{
		toggleRowButton->SetTarget(this);
		toggleViewButton->SetTarget(this);
	}

	virtual void MessageReceived(BMessage* message)
	{
		switch (message->what) {
			case MSG_TOGGLE_1:
			{
				toggledRow->SetVisible(!toggledRow->IsVisible());
				break;
			}

			case MSG_TOGGLE_2:
			{
				if (toggledView->IsHidden(toggledView))
					toggledView->Show();
				else
					toggledView->Hide();
				break;
			}

			default:
				BHandler::MessageReceived(message);
				break;
		}
	}

private:
	BButton*	toggleRowButton;
	BButton*	toggleViewButton;
	BGroupLayout* toggledRow;
	TestView*	toggledView;
};


// GroupAlignedLayoutTest1
struct GroupAlignedLayoutTest1 : public Test {
	GroupAlignedLayoutTest1()
		: Test("Group aligned", "Simple BGroupLayout, rows 1 and 3 aligned.")
	{
		BGroupView* rootView  = new BGroupView(B_HORIZONTAL, 10);
		this->rootView = rootView;

		// controls

		BGroupView* controls = new BGroupView(B_VERTICAL, 10);
		rootView->AddChild(controls);
		
		toggleRowButton = new BButton("Toggle Row", new BMessage(MSG_TOGGLE_1));
		controls->AddChild(toggleRowButton);
		
		toggleViewButton = new BButton("Toggle View",
			new BMessage(MSG_TOGGLE_2));
		controls->AddChild(toggleViewButton);


		controls->AddChild(BSpaceLayoutItem::CreateGlue());
		
		// test views
		
		BGroupView* testViews = new BGroupView(B_VERTICAL, 10);
		rootView->AddChild(testViews);

		// row 1
		BGroupView* row = new BGroupView(B_HORIZONTAL, 10);
		BGroupView* row1 = row;
		testViews->GroupLayout()->AddView(row, 1);

		row->GroupLayout()->AddView(new TestView(), 1);
		toggledView = new TestView();
		row->GroupLayout()->AddView(toggledView, 2);
		row->GroupLayout()->AddView(new TestView(), 3);

		// row 2
		row = new BGroupView(B_HORIZONTAL, 10);
		toggledRow = row;
		testViews->GroupLayout()->AddView(row, 2);

		row->GroupLayout()->AddView(new TestView());
		row->GroupLayout()->AddView(new TestView());
		row->GroupLayout()->AddView(new TestView());
		
		// row 3
		row = new BGroupView(B_HORIZONTAL, 10);
		BGroupView* row3 = row;
		testViews->GroupLayout()->AddView(row, 3);

		row->GroupLayout()->AddView(new TestView(), 3);
		row->GroupLayout()->AddView(new TestView(), 2);
		row->GroupLayout()->AddView(new TestView(), 1);

		// align rows 1 and 3
		row1->GroupLayout()->AlignLayoutWith(row3->GroupLayout(), B_HORIZONTAL);
	}

	virtual void RegisterListeners()
	{
		toggleRowButton->SetTarget(this);
		toggleViewButton->SetTarget(this);
	}

	virtual void MessageReceived(BMessage* message)
	{
		switch (message->what) {
			case MSG_TOGGLE_1:
			{
				if (toggledRow->IsHidden(toggledRow))
					toggledRow->Show();
				else
					toggledRow->Hide();
				break;
			}

			case MSG_TOGGLE_2:
			{
				if (toggledView->IsHidden(toggledView))
					toggledView->Show();
				else
					toggledView->Hide();
				break;
			}

			default:
				BHandler::MessageReceived(message);
				break;
		}
	}

private:
	BButton*	toggleRowButton;
	BButton*	toggleViewButton;
	BGroupView*	toggledRow;
	TestView*	toggledView;
};


// GridLayoutTest1
struct GridLayoutTest1 : public Test {
	GridLayoutTest1()
		: Test("Grid", "Simple BGridLayout.")
	{
		BLayoutBuilder::Group<>(rootView, B_HORIZONTAL)
			// controls
			.AddGroup(B_VERTICAL)
				.Add(toggleView1Button = new BButton("Toggle View 1",
						new BMessage(MSG_TOGGLE_1)))
				.Add(toggleView2Button = new BButton("Toggle View 2",
						new BMessage(MSG_TOGGLE_2)))
				.AddGlue()
				.End()

			// test views
			.AddGrid()
				// row 1
				.Add(toggledView1 = new TestView(), 0, 0, 3, 1)
				.Add(new TestView(), 3, 0)
				.Add(new TestView(), 4, 0)
	
				// row 2
				.Add(new TestView(), 0, 1)
				.Add(new TestView(), 1, 1)
				.Add(new TestView(), 2, 1)
				.Add(new TestView(), 3, 1)
				.Add(new TestView(), 4, 1)
	
				// row 3
				.Add(new TestView(), 0, 2)
				.Add(toggledView2 = new TestView(), 1, 2, 2, 2)
				.Add(new TestView(), 3, 2)
				.Add(new TestView(), 4, 2)
	
				// row 4
				.Add(new TestView(), 0, 3)
				.Add(new TestView(), 3, 3)
				.Add(new TestView(), 4, 3)
	
				// weights
				.SetColumnWeight(0, 1)
				.SetColumnWeight(1, 2)
				.SetColumnWeight(2, 3)
				.SetColumnWeight(3, 4)
				.SetColumnWeight(4, 5)
	
				.SetRowWeight(0, 1)
				.SetRowWeight(1, 2)
				.SetRowWeight(2, 3)
				.SetRowWeight(3, 4);
	}

	virtual void RegisterListeners()
	{
		toggleView1Button->SetTarget(this);
		toggleView2Button->SetTarget(this);
	}

	virtual void MessageReceived(BMessage* message)
	{
		switch (message->what) {
			case MSG_TOGGLE_1:
			{
				if (toggledView1->IsHidden(toggledView1))
					toggledView1->Show();
				else
					toggledView1->Hide();
				break;
			}

			case MSG_TOGGLE_2:
			{
				if (toggledView2->IsHidden(toggledView2))
					toggledView2->Show();
				else
					toggledView2->Hide();
				break;
			}

			default:
				BHandler::MessageReceived(message);
				break;
		}
	}

private:
	BButton*	toggleView1Button;
	BButton*	toggleView2Button;
	TestView*	toggledView1;
	TestView*	toggledView2;
};


// SplitterGroupLayoutTest1
struct SplitterGroupLayoutTest1 : public Test {
	SplitterGroupLayoutTest1()
		: Test("Group, splitters 1", "BGroupLayout with BSplitters.")
	{
		BLayoutBuilder::Group<>(rootView, B_HORIZONTAL)
			// controls
			.AddGroup(B_VERTICAL)
				.Add(toggleRowButton = new BButton("Toggle Row",
						new BMessage(MSG_TOGGLE_1)))
				.Add(toggleViewButton = new BButton("Toggle View",
						new BMessage(MSG_TOGGLE_2)))
				.AddGlue()
				.End()

			// test views
			.AddSplit(B_VERTICAL)
				// row 1
				.AddSplit(B_HORIZONTAL, B_USE_DEFAULT_SPACING, 1)
					.Add(new TestView(), 1)
					.Add(toggledView = new TestView(), 2)
					.Add(new TestView(), 3)
					.End()
				// make the row uncollapsible
				.SetCollapsible(false)

				// row 2
				.AddSplit(B_HORIZONTAL, B_USE_DEFAULT_SPACING, 2)
					.GetView(&toggledRow)
					.Add(new TestView())
					.Add(new TestView())
					.Add(new TestView())
					.End()

				// row 3
				.AddSplit(B_HORIZONTAL, B_USE_DEFAULT_SPACING, 3)
					.Add(new TestView(), 3)
					.Add(new TestView(), 2)
					.Add(new TestView(), 1)
					.End()
				// make the row uncollapsible
				.SetCollapsible(false);
	}

	virtual void RegisterListeners()
	{
		toggleRowButton->SetTarget(this);
		toggleViewButton->SetTarget(this);
	}

	virtual void MessageReceived(BMessage* message)
	{
		switch (message->what) {
			case MSG_TOGGLE_1:
			{
				if (toggledRow->IsHidden(toggledRow))
					toggledRow->Show();
				else
					toggledRow->Hide();
				break;
			}

			case MSG_TOGGLE_2:
			{
				if (toggledView->IsHidden(toggledView))
					toggledView->Show();
				else
					toggledView->Hide();
				break;
			}

			default:
				BHandler::MessageReceived(message);
				break;
		}
	}

private:
	BButton*	toggleRowButton;
	BButton*	toggleViewButton;
	BView*		toggledRow;
	TestView*	toggledView;
};


// SplitterGroupLayoutTest2
struct SplitterGroupLayoutTest2 : public Test {
	SplitterGroupLayoutTest2()
		: Test("Group, splitters 2",
			"BGroupLayout with BSplitters. Restricted maximum widths.")
	{
		TestView* testView1 = new TestView();
		TestView* testView2 = new TestView();
		TestView* testView3 = new TestView();

		BLayoutBuilder::Group<>(rootView, B_HORIZONTAL)
			// test views
			.AddGroup(B_VERTICAL)
				// split view
				.AddSplit(B_HORIZONTAL)
					.Add(testView1, 0)
					.Add(testView2, 1)
					.Add(testView3, 2);

		// set maximal width on the test views
		testView1->SetExplicitMaxSize(BSize(100, B_SIZE_UNSET));
		testView2->SetExplicitMaxSize(BSize(100, B_SIZE_UNSET));
		testView3->SetExplicitMaxSize(BSize(100, B_SIZE_UNSET));
	}
};


// SplitterGridLayoutTest1
struct SplitterGridLayoutTest1 : public Test {
	SplitterGridLayoutTest1()
		: Test("Grid, h splitters", "BGridLayout with horizontal BSplitters.")
	{
		BGridLayout* layouts[3];

		BLayoutBuilder::Group<>(rootView, B_HORIZONTAL)
			// controls
			.AddGroup(B_VERTICAL)
				.Add(toggleView1Button = new BButton("Toggle View 1",
						new BMessage(MSG_TOGGLE_1)))
				.Add(toggleView2Button = new BButton("Toggle View 2",
						new BMessage(MSG_TOGGLE_2)))
				.AddGlue()
				.End()

			// test views
			.AddSplit(B_HORIZONTAL)
				// splitter element 1
				.AddGrid(B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING, 6)
					.GetLayout(&layouts[0])
					// row 1
					.Add(toggledView1 = new TestView(), 0, 0, 3, 1)
					// row 2
					.Add(new TestView(), 0, 1)
					.Add(new TestView(), 1, 1)
					.Add(new TestView(), 2, 1)
					// row 3
					.Add(new TestView(), 0, 2)
					.Add(toggledView2 = new TestView(), 1, 2, 2, 2)
					// row 4
					.Add(new TestView(), 0, 3)

					// column weights
					.SetColumnWeight(0, 1)
					.SetColumnWeight(1, 2)
					.SetColumnWeight(2, 3)
					.End()
				
				// splitter element 2
				.AddGrid(B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING, 4)
					.GetLayout(&layouts[1])
					// row 1
					.Add(new TestView(), 0, 0)
					// row 2
					.Add(new TestView(), 0, 1)
					// row 3
					.Add(new TestView(), 0, 2)
					// row 4
					.Add(new TestView(), 0, 3)
					.End()

				// splitter element 3
				.AddGrid(B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING, 5)
					.GetLayout(&layouts[2])
					// row 1
					.Add(new TestView(), 0, 0)
					// row 2
					.Add(new TestView(), 0, 1)
					// row 3
					.Add(new TestView(), 0, 2)
					// row 4
					.Add(new TestView(), 0, 3);

		// set row weights
		for (int i = 0; i < 3; i++) {
			layouts[i]->SetRowWeight(0, 1);
			layouts[i]->SetRowWeight(1, 2);
			layouts[i]->SetRowWeight(2, 3);
			layouts[i]->SetRowWeight(3, 4);
		}

		// set explicit min/max heights for toggled views
		toggledView1->SetExplicitMinSize(BSize(B_SIZE_UNSET, 100));
		toggledView2->SetExplicitMaxSize(BSize(B_SIZE_UNSET, 200));

		// align the layouts
		layouts[0]->AlignLayoutWith(layouts[1], B_VERTICAL);
		layouts[0]->AlignLayoutWith(layouts[2], B_VERTICAL);
	}

	virtual void RegisterListeners()
	{
		toggleView1Button->SetTarget(this);
		toggleView2Button->SetTarget(this);
	}

	virtual void MessageReceived(BMessage* message)
	{
		switch (message->what) {
			case MSG_TOGGLE_1:
			{
				if (toggledView1->IsHidden(toggledView1))
					toggledView1->Show();
				else
					toggledView1->Hide();
				break;
			}

			case MSG_TOGGLE_2:
			{
				if (toggledView2->IsHidden(toggledView2))
					toggledView2->Show();
				else
					toggledView2->Hide();
				break;
			}

			default:
				BHandler::MessageReceived(message);
				break;
		}
	}

private:
	BButton*	toggleView1Button;
	BButton*	toggleView2Button;
	TestView*	toggledView1;
	TestView*	toggledView2;
};


// SplitterGridLayoutTest2
struct SplitterGridLayoutTest2 : public Test {
	SplitterGridLayoutTest2()
		: Test("Grid, v splitters", "BGridLayout with vertical BSplitters.")
	{
		BGridLayout* layouts[3];

		BLayoutBuilder::Group<>(rootView, B_HORIZONTAL)
			// controls
			.AddGroup(B_VERTICAL)
				.Add(toggleView1Button = new BButton("Toggle View 1",
						new BMessage(MSG_TOGGLE_1)))
				.Add(toggleView2Button = new BButton("Toggle View 2",
						new BMessage(MSG_TOGGLE_2)))
				.AddGlue()
			.End()

			// test views
			.AddSplit(B_VERTICAL)
				// splitter element 1
				.AddGrid(B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING, 1)
					.GetLayout(&layouts[0])
					// row 1
					.Add(toggledView1 = new TestView(), 0, 0, 3, 1)
					.Add(new TestView(), 3, 0)
					.Add(new TestView(), 4, 0)
					.End()
				
				// splitter element 2
				.AddGrid(B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING, 2)
					.GetLayout(&layouts[1])
					// row 2
					.Add(new TestView(), 0, 0)
					.Add(new TestView(), 1, 0)
					.Add(new TestView(), 2, 0)
					.Add(new TestView(), 3, 0)
					.Add(new TestView(), 4, 0)
					.End()

				// splitter element 3
				.AddGrid(B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING, 7)
					.GetLayout(&layouts[2])
					// row 3
					.Add(new TestView(), 0, 0)
					.Add(toggledView2 = new TestView(), 1, 0, 2, 2)
					.Add(new TestView(), 3, 0)
					.Add(new TestView(), 4, 0)
					// row 4
					.Add(new TestView(), 0, 1)
					.Add(new TestView(), 3, 1)
					.Add(new TestView(), 4, 1)

					// row weights
					.SetRowWeight(0, 3)
					.SetRowWeight(1, 4);

		// set column weights
		for (int i = 0; i < 3; i++) {
			layouts[i]->SetColumnWeight(0, 1);
			layouts[i]->SetColumnWeight(1, 2);
			layouts[i]->SetColumnWeight(2, 3);
			layouts[i]->SetColumnWeight(3, 4);
			layouts[i]->SetColumnWeight(4, 5);
		}

		// align the layouts
		layouts[0]->AlignLayoutWith(layouts[1], B_HORIZONTAL);
		layouts[0]->AlignLayoutWith(layouts[2], B_HORIZONTAL);
	}

	virtual void RegisterListeners()
	{
		toggleView1Button->SetTarget(this);
		toggleView2Button->SetTarget(this);
	}

	virtual void MessageReceived(BMessage* message)
	{
		switch (message->what) {
			case MSG_TOGGLE_1:
			{
				if (toggledView1->IsHidden(toggledView1))
					toggledView1->Show();
				else
					toggledView1->Hide();
				break;
			}

			case MSG_TOGGLE_2:
			{
				if (toggledView2->IsHidden(toggledView2))
					toggledView2->Show();
				else
					toggledView2->Hide();
				break;
			}

			default:
				BHandler::MessageReceived(message);
				break;
		}
	}

private:
	BButton*	toggleView1Button;
	BButton*	toggleView2Button;
	TestView*	toggledView1;
	TestView*	toggledView2;
};


// GroupLayoutHeightForWidthTestHorizontal1
struct GroupLayoutHeightForWidthTestHorizontal1 : public Test {
	GroupLayoutHeightForWidthTestHorizontal1()
		: Test("Group, height for width, h",
			"Horizontal BGroupLayout with height for width view.")
	{
		BLayoutBuilder::Group<>(rootView, B_HORIZONTAL)
			// controls
			.AddGroup(B_VERTICAL, 10, 0)
				.Add(aspectRatioButton = new BRadioButton("fixed aspect ratio",
						new BMessage(MSG_FIXED_ASPECT_RATIO)))
				.Add(sumButton = new BRadioButton("fixed sum",
						new BMessage(MSG_FIXED_SUM)))
				.Add(productButton = new BRadioButton("fixed product",
						new BMessage(MSG_FIXED_PRODUCT)))
				.AddGlue()
			.End()

			// test views
			.AddGroup(B_VERTICAL, 10)
				// row 1
				.AddGroup(B_HORIZONTAL, 10)
					.Add(new TestView())
					.Add(new TestView())
					.Add(new TestView())
				.End()

				// row 2
				.AddGroup(B_HORIZONTAL, 10)
					.Add(new TestView())
					.Add(heightForWidthView = new HeightForWidthTestView(kRed,
							FIXED_ASPECT_RATIO, 0.5f))
					.Add(new TestView())
				.End()

				// row 3
				.AddGroup(B_HORIZONTAL, 10)
					.Add(new TestView())
					.Add(new TestView())
					.Add(new TestView())
				.End()
			.End()
		;

		aspectRatioButton->SetValue(1);
	}

	virtual void RegisterListeners()
	{
		aspectRatioButton->SetTarget(this);
		sumButton->SetTarget(this);
		productButton->SetTarget(this);
	}

	virtual void MessageReceived(BMessage* message)
	{
		switch (message->what) {
			case MSG_FIXED_ASPECT_RATIO:
			{
				heightForWidthView->SetType(FIXED_ASPECT_RATIO, 0.5f);
				break;
			}

			case MSG_FIXED_SUM:
			{
				heightForWidthView->SetType(FIXED_SUM, 200);
				break;
			}

			case MSG_FIXED_PRODUCT:
			{
				heightForWidthView->SetType(FIXED_PRODUCT, 40000);
				break;
			}

			default:
				BHandler::MessageReceived(message);
				break;
		}
	}

private:
	BRadioButton*			aspectRatioButton;
	BRadioButton*			sumButton;
	BRadioButton*			productButton;
	HeightForWidthTestView*	heightForWidthView;
};


// GroupLayoutHeightForWidthTestVertical1
struct GroupLayoutHeightForWidthTestVertical1 : public Test {
	GroupLayoutHeightForWidthTestVertical1()
		: Test("Group, height for width, v",
			"Vertical BGroupLayout with height for width view.")
	{
		BLayoutBuilder::Group<>(rootView, B_HORIZONTAL)
			// controls
			.AddGroup(B_VERTICAL, 10, 0)
				.Add(aspectRatioButton = new BRadioButton("fixed aspect ratio",
						new BMessage(MSG_FIXED_ASPECT_RATIO)))
				.Add(sumButton = new BRadioButton("fixed sum",
						new BMessage(MSG_FIXED_SUM)))
				.Add(productButton = new BRadioButton("fixed product",
						new BMessage(MSG_FIXED_PRODUCT)))
				.AddGlue()
			.End()

			// test views
			.AddGroup(B_HORIZONTAL, 10)
				// column 1
				.AddGroup(B_VERTICAL, 10)
					.Add(new TestView())
					.Add(new TestView())
					.Add(new TestView())
				.End()

				// column 2
				.AddGroup(B_VERTICAL, 10)
					.Add(new TestView())
					.Add(heightForWidthView = new HeightForWidthTestView(kRed,
							FIXED_ASPECT_RATIO, 0.5f))
					.Add(new TestView())
				.End()

				// column 3
				.AddGroup(B_VERTICAL, 10)
					.Add(new TestView())
					.Add(new TestView())
					.Add(new TestView())
				.End()
			.End()
		;

		aspectRatioButton->SetValue(1);
	}

	virtual void RegisterListeners()
	{
		aspectRatioButton->SetTarget(this);
		sumButton->SetTarget(this);
		productButton->SetTarget(this);
	}

	virtual void MessageReceived(BMessage* message)
	{
		switch (message->what) {
			case MSG_FIXED_ASPECT_RATIO:
			{
				heightForWidthView->SetType(FIXED_ASPECT_RATIO, 0.5f);
				break;
			}

			case MSG_FIXED_SUM:
			{
				heightForWidthView->SetType(FIXED_SUM, 200);
				break;
			}

			case MSG_FIXED_PRODUCT:
			{
				heightForWidthView->SetType(FIXED_PRODUCT, 40000);
				break;
			}

			default:
				BHandler::MessageReceived(message);
				break;
		}
	}

private:
	BRadioButton*			aspectRatioButton;
	BRadioButton*			sumButton;
	BRadioButton*			productButton;
	HeightForWidthTestView*	heightForWidthView;
};


// GridLayoutHeightForWidthTest1
struct GridLayoutHeightForWidthTest1 : public Test {
	GridLayoutHeightForWidthTest1()
		: Test("Grid, height for width",
			"BGridLayout with height for width view.")
	{
		BLayoutBuilder::Group<>(rootView, B_HORIZONTAL)
			// controls
			.AddGroup(B_VERTICAL, B_USE_DEFAULT_SPACING, 0)
				.Add(aspectRatioButton = new BRadioButton("fixed aspect ratio",
						new BMessage(MSG_FIXED_ASPECT_RATIO)))
				.Add(sumButton = new BRadioButton("fixed sum",
						new BMessage(MSG_FIXED_SUM)))
				.Add(productButton = new BRadioButton("fixed product",
						new BMessage(MSG_FIXED_PRODUCT)))
				.AddGlue()
			.End()

			// test views
			.AddGrid()
				// row 1
				.Add(new TestView(), 0, 0, 3, 1)
				.Add(new TestView(), 3, 0)
				.Add(new TestView(), 4, 0)
	
				// row 2
				.Add(new TestView(), 0, 1)
				.Add(new TestView(), 1, 1)
				.Add(new TestView(), 2, 1)
				.Add(new TestView(), 3, 1)
				.Add(new TestView(), 4, 1)
	
				// row 3
				.Add(new TestView(), 0, 2)
				.Add(heightForWidthView = new HeightForWidthTestView(kRed,
						FIXED_ASPECT_RATIO, 0.5f), 1, 2, 2, 2)
				.Add(new TestView(), 3, 2)
				.Add(new TestView(), 4, 2)
	
				// row 4
				.Add(new TestView(), 0, 3)
				.Add(new TestView(), 3, 3)
				.Add(new TestView(), 4, 3)
	
				// weights
				.SetColumnWeight(0, 1)
				.SetColumnWeight(1, 2)
				.SetColumnWeight(2, 3)
				.SetColumnWeight(3, 4)
				.SetColumnWeight(4, 5)
	
				.SetRowWeight(0, 1)
				.SetRowWeight(1, 2)
				.SetRowWeight(2, 3)
				.SetRowWeight(3, 4);

		aspectRatioButton->SetValue(1);
	}

	virtual void RegisterListeners()
	{
		aspectRatioButton->SetTarget(this);
		sumButton->SetTarget(this);
		productButton->SetTarget(this);
	}

	virtual void MessageReceived(BMessage* message)
	{
		switch (message->what) {
			case MSG_FIXED_ASPECT_RATIO:
			{
				heightForWidthView->SetType(FIXED_ASPECT_RATIO, 0.5f);
				break;
			}

			case MSG_FIXED_SUM:
			{
				heightForWidthView->SetType(FIXED_SUM, 200);
				break;
			}

			case MSG_FIXED_PRODUCT:
			{
				heightForWidthView->SetType(FIXED_PRODUCT, 40000);
				break;
			}

			default:
				BHandler::MessageReceived(message);
				break;
		}
	}

private:
	BRadioButton*			aspectRatioButton;
	BRadioButton*			sumButton;
	BRadioButton*			productButton;
	HeightForWidthTestView*	heightForWidthView;
};


// SplitterGroupLayoutHeightForWidthTest1
struct SplitterGroupLayoutHeightForWidthTest1 : public Test {
	SplitterGroupLayoutHeightForWidthTest1()
		: Test("Group, splitters, height for width",
			"Horizontal BGroupLayout with height for width view and "
				"BSplitters.")
	{
		BLayoutBuilder::Group<>(rootView, B_HORIZONTAL)
			// controls
			.AddGroup(B_VERTICAL, B_USE_DEFAULT_SPACING, 0)
				.Add(aspectRatioButton = new BRadioButton("fixed aspect ratio",
						new BMessage(MSG_FIXED_ASPECT_RATIO)))
				.Add(sumButton = new BRadioButton("fixed sum",
						new BMessage(MSG_FIXED_SUM)))
				.Add(productButton = new BRadioButton("fixed product",
						new BMessage(MSG_FIXED_PRODUCT)))
				.AddGlue()
			.End()

			// test views
			.AddSplit(B_VERTICAL, B_USE_DEFAULT_SPACING, 1)
				// row 1
				.AddSplit(B_HORIZONTAL, B_USE_DEFAULT_SPACING, 1)
					.Add(new TestView(), 1)
					.Add(new TestView(), 2)
					.Add(new TestView(), 3)
					.End()
				// make the row uncollapsible
				.SetCollapsible(false)

				// row 2
				.AddSplit(B_HORIZONTAL, B_USE_DEFAULT_SPACING, 2)
					.Add(new TestView())
					.Add(heightForWidthView = new HeightForWidthTestView(kRed,
							FIXED_ASPECT_RATIO, 0.5f))
					.Add(new TestView())
					.End()

				// row 3
				.AddSplit(B_HORIZONTAL, B_USE_DEFAULT_SPACING, 3)
					.Add(new TestView(), 3)
					.Add(new TestView(), 2)
					.Add(new TestView(), 1)
					.End()
				// make the row uncollapsible
				.SetCollapsible(false);

		aspectRatioButton->SetValue(1);
	}

	virtual void RegisterListeners()
	{
		aspectRatioButton->SetTarget(this);
		sumButton->SetTarget(this);
		productButton->SetTarget(this);
	}

	virtual void MessageReceived(BMessage* message)
	{
		switch (message->what) {
			case MSG_FIXED_ASPECT_RATIO:
			{
				heightForWidthView->SetType(FIXED_ASPECT_RATIO, 0.5f);
				break;
			}

			case MSG_FIXED_SUM:
			{
				heightForWidthView->SetType(FIXED_SUM, 200);
				break;
			}

			case MSG_FIXED_PRODUCT:
			{
				heightForWidthView->SetType(FIXED_PRODUCT, 40000);
				break;
			}

			default:
				BHandler::MessageReceived(message);
				break;
		}
	}

private:
	BRadioButton*			aspectRatioButton;
	BRadioButton*			sumButton;
	BRadioButton*			productButton;
	HeightForWidthTestView*	heightForWidthView;
};


// SplitterGridLayoutHeightForWidthTest1
struct SplitterGridLayoutHeightForWidthTest1 : public Test {
	SplitterGridLayoutHeightForWidthTest1()
		: Test("Grid, splitters, height for width",
			"BGridLayout with height for width view and horizontal BSplitters.")
	{
		BGridLayout* layouts[3];

		BLayoutBuilder::Group<>(rootView, B_HORIZONTAL, B_USE_DEFAULT_SPACING)
			// controls
			.AddGroup(B_VERTICAL, B_USE_DEFAULT_SPACING, 0)
				.Add(aspectRatioButton = new BRadioButton("fixed aspect ratio",
						new BMessage(MSG_FIXED_ASPECT_RATIO)))
				.Add(sumButton = new BRadioButton("fixed sum",
						new BMessage(MSG_FIXED_SUM)))
				.Add(productButton = new BRadioButton("fixed product",
						new BMessage(MSG_FIXED_PRODUCT)))
				.AddGlue()
			.End()

			// test views
			.AddSplit(B_HORIZONTAL)
				// splitter element 1
				.AddGrid(B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING, 6)
					.GetLayout(&layouts[0])
					// row 1
					.Add(new TestView(), 0, 0, 3, 1)
					// row 2
					.Add(new TestView(), 0, 1)
					.Add(new TestView(), 1, 1)
					.Add(new TestView(), 2, 1)
					// row 3
					.Add(new TestView(), 0, 2)
					.Add(heightForWidthView = new HeightForWidthTestView(kRed,
							FIXED_ASPECT_RATIO, 0.5f), 1, 2, 2, 2)
					// row 4
					.Add(new TestView(), 0, 3)

					// column weights
					.SetColumnWeight(0, 1)
					.SetColumnWeight(1, 2)
					.SetColumnWeight(2, 3)
					.End()
				
				// splitter element 2
				.AddGrid(B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING, 4)
					.GetLayout(&layouts[1])
					// row 1
					.Add(new TestView(), 0, 0)
					// row 2
					.Add(new TestView(), 0, 1)
					// row 3
					.Add(new TestView(), 0, 2)
					// row 4
					.Add(new TestView(), 0, 3)
					.End()

				// splitter element 3
				.AddGrid(B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING, 5)
					.GetLayout(&layouts[2])
					// row 1
					.Add(new TestView(), 0, 0)
					// row 2
					.Add(new TestView(), 0, 1)
					// row 3
					.Add(new TestView(), 0, 2)
					// row 4
					.Add(new TestView(), 0, 3);

		// set row weights
		for (int i = 0; i < 3; i++) {
			layouts[i]->SetRowWeight(0, 1);
			layouts[i]->SetRowWeight(1, 2);
			layouts[i]->SetRowWeight(2, 3);
			layouts[i]->SetRowWeight(3, 4);
		}

		// align the layouts
		layouts[0]->AlignLayoutWith(layouts[1], B_VERTICAL);
		layouts[0]->AlignLayoutWith(layouts[2], B_VERTICAL);

		aspectRatioButton->SetValue(1);
	}

	virtual void RegisterListeners()
	{
		aspectRatioButton->SetTarget(this);
		sumButton->SetTarget(this);
		productButton->SetTarget(this);
	}

	virtual void MessageReceived(BMessage* message)
	{
		switch (message->what) {
			case MSG_FIXED_ASPECT_RATIO:
			{
				heightForWidthView->SetType(FIXED_ASPECT_RATIO, 0.5f);
				break;
			}

			case MSG_FIXED_SUM:
			{
				heightForWidthView->SetType(FIXED_SUM, 200);
				break;
			}

			case MSG_FIXED_PRODUCT:
			{
				heightForWidthView->SetType(FIXED_PRODUCT, 40000);
				break;
			}

			default:
				BHandler::MessageReceived(message);
				break;
		}
	}

private:
	BRadioButton*			aspectRatioButton;
	BRadioButton*			sumButton;
	BRadioButton*			productButton;
	HeightForWidthTestView*	heightForWidthView;
};


// LabelTest1
struct LabelTest1 : public Test {
	LabelTest1()
		: Test("BTextControl, BMenuField, grid",
			"Aligning BTextControl/BMenuField labels using a 2 column "
			"BGridLayout.")
	{
		textControl1 = new BTextControl("Label", NULL, NULL);
		textControl2 = new BTextControl("Long Label", NULL, NULL);
		textControl3 = new BTextControl("Very Long Label", NULL, NULL);

		menuField1 = new BMenuField("Label", new BMenu("Options"));
		menuField2 = new BMenuField("Long Label",
			new BMenu("More Options"));
		menuField3 = new BMenuField("Very Long Label",
			new BMenu("Obscure Options"));

		BLayoutBuilder::Group<>(rootView, B_VERTICAL)
			// controls
			.AddGroup(B_HORIZONTAL)
				.Add(changeLabelsButton = new BButton("Random Labels",
						new BMessage(MSG_TOGGLE_1)))
				.AddGlue()
				.End()

			.AddGlue()

			// test views
			.AddGrid()
				// padding
				.Add(BSpaceLayoutItem::CreateGlue(), 0, 0)
				.Add(BSpaceLayoutItem::CreateGlue(), 1, 0)

				// row 1
				.Add(textControl1->CreateLabelLayoutItem(), 0, 1)
				.Add(textControl1->CreateTextViewLayoutItem(), 1, 1)
	
				// row 2
				.Add(textControl2->CreateLabelLayoutItem(), 0, 2)
				.Add(textControl2->CreateTextViewLayoutItem(), 1, 2)
	
				// row 3
				.Add(textControl3->CreateLabelLayoutItem(), 0, 3)
				.Add(textControl3->CreateTextViewLayoutItem(), 1, 3)

				// row 4
				.Add(menuField1->CreateLabelLayoutItem(), 0, 4)
				.Add(menuField1->CreateMenuBarLayoutItem(), 1, 4)
	
				// row 5
				.Add(menuField2->CreateLabelLayoutItem(), 0, 5)
				.Add(menuField2->CreateMenuBarLayoutItem(), 1, 5)
	
				// row 6
				.Add(menuField3->CreateLabelLayoutItem(), 0, 6)
				.Add(menuField3->CreateMenuBarLayoutItem(), 1, 6)

				// padding
				.Add(BSpaceLayoutItem::CreateGlue(), 0, 7)
				.Add(BSpaceLayoutItem::CreateGlue(), 1, 7)
				.End()
			.AddGlue();
	}

	virtual void RegisterListeners()
	{
		changeLabelsButton->SetTarget(this);
	}

	virtual void MessageReceived(BMessage* message)
	{
		switch (message->what) {
			case MSG_TOGGLE_1:
			{
				BTextControl* textControls[] = {
					textControl1, textControl2, textControl3
				};

				BMenuField* menuFields[] = {
					menuField1, menuField2, menuField3
				};
				
				for (int i = 0; i < 3; i++) {
					BTextControl* textControl = textControls[i];
					BMenuField* menuField = menuFields[i];

					textControl->SetLabel(_RandomLabel().String());
					menuField->SetLabel(_RandomLabel().String());
				}
			}

			default:
				BHandler::MessageReceived(message);
				break;
		}
	}

	BString _RandomLabel() const
	{
		const char* digits = "0123456789";

		int length = rand() % 20;
		BString label("Random ");
		for (int k = 0; k < length; k++)
			label.Append(digits[k % 10], 1);

		return label;
	}

private:
	BButton*		changeLabelsButton;
	BTextControl*	textControl1;
	BTextControl*	textControl2;
	BTextControl*	textControl3;
	BMenuField*		menuField1;
	BMenuField*		menuField2;
	BMenuField*		menuField3;
};


// TestWindow
class TestWindow : public BWindow {
public:
	TestWindow()
		: BWindow(BRect(100, 100, 700, 500), "LayoutTest1",
			B_TITLED_WINDOW,
			B_ASYNCHRONOUS_CONTROLS | B_QUIT_ON_WINDOW_CLOSE
				| B_AUTO_UPDATE_SIZE_LIMITS)
	{
		fTestList = new BListView(BRect(0, 0, 10, 10), "test list",
			B_SINGLE_SELECTION_LIST, B_FOLLOW_ALL);
		BView* scrollView = new BScrollView("test scroll view", fTestList);
		scrollView->SetExplicitMaxSize(BSize(190, B_SIZE_UNLIMITED));

		fTestCardLayout = new BCardLayout();
		BView* cardView = new BView("card view", 0, fTestCardLayout);

		BLayoutBuilder::Group<>(this, B_HORIZONTAL)
			.SetInsets(B_USE_WINDOW_INSETS)
			.Add(scrollView)
			.Add(cardView);

		// add the tests
		_AddTest(new GroupLayoutTest1());
		_AddTest(new GroupAlignedLayoutTest1());
		_AddTest(new GridLayoutTest1());
		_AddTest(new SplitterGroupLayoutTest1());
		_AddTest(new SplitterGroupLayoutTest2());
		_AddTest(new SplitterGridLayoutTest1());
		_AddTest(new SplitterGridLayoutTest2());
		_AddTest(new GroupLayoutHeightForWidthTestHorizontal1());
		_AddTest(new GroupLayoutHeightForWidthTestVertical1());
		_AddTest(new GridLayoutHeightForWidthTest1());
		_AddTest(new SplitterGroupLayoutHeightForWidthTest1());
		_AddTest(new SplitterGridLayoutHeightForWidthTest1());
		_AddTest(new LabelTest1());

		fTestList->SetSelectionMessage(new BMessage(MSG_TEST_SELECTED));
		_DumpViewHierarchy(GetLayout()->Owner());
	}

	virtual void MessageReceived(BMessage* message)
	{
		switch (message->what) {
			case MSG_TEST_SELECTED:
				fTestCardLayout->SetVisibleItem(fTestList->CurrentSelection());
				break;
			default:
				BWindow::MessageReceived(message);
				break;
		}
	}

private:
	void _AddTest(Test* test) {
		fTestList->AddItem(new BStringItem(test->name.String()));

		BGroupView* containerView = new BGroupView(B_VERTICAL, 0);

		BStringView* descriptionView = new BStringView("test description",
			test->description.String());

		descriptionView->SetExplicitMinSize(BSize(0, B_SIZE_UNSET));
		containerView->AddChild(descriptionView);

		// spacing/glue
		containerView->GroupLayout()->AddItem(
			BSpaceLayoutItem::CreateVerticalStrut(10));
		containerView->GroupLayout()->AddItem(
			BSpaceLayoutItem::CreateGlue(), 0);
		
		// the test view: wrap it, so we can have unlimited size
		BGroupView* wrapperView = new BGroupView(B_HORIZONTAL, 0);
		containerView->AddChild(wrapperView);
		wrapperView->AddChild(test->rootView);

		// glue
		containerView->GroupLayout()->AddItem(
			BSpaceLayoutItem::CreateGlue(), 0);
		
		wrapperView->SetExplicitMaxSize(
			BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED));
		containerView->SetExplicitMaxSize(
			BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED));

		fTestCardLayout->AddView(containerView);

		AddHandler(test);
		test->RegisterListeners();
	}

	void _DumpViewHierarchy(BView* view, int32 indent = 0)
	{
		for (int32 i = 0; i < indent; i++)
			printf("  ");
		printf("view: %p", view);
		for (int32 i = 0; i < 15 - indent; i++)
			printf("  ");
		printf("(%s)\n", typeid(*view).name());

		int32 count = view->CountChildren();
		for (int32 i = 0; i < count; i++)
			_DumpViewHierarchy(view->ChildAt(i), indent + 1);
	}
	
private:
	BListView*		fTestList;
	BCardLayout*	fTestCardLayout;
};


int
main()
{
	BApplication app("application/x-vnd.haiku.layout-test1");

	BWindow* window = new TestWindow;
	window->Show();

	app.Run();

	return 0;
}