@@ -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); | |||||
} | } | ||||