diff options
Diffstat (limited to 'dggui/layout.cc')
-rw-r--r-- | dggui/layout.cc | 386 |
1 files changed, 386 insertions, 0 deletions
diff --git a/dggui/layout.cc b/dggui/layout.cc new file mode 100644 index 0000000..61e4f77 --- /dev/null +++ b/dggui/layout.cc @@ -0,0 +1,386 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * layout.cc + * + * Sat Mar 21 15:12:36 CET 2015 + * Copyright 2015 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of DrumGizmo. + * + * DrumGizmo is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * DrumGizmo is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with DrumGizmo; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#include "layout.h" + +#include "widget.h" + +#include <algorithm> + +namespace GUI +{ + +LayoutItem::LayoutItem() + : parent(nullptr) +{ +} + +LayoutItem::~LayoutItem() +{ + setLayoutParent(nullptr); // Will disconnect from layout if any. +} + +void LayoutItem::setLayoutParent(Layout* p) +{ + if(this->parent) + { + this->parent->removeItem(this); + } + + this->parent = p; +} + +Layout::Layout(LayoutItem* parent) : parent(parent) +{ + auto widget = dynamic_cast<Widget*>(parent); + if(widget) + { + CONNECT(widget, sizeChangeNotifier, this, &Layout::sizeChanged); + } +} + +void Layout::addItem(LayoutItem* item) +{ + items.push_back(item); + item->setLayoutParent(this); + layout(); +} + +void Layout::removeItem(LayoutItem* item) +{ + auto new_end = std::remove(items.begin(), items.end(), item); + items.erase(new_end, items.end()); + + layout(); +} + +void Layout::sizeChanged(int width, int height) +{ + layout(); +} + +// +// BoxLayout +// + +BoxLayout::BoxLayout(LayoutItem* parent) : Layout(parent) +{ +} + +void BoxLayout::setResizeChildren(bool resizeChildren) +{ + this->resizeChildren = resizeChildren; + layout(); +} + +void BoxLayout::setSpacing(size_t spacing) +{ + this->spacing = spacing; + layout(); +} + +// +// VBoxLayout +// + +VBoxLayout::VBoxLayout(LayoutItem* parent) + : BoxLayout(parent) + , align(HAlignment::center) +{ +} + +void VBoxLayout::layout() +{ + size_t y = 0; + size_t w = parent->width(); + // size_t h = parent->height() / items.size(); + + LayoutItemList::iterator i = items.begin(); + while(i != items.end()) + { + LayoutItem* item = *i; + + if(resizeChildren) + { + auto num_items = items.size(); + auto empty_space = (num_items - 1) * spacing; + auto available_space = parent->height(); + + if(available_space >= empty_space) + { + auto item_height = (available_space - empty_space) / num_items; + item->resize(w, item_height); + } + else + { + // TODO: Should this case be handled differently? + item->resize(w, 0); + } + } + + size_t x = 0; + switch(align) + { + case HAlignment::left: + x = 0; + break; + case HAlignment::center: + x = (w / 2) - (item->width() / 2); + break; + case HAlignment::right: + x = w - item->width(); + break; + } + + item->move(x, y); + y += item->height() + spacing; + ++i; + } +} + +void VBoxLayout::setHAlignment(HAlignment alignment) +{ + align = alignment; +} + +// +// HBoxLayout +// + +HBoxLayout::HBoxLayout(LayoutItem* parent) + : BoxLayout(parent) + , align(VAlignment::center) +{ +} + +void HBoxLayout::layout() +{ + if(items.empty()) + { + return; + } + + // size_t w = parent->width() / items.size(); + size_t h = parent->height(); + size_t x = 0; + + LayoutItemList::iterator i = items.begin(); + while(i != items.end()) + { + LayoutItem* item = *i; + if(resizeChildren) + { + auto num_items = items.size(); + auto empty_space = (num_items - 1) * spacing; + auto available_space = parent->width(); + + if(available_space >= empty_space) + { + auto item_width = (available_space - empty_space) / num_items; + item->resize(item_width, h); + } + else + { + // TODO: Should this case be handled differently? + item->resize(0, h); + } + + item->move(x, 0); + } + else + { + size_t y = 0; + switch(align) + { + case VAlignment::top: + y = 0; + break; + case VAlignment::center: + y = (h / 2) - (item->height() / 2); + break; + case VAlignment::bottom: + y = h - item->height(); + break; + } + + int diff = 0; // w - item->width(); + item->move(x + diff / 2, y); + } + x += item->width() + spacing; + ++i; + } +} + +void HBoxLayout::setVAlignment(VAlignment alignment) +{ + align = alignment; +} + +// +// GridLayout +// + +GridLayout::GridLayout(LayoutItem* parent, std::size_t number_of_columns, + std::size_t number_of_rows) + : BoxLayout(parent) + , number_of_columns(number_of_columns) + , number_of_rows(number_of_rows) +{ +} + +void GridLayout::removeItem(LayoutItem* item) +{ + // manually remove from grid_ranges as remove_if doesn't work on an + // unordered_map. + auto it = grid_ranges.begin(); + while(it != grid_ranges.end()) + { + if(it->first == item) + { + it = grid_ranges.erase(it); + } + else + { + ++it; + } + } + + Layout::removeItem(item); +} + +void GridLayout::layout() +{ + if(grid_ranges.empty()) + { + return; + } + + // Calculate cell sizes + auto cell_size = calculateCellSize(); + + for(auto const& pair : grid_ranges) + { + auto& item = *pair.first; + auto const& range = pair.second; + + moveAndResize(item, range, cell_size); + } +} + +void GridLayout::setPosition(LayoutItem* item, GridRange const& range) +{ + grid_ranges[item] = range; +} + +int GridLayout::lastUsedRow(int column) const +{ + int last_row = -1; + + for (auto const& grid_range : grid_ranges) + { + auto const& range = grid_range.second; + if (column >= range.column_begin && column < range.column_end) + { + last_row = std::max(last_row, range.row_end - 1); + } + } + + return last_row; +} + +int GridLayout::lastUsedColumn(int row) const +{ + int last_column = -1; + + for (auto const& grid_range : grid_ranges) + { + auto const& range = grid_range.second; + if (row >= range.row_begin && row < range.row_end) + { + last_column = std::max(last_column, range.column_end - 1); + } + } + + return last_column; + +} + +auto GridLayout::calculateCellSize() const -> CellSize +{ + auto empty_width = (number_of_columns - 1) * spacing; + auto available_width = parent->width(); + auto empty_height = (number_of_rows - 1) * spacing; + auto available_height = parent->height(); + + CellSize cell_size; + if(available_width > empty_width && available_height > empty_height) + { + cell_size.width = (available_width - empty_width) / number_of_columns; + cell_size.height = (available_height - empty_height) / number_of_rows; + } + else + { + cell_size.width = 0; + cell_size.height = 0; + } + + return cell_size; +} + +void GridLayout::moveAndResize( + LayoutItem& item, GridRange const& range, CellSize cell_size) const +{ + std::size_t x = range.column_begin * (cell_size.width + spacing); + std::size_t y = range.row_begin * (cell_size.height + spacing); + + std::size_t column_count = (range.column_end - range.column_begin); + std::size_t row_count = (range.row_end - range.row_begin); + std::size_t width = column_count * (cell_size.width + spacing) - spacing; + std::size_t height = row_count * (cell_size.height + spacing) - spacing; + + if(resizeChildren) + { + item.move(x, y); + + if(cell_size.width * cell_size.height != 0) + { + item.resize(width, height); + } + else + { + item.resize(0, 0); + } + } + else + { + auto x_new = (item.width() > width) ? x : x + (width - item.width()) / 2; + auto y_new = (item.height() > height) ? y : y + (height - item.height()) / 2; + + item.move(x_new, y_new); + } +} + +} // GUI:: |