PixelRect ButtonPanel::BottomLayout(PixelRect rc) { assert(!buttons.empty()); const UPixelScalar total_width = rc.right - rc.left; unsigned end = buttons.size(); while (end > 0) { unsigned start = end - 1; UPixelScalar max_width = Width(start); while (start > 0) { --start; UPixelScalar width = Width(start); UPixelScalar new_width = std::max(width, max_width); if ((end - start) * new_width > total_width) { ++start; break; } max_width = new_width; } rc = HorizontalRange(rc, start, end); end = start; } return rc; }
PixelRect ButtonPanel::BottomLayout(PixelRect rc) { assert(!buttons.empty()); const unsigned n_buttons = buttons.size(); const unsigned total_width = rc.GetWidth(); /* naive button distribution algorithm: distribute as many buttons as possible into each row; weakness: the last row may have only one button */ struct Row { unsigned start, end; constexpr unsigned size() const { return end - start; } }; StaticArray<Row, 8u> rows; for (unsigned i = 0; i < n_buttons;) { unsigned end = FitButtonRow(i, total_width); assert(end > i); auto &row = rows.append(); row.start = i; row.end = i = end; } assert(!rows.empty()); /* optimise the naive result: try to move buttons down until we see no further chance for improvement */ bool modified; do { modified = false; for (unsigned i = rows.size() - 1; i > 0; --i) { auto &dest_row = rows[i]; auto &src_row = rows[i - 1]; /* the upper row has many more buttons than the lower row */ if (dest_row.size() + 2 <= src_row.size()) { unsigned max_width = RangeMaxWidth(dest_row.start - 1, dest_row.end); unsigned row_width = (dest_row.size() + 1) * max_width; /* yes, we can move one button down */ if (row_width <= total_width) { --src_row.end; --dest_row.start; modified = true; } } } } while (modified); /* now do the actual layout based on row metadata */ for (int i = rows.size() - 1; i >= 0; --i) { const auto &row = rows[i]; rc = HorizontalRange(rc, row.start, row.end); } return rc; }