| @@ -1,19 +0,0 @@ | |||||
| #pragma once | |||||
| #include <widget/Widget.hpp> | |||||
| #include <ui/common.hpp> | |||||
| namespace rack { | |||||
| namespace ui { | |||||
| /** Positions children with a margin between the layout's box. */ | |||||
| struct MarginLayout : widget::Widget { | |||||
| math::Vec margin; | |||||
| void step() override; | |||||
| }; | |||||
| } // namespace ui | |||||
| } // namespace rack | |||||
| @@ -108,7 +108,7 @@ json_t* CableWidget::toJson() { | |||||
| void CableWidget::fromJson(json_t* rootJ) { | void CableWidget::fromJson(json_t* rootJ) { | ||||
| json_t* colorJ = json_object_get(rootJ, "color"); | json_t* colorJ = json_object_get(rootJ, "color"); | ||||
| if (colorJ) { | if (colorJ) { | ||||
| // In <=v0.6.0, patches used JSON objects. Just ignore them if so and use the existing cable color. | |||||
| // In <v0.6.0, cables used JSON objects instead of hex strings. Just ignore them if so and use the existing cable color. | |||||
| if (json_is_string(colorJ)) | if (json_is_string(colorJ)) | ||||
| color = color::fromHexString(json_string_value(colorJ)); | color = color::fromHexString(json_string_value(colorJ)); | ||||
| } | } | ||||
| @@ -9,7 +9,6 @@ | |||||
| #include <ui/MenuOverlay.hpp> | #include <ui/MenuOverlay.hpp> | ||||
| #include <ui/ScrollWidget.hpp> | #include <ui/ScrollWidget.hpp> | ||||
| #include <ui/SequentialLayout.hpp> | #include <ui/SequentialLayout.hpp> | ||||
| #include <ui/MarginLayout.hpp> | |||||
| #include <ui/Label.hpp> | #include <ui/Label.hpp> | ||||
| #include <ui/Slider.hpp> | #include <ui/Slider.hpp> | ||||
| #include <ui/TextField.hpp> | #include <ui/TextField.hpp> | ||||
| @@ -440,7 +439,7 @@ struct ModuleBrowser : widget::OpaqueWidget { | |||||
| ClearButton* clearButton; | ClearButton* clearButton; | ||||
| ui::ScrollWidget* modelScroll; | ui::ScrollWidget* modelScroll; | ||||
| ui::MarginLayout* modelMargin; | |||||
| widget::Widget* modelMargin; | |||||
| ui::SequentialLayout* modelContainer; | ui::SequentialLayout* modelContainer; | ||||
| std::string search; | std::string search; | ||||
| @@ -450,12 +449,12 @@ struct ModuleBrowser : widget::OpaqueWidget { | |||||
| std::map<plugin::Model*, float> prefilteredModelScores; | std::map<plugin::Model*, float> prefilteredModelScores; | ||||
| ModuleBrowser() { | ModuleBrowser() { | ||||
| float margin = 10; | |||||
| const float margin = 10; | |||||
| // Header | // Header | ||||
| headerLayout = new ui::SequentialLayout; | headerLayout = new ui::SequentialLayout; | ||||
| headerLayout->box.pos = math::Vec(0, 0); | headerLayout->box.pos = math::Vec(0, 0); | ||||
| headerLayout->box.size.y = BND_WIDGET_HEIGHT + 2 * margin; | |||||
| headerLayout->box.size.y = 0; | |||||
| headerLayout->margin = math::Vec(margin, margin); | headerLayout->margin = math::Vec(margin, margin); | ||||
| headerLayout->spacing = math::Vec(margin, margin); | headerLayout->spacing = math::Vec(margin, margin); | ||||
| addChild(headerLayout); | addChild(headerLayout); | ||||
| @@ -482,9 +481,10 @@ struct ModuleBrowser : widget::OpaqueWidget { | |||||
| clearButton->browser = this; | clearButton->browser = this; | ||||
| headerLayout->addChild(clearButton); | headerLayout->addChild(clearButton); | ||||
| widget::Widget* spacer1 = new widget::Widget; | |||||
| spacer1->box.size.x = 20; | |||||
| headerLayout->addChild(spacer1); | |||||
| // widget::Widget* spacer1 = new widget::Widget; | |||||
| // spacer1->box.size.x = 20; | |||||
| // spacer1->box.size.y = 0; | |||||
| // headerLayout->addChild(spacer1); | |||||
| SortButton* sortButton = new SortButton; | SortButton* sortButton = new SortButton; | ||||
| sortButton->box.size.x = 150; | sortButton->box.size.x = 150; | ||||
| @@ -507,11 +507,11 @@ struct ModuleBrowser : widget::OpaqueWidget { | |||||
| modelScroll->box.pos.y = BND_WIDGET_HEIGHT; | modelScroll->box.pos.y = BND_WIDGET_HEIGHT; | ||||
| addChild(modelScroll); | addChild(modelScroll); | ||||
| modelMargin = new ui::MarginLayout; | |||||
| modelMargin->margin = math::Vec(margin, 0); | |||||
| modelMargin = new widget::Widget; | |||||
| modelScroll->container->addChild(modelMargin); | modelScroll->container->addChild(modelMargin); | ||||
| modelContainer = new ui::SequentialLayout; | modelContainer = new ui::SequentialLayout; | ||||
| modelContainer->margin = math::Vec(margin, 0); | |||||
| modelContainer->spacing = math::Vec(margin, margin); | modelContainer->spacing = math::Vec(margin, margin); | ||||
| modelMargin->addChild(modelContainer); | modelMargin->addChild(modelContainer); | ||||
| @@ -558,10 +558,12 @@ struct ModuleBrowser : widget::OpaqueWidget { | |||||
| headerLayout->box.size.x = box.size.x; | headerLayout->box.size.x = box.size.x; | ||||
| const float margin = 10; | |||||
| modelScroll->box.pos = headerLayout->box.getBottomLeft(); | modelScroll->box.pos = headerLayout->box.getBottomLeft(); | ||||
| modelScroll->box.size = box.size.minus(modelScroll->box.pos); | modelScroll->box.size = box.size.minus(modelScroll->box.pos); | ||||
| modelMargin->box.size.x = modelScroll->box.size.x; | modelMargin->box.size.x = modelScroll->box.size.x; | ||||
| modelMargin->box.size.y = modelContainer->getChildrenBoundingBox().size.y + 2 * modelMargin->margin.y; | |||||
| modelMargin->box.size.y = modelContainer->box.size.y + margin; | |||||
| modelContainer->box.size.x = modelMargin->box.size.x - margin; | |||||
| OpaqueWidget::step(); | OpaqueWidget::step(); | ||||
| } | } | ||||
| @@ -1,20 +0,0 @@ | |||||
| #include <ui/MarginLayout.hpp> | |||||
| #include <vector> | |||||
| namespace rack { | |||||
| namespace ui { | |||||
| void MarginLayout::step() { | |||||
| Widget::step(); | |||||
| math::Rect childBox = box.zeroPos().grow(margin.neg()); | |||||
| for (Widget* child : children) { | |||||
| child->box = childBox; | |||||
| } | |||||
| } | |||||
| } // namespace ui | |||||
| } // namespace rack | |||||
| @@ -7,6 +7,8 @@ namespace rack { | |||||
| namespace ui { | namespace ui { | ||||
| /** We assume horizontal orientation in this file, but we can achieve vertical orientation just by swapping the axes. | |||||
| */ | |||||
| #define X(v) (orientation == HORIZONTAL_ORIENTATION ? (v).x : (v).y) | #define X(v) (orientation == HORIZONTAL_ORIENTATION ? (v).x : (v).y) | ||||
| #define Y(v) (orientation == HORIZONTAL_ORIENTATION ? (v).y : (v).x) | #define Y(v) (orientation == HORIZONTAL_ORIENTATION ? (v).y : (v).x) | ||||
| @@ -14,16 +16,13 @@ namespace ui { | |||||
| void SequentialLayout::step() { | void SequentialLayout::step() { | ||||
| Widget::step(); | Widget::step(); | ||||
| math::Rect bound; | |||||
| bound.pos = margin; | |||||
| bound.size = box.size.minus(margin.mult(2)); | |||||
| float boundWidth = X(box.size) - 2 * X(margin); | |||||
| // Sort widgets into rows (or columns if vertical) | // Sort widgets into rows (or columns if vertical) | ||||
| std::vector<widget::Widget*> row; | std::vector<widget::Widget*> row; | ||||
| math::Vec cursor = bound.pos; | |||||
| math::Vec cursor = margin; | |||||
| auto flushRow = [&]() { | auto flushRow = [&]() { | ||||
| // For center and right alignment, compute offset from the left margin | // For center and right alignment, compute offset from the left margin | ||||
| float offset = 0.f; | |||||
| if (alignment != LEFT_ALIGNMENT) { | if (alignment != LEFT_ALIGNMENT) { | ||||
| float rowWidth = 0.f; | float rowWidth = 0.f; | ||||
| for (widget::Widget* child : row) { | for (widget::Widget* child : row) { | ||||
| @@ -32,16 +31,15 @@ void SequentialLayout::step() { | |||||
| rowWidth -= X(spacing); | rowWidth -= X(spacing); | ||||
| if (alignment == CENTER_ALIGNMENT) | if (alignment == CENTER_ALIGNMENT) | ||||
| offset = (X(bound.size) - rowWidth) / 2; | |||||
| X(cursor) += (boundWidth - rowWidth) / 2; | |||||
| else if (alignment == RIGHT_ALIGNMENT) | else if (alignment == RIGHT_ALIGNMENT) | ||||
| offset = X(bound.size) - rowWidth; | |||||
| X(cursor) += boundWidth - rowWidth; | |||||
| } | } | ||||
| // Set positions of widgets | // Set positions of widgets | ||||
| float maxHeight = 0.f; | float maxHeight = 0.f; | ||||
| for (widget::Widget* child : row) { | for (widget::Widget* child : row) { | ||||
| child->box.pos = cursor; | child->box.pos = cursor; | ||||
| X(child->box.pos) += offset; | |||||
| X(cursor) += X(child->box.size) + X(spacing); | X(cursor) += X(child->box.size) + X(spacing); | ||||
| if (Y(child->box.size) > maxHeight) | if (Y(child->box.size) > maxHeight) | ||||
| @@ -50,20 +48,21 @@ void SequentialLayout::step() { | |||||
| row.clear(); | row.clear(); | ||||
| // Reset cursor to next line | // Reset cursor to next line | ||||
| X(cursor) = X(bound.pos); | |||||
| X(cursor) = X(margin); | |||||
| Y(cursor) += maxHeight + Y(spacing); | Y(cursor) += maxHeight + Y(spacing); | ||||
| }; | }; | ||||
| // Iterate through children until row is full | // Iterate through children until row is full | ||||
| float rowWidth = 0.0; | float rowWidth = 0.0; | ||||
| for (widget::Widget* child : children) { | for (widget::Widget* child : children) { | ||||
| // Skip invisible children | |||||
| if (!child->isVisible()) { | if (!child->isVisible()) { | ||||
| child->box.pos = math::Vec(); | child->box.pos = math::Vec(); | ||||
| continue; | continue; | ||||
| } | } | ||||
| // Should we wrap the widget now? | // Should we wrap the widget now? | ||||
| if (!row.empty() && rowWidth + X(child->box.size) > X(bound.size)) { | |||||
| if (!row.empty() && rowWidth + X(child->box.size) > boundWidth) { | |||||
| flushRow(); | flushRow(); | ||||
| rowWidth = 0.0; | rowWidth = 0.0; | ||||
| } | } | ||||
| @@ -76,6 +75,8 @@ void SequentialLayout::step() { | |||||
| if (!row.empty()) { | if (!row.empty()) { | ||||
| flushRow(); | flushRow(); | ||||
| } | } | ||||
| Y(box.size) = Y(cursor) - Y(spacing) + Y(margin); | |||||
| } | } | ||||