21 namespace visualization {
26 std::vector<int> CalcMajor(
const LayoutContext&
context,
27 const Widget::Constraints& constraints,
29 const std::vector<std::shared_ptr<Widget>>& children,
30 int* minor =
nullptr) {
31 std::vector<Size> preferred_sizes;
32 preferred_sizes.reserve(children.size());
33 for (
auto& child : children) {
34 preferred_sizes.push_back(
35 child->CalcPreferredSize(
context, constraints));
42 int num_other_maxgrow_items = 0;
43 std::vector<int> major;
44 major.reserve(preferred_sizes.size());
46 for (
auto& preferred : preferred_sizes) {
47 major.push_back(preferred.height);
49 num_other_maxgrow_items += 1;
51 other =
std::max(other, preferred.width);
55 for (
auto& preferred : preferred_sizes) {
56 major.push_back(preferred.width);
58 num_other_maxgrow_items += 1;
60 other =
std::max(other, preferred.height);
65 if (other == 0 && num_other_maxgrow_items > 0) {
75 std::vector<std::vector<std::shared_ptr<Widget>>> CalcColumns(
76 int num_cols,
const std::vector<std::shared_ptr<Widget>>& children) {
77 std::vector<std::vector<std::shared_ptr<Widget>>> columns(num_cols);
79 for (
auto& child : children) {
80 columns[col++].push_back(child);
81 if (col >= num_cols) {
88 std::vector<Size> CalcColumnSizes(
89 const std::vector<std::vector<std::shared_ptr<Widget>>>& columns,
91 const Widget::Constraints& constraints) {
92 std::vector<Size> sizes;
93 sizes.reserve(columns.size());
95 for (
auto& col : columns) {
97 for (
auto& widget : col) {
98 auto preferred = widget->CalcPreferredSize(
context, constraints);
100 h += preferred.height;
102 sizes.push_back(Size(w, h));
114 : left(horiz_px), top(vert_px), right(horiz_px), bottom(vert_px) {}
116 : left(left_px), top(top_px), right(right_px), bottom(bottom_px) {}
134 static const char spaces[21] =
" ";
135 const char* indent = spaces + (20 - 3 * depth);
137 std::cout <<
"[debug] " << indent <<
"Layout1D ("
139 <<
"): pref: (" << pref_total.
width <<
", " << pref_total.height
141 std::cout <<
"[debug] " << indent <<
"spacing: " << layout->impl_->spacing_
142 <<
", margins: (l:" << layout->impl_->margins_.left
143 <<
", t:" << layout->impl_->margins_.top
144 <<
", r:" << layout->impl_->margins_.right
145 <<
", b:" << layout->impl_->margins_.bottom <<
")" <<
std::endl;
146 for (
size_t i = 0; i < layout->
GetChildren().size(); ++i) {
148 auto pref = child->CalcPreferredSize(
context, constraints);
149 std::cout <<
"[debug] " << indent <<
"i: " << i <<
" (" << pref.width
150 <<
", " << pref.height <<
")" <<
std::endl;
156 VGrid* vgrid =
dynamic_cast<VGrid*
>(child.get());
158 const char* grid_indent = spaces + (20 - 3 * (depth + 1));
159 std::cout <<
"[debug] " << grid_indent
168 auto pref = e->CalcPreferredSize(
context, constraints);
169 std::cout <<
"[debug] " << grid_indent <<
"i: " << i <<
" ("
170 << pref.width <<
", " << pref.height <<
")"
188 Size Layout1D::Stretch::CalcPreferredSize(
196 const std::vector<std::shared_ptr<Widget>>& children)
199 impl_->spacing_ = spacing;
200 impl_->margins_ = margins;
217 return impl_->minor_axis_size_;
221 impl_->minor_axis_size_ =
size;
229 std::vector<int> major =
232 minor = impl_->minor_axis_size_;
235 int total_spacing = impl_->spacing_ *
std::max(0,
int(major.size()) - 1);
237 for (
auto&
size : major) {
241 if (impl_->dir_ ==
VERT) {
242 return Size(minor + impl_->margins_.GetHoriz(),
243 major_size + impl_->margins_.GetVert() + total_spacing);
245 return Size(major_size + impl_->margins_.GetHoriz() + total_spacing,
246 minor + impl_->margins_.GetVert());
253 if (impl_->dir_ ==
VERT) {
255 frame.width - impl_->margins_.left - impl_->margins_.right;
258 frame.height - impl_->margins_.top - impl_->margins_.bottom;
261 std::vector<int> major =
262 CalcMajor(
context, constraints, impl_->dir_, children,
nullptr);
263 int total = 0, num_stretch = 0, num_grow = 0;
264 for (
auto& mj : major) {
274 if (impl_->dir_ ==
VERT) {
275 frame_size =
frame.height - impl_->margins_.GetVert();
277 frame_size =
frame.width - impl_->margins_.GetHoriz();
279 int total_spacing = impl_->spacing_ *
std::max(0,
int(major.size()) - 1);
280 auto total_extra = frame_size - total - total_spacing;
281 if (num_stretch > 0 && frame_size > total) {
282 auto stretch = total_extra / num_stretch;
283 auto leftover_stretch = total_extra - stretch * num_stretch;
284 for (
size_t i = 0; i < major.size(); ++i) {
287 if (leftover_stretch > 0) {
289 leftover_stretch -= 1;
293 }
else if (frame_size < total) {
294 int n_shrinkable = num_grow;
295 if (impl_->dir_ ==
VERT) {
297 if (std::dynamic_pointer_cast<ScrollableVert>(child)) {
302 if (n_shrinkable > 0) {
303 auto total_excess = total - (frame_size - total_spacing);
304 auto excess = total_excess / n_shrinkable;
305 auto leftover = total_excess - excess * num_stretch;
306 for (
size_t i = 0; i < major.size(); ++i) {
308 (impl_->dir_ ==
VERT &&
309 std::dynamic_pointer_cast<ScrollableVert>(
321 int x =
frame.GetLeft() + impl_->margins_.left;
322 int y =
frame.GetTop() + impl_->margins_.top;
323 if (impl_->dir_ ==
VERT) {
324 int minor =
frame.width - impl_->margins_.GetHoriz();
325 for (
size_t i = 0; i < children.size(); ++i) {
328 children[i]->SetFrame(
Rect(x, y, minor, h));
329 y += major[i] + impl_->spacing_;
332 int minor =
frame.height - impl_->margins_.GetVert();
333 for (
size_t i = 0; i < children.size(); ++i) {
334 children[i]->SetFrame(
Rect(x, y, major[i], minor));
335 x += major[i] + impl_->spacing_;
344 return std::make_shared<Layout1D::Fixed>(
size,
VERT);
348 return std::make_shared<Layout1D::Stretch>();
354 :
Layout1D(VERT, spacing, margins, {}) {}
358 const std::vector<std::shared_ptr<Widget>>& children)
359 :
Layout1D(VERT, spacing, margins, children) {}
391 static int g_next_id = 1;
394 impl_->id_ = impl_->text_ +
"##collapsing_" +
std::to_string(g_next_id++);
406 ImGui::PushFont((ImFont*)
context.fonts.GetFont(impl_->font_id_));
407 auto* font = ImGui::GetFont();
408 auto padding = ImGui::GetStyle().FramePadding;
409 int text_height = int(
410 std::ceil(ImGui::GetTextLineHeightWithSpacing() + 2 * padding.y));
412 int(
std::ceil(font->CalcTextSizeA(font->FontSize, FLT_MAX, FLT_MAX,
413 impl_->text_.c_str())
418 if (!impl_->is_open_) {
423 return Size(
std::max(text_width, pref.width) + margins.GetHoriz(),
424 text_height + pref.height + margins.GetVert());
428 ImGui::PushFont((ImFont*)
context.fonts.GetFont(impl_->font_id_));
429 auto padding = ImGui::GetStyle().FramePadding;
430 int text_height = int(
431 std::ceil(ImGui::GetTextLineHeightWithSpacing() + 2 * padding.y));
433 auto orig_top = margins.top;
434 margins.top = orig_top + text_height;
439 margins.top = orig_top;
444 bool was_open = impl_->is_open_;
447 ImGui::SetCursorScreenPos(
448 ImVec2(
float(
frame.x),
float(
frame.y) - ImGui::GetScrollY()));
449 ImGui::PushItemWidth(
float(
frame.width));
451 auto padding = ImGui::GetStyle().FramePadding;
452 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, padding.y));
453 ImGui::PushStyleColor(ImGuiCol_HeaderHovered,
455 ImGui::PushStyleColor(ImGuiCol_HeaderActive,
458 ImGui::SetNextItemOpen(impl_->is_open_);
459 ImGui::PushFont((ImFont*)
context.fonts.GetFont(impl_->font_id_));
460 bool node_clicked = ImGui::TreeNode(impl_->id_.c_str());
465 impl_->is_open_ =
true;
467 impl_->is_open_ =
false;
470 ImGui::PopStyleColor(2);
471 ImGui::PopStyleVar();
472 ImGui::PopItemWidth();
474 if (impl_->is_open_ != was_open) {
494 const std::vector<std::shared_ptr<Widget>>& children)
496 static int g_next_id = 1;
497 impl_->id_ = g_next_id++;
504 ImGui::SetCursorScreenPos(
505 ImVec2(
float(
frame.x),
float(
frame.y) - ImGui::GetScrollY()));
506 ImGui::PushStyleColor(ImGuiCol_FrameBg,
507 ImGui::GetStyleColorVec4(ImGuiCol_WindowBg));
508 ImGui::PushStyleColor(ImGuiCol_Border,
510 ImGui::PushStyleColor(ImGuiCol_BorderShadow,
513 ImGui::BeginChildFrame(impl_->id_, ImVec2(
frame.width,
frame.height));
515 ImGui::EndChildFrame();
517 ImGui::PopStyleColor(3);
524 return std::make_shared<Layout1D::Fixed>(
size,
HORIZ);
528 return std::make_shared<Layout1D::Stretch>();
532 return std::make_shared<Horiz>(
534 std::vector<std::shared_ptr<Widget>>(
541 :
Layout1D(HORIZ, spacing, margins, {}) {}
545 const std::vector<std::shared_ptr<Widget>>& children)
546 :
Layout1D(HORIZ, spacing, margins, children) {}
565 impl_->num_cols_ = num_cols;
566 impl_->spacing_ = spacing;
567 impl_->margins_ = margins;
580 auto columns = CalcColumns(impl_->num_cols_,
GetChildren());
581 auto column_sizes = CalcColumnSizes(columns,
context, constraints);
584 for (
size_t i = 0; i < column_sizes.size(); ++i) {
585 auto& sz = column_sizes[i];
587 auto v_spacing = (int(columns[i].
size()) - 1) * impl_->spacing_;
590 width += (int(column_sizes.size()) - 1) * impl_->spacing_;
595 width = impl_->preferred_width_;
597 width =
width + impl_->margins_.left + impl_->margins_.right;
600 return Size(
width,
height + impl_->margins_.top + impl_->margins_.bottom);
605 const int layout_width =
606 frame.width - impl_->margins_.left - impl_->margins_.right;
608 constraints.
width = layout_width;
610 auto columns = CalcColumns(impl_->num_cols_,
GetChildren());
611 auto column_sizes = CalcColumnSizes(columns,
context, constraints);
617 int grow_size = constraints.
width;
618 int wanted_width = 0;
619 int total_not_growing_width = 0;
621 for (
auto& sz : column_sizes) {
622 wanted_width += sz.width;
623 if (sz.width < grow_size) {
624 total_not_growing_width += sz.width;
629 if (wanted_width > layout_width && num_growing > 0) {
630 int total_spacing = (int(columns.size()) - 1) * impl_->spacing_;
632 (layout_width - total_spacing - total_not_growing_width) /
634 if (growing_size < 0) {
635 growing_size = layout_width / num_growing;
637 for (
auto& sz : column_sizes) {
638 if (sz.width >= grow_size) {
639 sz.width = growing_size;
644 int leftHalf = int(
std::floor(
float(impl_->spacing_) / 2.0));
645 int rightHalf = int(
std::ceil(
float(impl_->spacing_) / 2.0));
646 for (
size_t i = 0; i < column_sizes.size() - 1; ++i) {
647 column_sizes[i].width -= leftHalf;
648 column_sizes[i + 1].width -= rightHalf;
653 int x =
frame.GetLeft() + impl_->margins_.left;
654 for (
size_t i = 0; i < columns.size(); ++i) {
656 constraints.
width = column_sizes[i].width;
657 int y =
frame.GetTop() + impl_->margins_.top;
658 for (
auto& w : columns[i]) {
659 auto preferred = w->CalcPreferredSize(
context, constraints);
660 w->SetFrame(
Rect(x, y, column_sizes[i].
width, preferred.height));
661 y += preferred.height + impl_->spacing_;
663 x += column_sizes[i].width + impl_->spacing_;
static constexpr FontId DEFAULT_FONT_ID
Identifier for font used by default for all UI elements.
std::string GetText() const
void SetIsOpen(bool is_open)
CollapsableVert(const char *text)
void Layout(const LayoutContext &context) override
virtual ~CollapsableVert()
void SetFontId(FontId font_id)
bool GetIsOpen()
Returns true if open and false if collapsed.
Widget::DrawResult Draw(const DrawContext &context) override
void SetText(const char *text)
Size CalcPreferredSize(const LayoutContext &context, const Constraints &constraints) const override
void SetPreferredHeight(int h)
int GetPreferredHeight() const
static std::shared_ptr< Layout1D::Fixed > MakeFixed(int size)
static std::shared_ptr< Layout1D::Stretch > MakeStretch()
static std::shared_ptr< Horiz > MakeCentered(std::shared_ptr< Widget > w)
Size CalcPreferredSize(const LayoutContext &context, const Constraints &constraints) const override
int GetMinorAxisPreferredSize() const
void SetMinorAxisPreferredSize(int size)
Size CalcPreferredSize(const LayoutContext &context, const Constraints &constraints) const override
void AddFixed(int size)
Adds a fixed number of pixels after the previously added widget.
void SetSpacing(int spacing)
Margins & GetMutableMargins()
Layout1D(Dir dir, int spacing, const Margins &margins, const std::vector< std::shared_ptr< Widget >> &children)
static void debug_PrintPreferredSizes(Layout1D *layout, const LayoutContext &context, const Constraints &constraints, int depth=0)
const Margins & GetMargins() const
void SetMargins(const Margins &margins)
void Layout(const LayoutContext &context) override
void Layout(const LayoutContext &context) override
void SetPreferredWidth(int w)
Size CalcPreferredSize(const LayoutContext &context, const Constraints &constraints) const override
const Margins & GetMargins() const
VGrid(int num_cols, int spacing=0, const Margins &margins=Margins())
int GetPreferredWidth() const
Lays out widgets vertically.
static std::shared_ptr< Layout1D::Stretch > MakeStretch()
int GetPreferredWidth() const
void SetPreferredWidth(int w)
static std::shared_ptr< Layout1D::Fixed > MakeFixed(int size)
QTextStream & endl(QTextStream &stream)
MiniVec< float, N > floor(const MiniVec< float, N > &a)
MiniVec< float, N > ceil(const MiniVec< float, N > &a)
ImVec4 colorToImgui(const Color &color)
Generic file read and write utility for python interface.
std::string to_string(const T &n)
int GetVert() const
Convenience function that returns top + bottom.
int GetHoriz() const
Convenience function that returns left + right.