PixelRect ButtonPanel::VerticalRange(PixelRect rc, unsigned start, unsigned end) { const unsigned n = end - start; assert(n > 0); const UPixelScalar width = RangeMaxWidth(start, end); const UPixelScalar total_height = rc.bottom - rc.top; const UPixelScalar max_height = n * Layout::GetMaximumControlHeight(); const UPixelScalar row_height = std::min(total_height, max_height) / n; PixelRect button_rc = { rc.left, rc.top, PixelScalar(rc.left + width), PixelScalar(rc.top + row_height), }; rc.left += width; for (unsigned i = start; i < end; ++i) { buttons[i]->Move(button_rc); button_rc.top = button_rc.bottom; button_rc.bottom += row_height; } 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; }