/* * Copyright 2009-2010, Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT License. */ #ifndef _LAYOUT_BUILDER_H #define _LAYOUT_BUILDER_H #include #include #include #include #include #include #include #include #include #include #include #include namespace BLayoutBuilder { template class Base; template class Group; template class Grid; template class Split; template class Menu; template class MenuItem; template class Base { protected: inline Base(); public: inline void SetParent(ParentBuilder* parent); // conceptually private inline ParentBuilder& End(); protected: ParentBuilder* fParent; }; template class Group : public Base { public: typedef Group ThisBuilder; typedef Group GroupBuilder; typedef Grid GridBuilder; typedef Split SplitBuilder; public: inline Group(enum orientation orientation = B_HORIZONTAL, float spacing = B_USE_DEFAULT_SPACING); inline Group(BWindow* window, enum orientation orientation = B_HORIZONTAL, float spacing = B_USE_DEFAULT_SPACING); inline Group(BGroupLayout* layout); inline Group(BGroupView* view); inline BGroupLayout* Layout() const; inline BView* View() const; inline ThisBuilder& GetLayout(BGroupLayout** _layout); inline ThisBuilder& GetView(BView** _view); inline ThisBuilder& Add(BView* view); inline ThisBuilder& Add(BView* view, float weight); inline ThisBuilder& Add(BLayoutItem* item); inline ThisBuilder& Add(BLayoutItem* item, float weight); inline GroupBuilder AddGroup(enum orientation orientation, float spacing = B_USE_DEFAULT_SPACING, float weight = 1.0f); inline GroupBuilder AddGroup(BGroupView* groupView, float weight = 1.0f); inline GroupBuilder AddGroup(BGroupLayout* groupLayout, float weight = 1.0f); inline GridBuilder AddGrid(float horizontal = B_USE_DEFAULT_SPACING, float vertical = B_USE_DEFAULT_SPACING, float weight = 1.0f); inline GridBuilder AddGrid(BGridLayout* gridLayout, float weight = 1.0f); inline GridBuilder AddGrid(BGridView* gridView, float weight = 1.0f); inline SplitBuilder AddSplit(enum orientation orientation, float spacing = B_USE_DEFAULT_SPACING, float weight = 1.0f); inline SplitBuilder AddSplit(BSplitView* splitView, float weight = 1.0f); inline ThisBuilder& AddGlue(float weight = 1.0f); inline ThisBuilder& AddStrut(float size); inline ThisBuilder& SetInsets(float left, float top, float right, float bottom); inline operator BGroupLayout*(); private: BGroupLayout* fLayout; }; template class Grid : public Base { public: typedef Grid ThisBuilder; typedef Group GroupBuilder; typedef Grid GridBuilder; typedef Split SplitBuilder; public: inline Grid(float horizontal = B_USE_DEFAULT_SPACING, float vertical = B_USE_DEFAULT_SPACING); inline Grid(BWindow* window, float horizontal = B_USE_DEFAULT_SPACING, float vertical = B_USE_DEFAULT_SPACING); inline Grid(BGridLayout* layout); inline Grid(BGridView* view); inline BGridLayout* Layout() const; inline BView* View() const; inline ThisBuilder& GetLayout(BGridLayout** _layout); inline ThisBuilder& GetView(BView** _view); inline ThisBuilder& Add(BView* view, int32 column, int32 row, int32 columnCount = 1, int32 rowCount = 1); inline ThisBuilder& Add(BLayoutItem* item, int32 column, int32 row, int32 columnCount = 1, int32 rowCount = 1); inline ThisBuilder& AddMenuField(BMenuField* menuField, int32 column, int32 row, alignment labelAlignment = B_ALIGN_HORIZONTAL_UNSET, int32 labelColumnCount = 1, int32 fieldColumnCount = 1, int32 rowCount = 1); inline ThisBuilder& AddTextControl(BTextControl* textControl, int32 column, int32 row, alignment labelAlignment = B_ALIGN_HORIZONTAL_UNSET, int32 labelColumnCount = 1, int32 textColumnCount = 1, int32 rowCount = 1); inline GroupBuilder AddGroup(enum orientation orientation, float spacing, int32 column, int32 row, int32 columnCount = 1, int32 rowCount = 1); inline GroupBuilder AddGroup(BGroupView* groupView, int32 column, int32 row, int32 columnCount = 1, int32 rowCount = 1); inline GroupBuilder AddGroup(BGroupLayout* groupLayout, int32 column, int32 row, int32 columnCount = 1, int32 rowCount = 1); inline GridBuilder AddGrid(float horizontalSpacing, float verticalSpacing, int32 column, int32 row, int32 columnCount = 1, int32 rowCount = 1); inline GridBuilder AddGrid(BGridLayout* gridLayout, int32 column, int32 row, int32 columnCount = 1, int32 rowCount = 1); inline GridBuilder AddGrid(BGridView* gridView, int32 column, int32 row, int32 columnCount = 1, int32 rowCount = 1); inline SplitBuilder AddSplit(enum orientation orientation, float spacing, int32 column, int32 row, int32 columnCount = 1, int32 rowCount = 1); inline SplitBuilder AddSplit(BSplitView* splitView, int32 column, int32 row, int32 columnCount = 1, int32 rowCount = 1); inline ThisBuilder& SetColumnWeight(int32 column, float weight); inline ThisBuilder& SetRowWeight(int32 row, float weight); inline ThisBuilder& SetInsets(float left, float top, float right, float bottom); inline operator BGridLayout*(); private: BGridLayout* fLayout; }; template class Split : public Base { public: typedef Split ThisBuilder; typedef Group GroupBuilder; typedef Grid GridBuilder; typedef Split SplitBuilder; public: inline Split(enum orientation orientation = B_HORIZONTAL, float spacing = B_USE_DEFAULT_SPACING); inline Split(BSplitView* view); inline BSplitView* View() const; inline ThisBuilder& GetView(BView** _view); inline ThisBuilder& GetSplitView(BSplitView** _view); inline ThisBuilder& Add(BView* view); inline ThisBuilder& Add(BView* view, float weight); inline ThisBuilder& Add(BLayoutItem* item); inline ThisBuilder& Add(BLayoutItem* item, float weight); inline GroupBuilder AddGroup(enum orientation orientation, float spacing = B_USE_DEFAULT_SPACING, float weight = 1.0f); inline GroupBuilder AddGroup(BGroupView* groupView, float weight = 1.0f); inline GroupBuilder AddGroup(BGroupLayout* groupLayout, float weight = 1.0f); inline GridBuilder AddGrid(float horizontal = B_USE_DEFAULT_SPACING, float vertical = B_USE_DEFAULT_SPACING, float weight = 1.0f); inline GridBuilder AddGrid(BGridView* gridView, float weight = 1.0f); inline GridBuilder AddGrid(BGridLayout* gridLayout, float weight = 1.0f); inline SplitBuilder AddSplit(enum orientation orientation, float spacing = B_USE_DEFAULT_SPACING, float weight = 1.0f); inline SplitBuilder AddSplit(BSplitView* splitView, float weight = 1.0f); inline ThisBuilder& SetCollapsible(bool collapsible); inline ThisBuilder& SetCollapsible(int32 index, bool collapsible); inline ThisBuilder& SetCollapsible(int32 first, int32 last, bool collapsible); inline ThisBuilder& SetInsets(float left, float top, float right, float bottom); inline operator BSplitView*(); private: BSplitView* fView; }; template class Menu : public Base { public: typedef Menu ThisBuilder; typedef MenuItem ItemBuilder; typedef Menu MenuBuilder; public: inline Menu(BMenu* menu); inline ThisBuilder& GetMenu(BMenu*& _menu); inline ItemBuilder AddItem(BMenuItem* item); inline ItemBuilder AddItem(BMenu* menu); inline ItemBuilder AddItem(const char* label, BMessage* message, char shortcut = 0, uint32 modifiers = 0); inline ItemBuilder AddItem(const char* label, uint32 messageWhat, char shortcut = 0, uint32 modifiers = 0); inline MenuBuilder AddMenu(BMenu* menu); inline MenuBuilder AddMenu(const char* title, menu_layout layout = B_ITEMS_IN_COLUMN); inline ThisBuilder& AddSeparator(); private: BMenu* fMenu; }; template class MenuItem : public Menu { public: typedef MenuItem ThisBuilder; public: inline MenuItem(ParentBuilder* parentBuilder, BMenu* menu, BMenuItem* item); inline ThisBuilder& GetItem(BMenuItem*& _item); inline ThisBuilder& SetEnabled(bool enabled); private: BMenuItem* fMenuItem; }; // #pragma mark - Base template Base::Base() : fParent(NULL) { } template void Base::SetParent(ParentBuilder* parent) { fParent = parent; } template ParentBuilder& Base::End() { return *fParent; } // #pragma mark - Group template Group::Group(enum orientation orientation, float spacing) : fLayout((new BGroupView(orientation, spacing))->GroupLayout()) { } template Group::Group(BWindow* window, enum orientation orientation, float spacing) : fLayout(new BGroupLayout(orientation, spacing)) { window->SetLayout(fLayout); fLayout->Owner()->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); // TODO: we get a white background if we don't do this } template Group::Group(BGroupLayout* layout) : fLayout(layout) { } template Group::Group(BGroupView* view) : fLayout(view->GroupLayout()) { } template BGroupLayout* Group::Layout() const { return fLayout; } template BView* Group::View() const { return fLayout->Owner(); } template typename Group::ThisBuilder& Group::GetLayout(BGroupLayout** _layout) { *_layout = fLayout; return *this; } template typename Group::ThisBuilder& Group::GetView(BView** _view) { *_view = fLayout->Owner(); return *this; } template typename Group::ThisBuilder& Group::Add(BView* view) { fLayout->AddView(view); return *this; } template typename Group::ThisBuilder& Group::Add(BView* view, float weight) { fLayout->AddView(view, weight); return *this; } template typename Group::ThisBuilder& Group::Add(BLayoutItem* item) { fLayout->AddItem(item); return *this; } template typename Group::ThisBuilder& Group::Add(BLayoutItem* item, float weight) { fLayout->AddItem(item, weight); return *this; } template typename Group::GroupBuilder Group::AddGroup(enum orientation orientation, float spacing, float weight) { GroupBuilder builder(new BGroupLayout(orientation, spacing)); builder.SetParent(this); fLayout->AddItem(builder.Layout(), weight); return builder; } template typename Group::GroupBuilder Group::AddGroup(BGroupView* groupView, float weight) { GroupBuilder builder(groupView); builder.SetParent(this); fLayout->AddItem(builder.Layout(), weight); return builder; } template typename Group::GroupBuilder Group::AddGroup(BGroupLayout* groupLayout, float weight) { GroupBuilder builder(groupLayout); builder.SetParent(this); fLayout->AddItem(builder.Layout(), weight); return builder; } template typename Group::GridBuilder Group::AddGrid(float horizontalSpacing, float verticalSpacing, float weight) { GridBuilder builder(new BGridLayout(horizontalSpacing, verticalSpacing)); builder.SetParent(this); fLayout->AddItem(builder.Layout(), weight); return builder; } template typename Group::GridBuilder Group::AddGrid(BGridLayout* gridLayout, float weight) { GridBuilder builder(gridLayout); builder.SetParent(this); fLayout->AddItem(builder.Layout(), weight); return builder; } template typename Group::GridBuilder Group::AddGrid(BGridView* gridView, float weight) { GridBuilder builder(gridView); builder.SetParent(this); fLayout->AddItem(builder.Layout(), weight); return builder; } template typename Group::SplitBuilder Group::AddSplit(enum orientation orientation, float spacing, float weight) { SplitBuilder builder(orientation, spacing); builder.SetParent(this); fLayout->AddView(builder.View(), weight); return builder; } template typename Group::SplitBuilder Group::AddSplit(BSplitView* splitView, float weight) { SplitBuilder builder(splitView); builder.SetParent(this); fLayout->AddView(builder.View(), weight); return builder; } template typename Group::ThisBuilder& Group::AddGlue(float weight) { fLayout->AddItem(BSpaceLayoutItem::CreateGlue(), weight); return *this; } template typename Group::ThisBuilder& Group::AddStrut(float size) { if (fLayout->Orientation() == B_HORIZONTAL) fLayout->AddItem(BSpaceLayoutItem::CreateHorizontalStrut(size)); else fLayout->AddItem(BSpaceLayoutItem::CreateVerticalStrut(size)); return *this; } template typename Group::ThisBuilder& Group::SetInsets(float left, float top, float right, float bottom) { fLayout->SetInsets(left, top, right, bottom); return *this; } template Group::operator BGroupLayout*() { return fLayout; } // #pragma mark - Grid template Grid::Grid(float horizontalSpacing, float verticalSpacing) : fLayout((new BGridView(horizontalSpacing, verticalSpacing))->GridLayout()) { } template Grid::Grid(BWindow* window, float horizontalSpacing, float verticalSpacing) : fLayout(new BGridLayout(horizontalSpacing, verticalSpacing)) { window->SetLayout(fLayout); fLayout->Owner()->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); // TODO: we get a white background if we don't do this } template Grid::Grid(BGridLayout* layout) : fLayout(layout) { } template Grid::Grid(BGridView* view) : fLayout(view->GridLayout()) { } template BGridLayout* Grid::Layout() const { return fLayout; } template BView* Grid::View() const { return fLayout->Owner(); } template typename Grid::ThisBuilder& Grid::GetLayout(BGridLayout** _layout) { *_layout = fLayout; return *this; } template typename Grid::ThisBuilder& Grid::GetView(BView** _view) { *_view = fLayout->Owner(); return *this; } template typename Grid::ThisBuilder& Grid::Add(BView* view, int32 column, int32 row, int32 columnCount, int32 rowCount) { fLayout->AddView(view, column, row, columnCount, rowCount); return *this; } template typename Grid::ThisBuilder& Grid::Add(BLayoutItem* item, int32 column, int32 row, int32 columnCount, int32 rowCount) { fLayout->AddItem(item, column, row, columnCount, rowCount); return *this; } template typename Grid::ThisBuilder& Grid::AddMenuField(BMenuField* menuField, int32 column, int32 row, alignment labelAlignment, int32 labelColumnCount, int32 fieldColumnCount, int32 rowCount) { BLayoutItem* item = menuField->CreateLabelLayoutItem(); item->SetExplicitAlignment( BAlignment(labelAlignment, B_ALIGN_VERTICAL_UNSET)); fLayout->AddItem(item, column, row, labelColumnCount, rowCount); fLayout->AddItem(menuField->CreateMenuBarLayoutItem(), column + labelColumnCount, row, fieldColumnCount, rowCount); return *this; } template typename Grid::ThisBuilder& Grid::AddTextControl(BTextControl* textControl, int32 column, int32 row, alignment labelAlignment, int32 labelColumnCount, int32 textColumnCount, int32 rowCount) { BLayoutItem* item = textControl->CreateLabelLayoutItem(); item->SetExplicitAlignment( BAlignment(labelAlignment, B_ALIGN_VERTICAL_UNSET)); fLayout->AddItem(item, column, row, labelColumnCount, rowCount); fLayout->AddItem(textControl->CreateTextViewLayoutItem(), column + labelColumnCount, row, textColumnCount, rowCount); return *this; } template typename Grid::GroupBuilder Grid::AddGroup(enum orientation orientation, float spacing, int32 column, int32 row, int32 columnCount, int32 rowCount) { GroupBuilder builder(new BGroupLayout(orientation, spacing)); builder.SetParent(this); fLayout->AddItem(builder.Layout(), column, row, columnCount, rowCount); return builder; } template typename Grid::GroupBuilder Grid::AddGroup(BGroupView* groupView, int32 column, int32 row, int32 columnCount, int32 rowCount) { GroupBuilder builder(groupView); builder.SetParent(this); fLayout->AddItem(builder.Layout(), column, row, columnCount, rowCount); return builder; } template typename Grid::GroupBuilder Grid::AddGroup(BGroupLayout* groupLayout, int32 column, int32 row, int32 columnCount, int32 rowCount) { GroupBuilder builder(groupLayout); builder.SetParent(this); fLayout->AddItem(builder.Layout(), column, row, columnCount, rowCount); return builder; } template typename Grid::GridBuilder Grid::AddGrid(float horizontalSpacing, float verticalSpacing, int32 column, int32 row, int32 columnCount, int32 rowCount) { GridBuilder builder(new BGridLayout(horizontalSpacing, verticalSpacing)); builder.SetParent(this); fLayout->AddItem(builder.Layout(), column, row, columnCount, rowCount); return builder; } template typename Grid::GridBuilder Grid::AddGrid(BGridView* gridView, int32 column, int32 row, int32 columnCount, int32 rowCount) { GridBuilder builder(gridView); builder.SetParent(this); fLayout->AddView(builder.View(), column, row, columnCount, rowCount); return builder; } template typename Grid::SplitBuilder Grid::AddSplit(enum orientation orientation, float spacing, int32 column, int32 row, int32 columnCount, int32 rowCount) { SplitBuilder builder(orientation, spacing); builder.SetParent(this); fLayout->AddView(builder.View(), column, row, columnCount, rowCount); return builder; } template typename Grid::SplitBuilder Grid::AddSplit(BSplitView* splitView, int32 column, int32 row, int32 columnCount, int32 rowCount) { SplitBuilder builder(splitView); builder.SetParent(this); fLayout->AddView(builder.View(), column, row, columnCount, rowCount); return builder; } template typename Grid::ThisBuilder& Grid::SetColumnWeight(int32 column, float weight) { fLayout->SetColumnWeight(column, weight); return *this; } template typename Grid::ThisBuilder& Grid::SetRowWeight(int32 row, float weight) { fLayout->SetRowWeight(row, weight); return *this; } template typename Grid::ThisBuilder& Grid::SetInsets(float left, float top, float right, float bottom) { fLayout->SetInsets(left, top, right, bottom); return *this; } template Grid::operator BGridLayout*() { return fLayout; } // #pragma mark - Split template Split::Split(enum orientation orientation, float spacing) : fView(new BSplitView(orientation, spacing)) { } template Split::Split(BSplitView* view) : fView(view) { } template BSplitView* Split::View() const { return fView; } template typename Split::ThisBuilder& Split::GetView(BView** _view) { *_view = fView; return *this; } template typename Split::ThisBuilder& Split::GetSplitView(BSplitView** _view) { *_view = fView; return *this; } template typename Split::ThisBuilder& Split::Add(BView* view) { fView->AddChild(view); return *this; } template typename Split::ThisBuilder& Split::Add(BView* view, float weight) { fView->AddChild(view, weight); return *this; } template typename Split::ThisBuilder& Split::Add(BLayoutItem* item) { fView->AddChild(item); return *this; } template typename Split::ThisBuilder& Split::Add(BLayoutItem* item, float weight) { fView->AddChild(item, weight); return *this; } template typename Split::GroupBuilder Split::AddGroup(enum orientation orientation, float spacing, float weight) { GroupBuilder builder(new BGroupLayout(orientation, spacing)); builder.SetParent(this); fView->AddChild(builder.Layout(), weight); return builder; } template typename Split::GroupBuilder Split::AddGroup(BGroupView* groupView, float weight) { GroupBuilder builder(groupView); builder.SetParent(this); fView->AddChild(builder.Layout(), weight); return builder; } template typename Split::GroupBuilder Split::AddGroup(BGroupLayout* groupLayout, float weight) { GroupBuilder builder(groupLayout); builder.SetParent(this); fView->AddChild(builder.Layout(), weight); return builder; } template typename Split::GridBuilder Split::AddGrid(float horizontalSpacing, float verticalSpacing, float weight) { GridBuilder builder(new BGridLayout(horizontalSpacing, verticalSpacing)); builder.SetParent(this); fView->AddChild(builder.Layout(), weight); return builder; } template typename Split::GridBuilder Split::AddGrid(BGridView* gridView, float weight) { GridBuilder builder(gridView); builder.SetParent(this); fView->AddChild(builder.Layout(), weight); return builder; } template typename Split::GridBuilder Split::AddGrid(BGridLayout* layout, float weight) { GridBuilder builder(layout); builder.SetParent(this); fView->AddChild(builder.Layout(), weight); return builder; } template typename Split::SplitBuilder Split::AddSplit(enum orientation orientation, float spacing, float weight) { SplitBuilder builder(orientation, spacing); builder.SetParent(this); fView->AddChild(builder.View(), weight); return builder; } template typename Split::ThisBuilder& Split::SetCollapsible(bool collapsible) { fView->SetCollapsible(collapsible); return *this; } template typename Split::ThisBuilder& Split::SetCollapsible(int32 index, bool collapsible) { fView->SetCollapsible(index, collapsible); return *this; } template typename Split::ThisBuilder& Split::SetCollapsible(int32 first, int32 last, bool collapsible) { fView->SetCollapsible(first, last, collapsible); return *this; } template typename Split::ThisBuilder& Split::SetInsets(float left, float top, float right, float bottom) { fView->SetInsets(left, top, right, bottom); return *this; } template Split::operator BSplitView*() { return fView; } // #pragma mark - Menu template Menu::Menu(BMenu* menu) : fMenu(menu) { } template typename Menu::ThisBuilder& Menu::GetMenu(BMenu*& _menu) { _menu = fMenu; return *this; } template typename Menu::ItemBuilder Menu::AddItem(BMenuItem* item) { fMenu->AddItem(item); return MenuItem(this->fParent, fMenu, item); } template typename Menu::ItemBuilder Menu::AddItem(BMenu* menu) { if (!fMenu->AddItem(menu)) throw std::bad_alloc(); return MenuItem(this->fParent, fMenu, fMenu->ItemAt(fMenu->CountItems() - 1)); } template typename Menu::ItemBuilder Menu::AddItem(const char* label, BMessage* message, char shortcut, uint32 modifiers) { BMenuItem* item = new BMenuItem(label, message, shortcut, modifiers); if (!fMenu->AddItem(item)) delete item; return MenuItem(this->fParent, fMenu, item); } template typename Menu::ItemBuilder Menu::AddItem(const char* label, uint32 messageWhat, char shortcut, uint32 modifiers) { BMessage* message = new BMessage(messageWhat); BMenuItem* item; try { item = new BMenuItem(label, message, shortcut, modifiers); } catch (...) { delete message; throw; } if (!fMenu->AddItem(item)) delete item; return MenuItem(this->fParent, fMenu, item); } template typename Menu::ThisBuilder& Menu::AddSeparator() { fMenu->AddSeparatorItem(); return *this; } template typename Menu::MenuBuilder Menu::AddMenu(BMenu* menu) { if (!fMenu->AddItem(menu)) throw std::bad_alloc(); MenuBuilder builder(menu); builder.SetParent(this); return builder; } template typename Menu::MenuBuilder Menu::AddMenu(const char* title, menu_layout layout) { BMenu* menu = new BMenu(title, layout); if (!fMenu->AddItem(menu)) { delete menu; throw std::bad_alloc(); } MenuBuilder builder(menu); builder.SetParent(this); return builder; } // #pragma mark - MenuItem template MenuItem::MenuItem(ParentBuilder* parentBuilder, BMenu* menu, BMenuItem* item) : Menu(menu), fMenuItem(item) { SetParent(parentBuilder); } template typename MenuItem::ThisBuilder& MenuItem::GetItem(BMenuItem*& _item) { _item = fMenuItem; return *this; } template typename MenuItem::ThisBuilder& MenuItem::SetEnabled(bool enabled) { fMenuItem->SetEnabled(enabled); return *this; } } // namespace BLayoutBuilder #endif // _LAYOUT_BUILDER_H