@@ -22,10 +22,8 @@ struct SequentialLayout : virtual Widget { | |||
RIGHT_ALIGNMENT, | |||
}; | |||
Alignment alignment = LEFT_ALIGNMENT; | |||
/** Space outside elements */ | |||
float margin = 0.0; | |||
/** Space between adjacent elements */ | |||
float padding = 0.0; | |||
float spacing = 0.0; | |||
void step() override; | |||
}; | |||
@@ -45,6 +45,20 @@ struct FavoriteRadioButton : RadioButton { | |||
}; | |||
struct SeparatorItem : OpaqueWidget { | |||
SeparatorItem() { | |||
box.size.y = BND_WIDGET_HEIGHT; | |||
} | |||
void setText(std::string text) { | |||
clearChildren(); | |||
Label *label = Widget::create<Label>(Vec(0, 0)); | |||
label->text = text; | |||
addChild(label); | |||
} | |||
}; | |||
struct BrowserListItem : OpaqueWidget { | |||
bool selected = false; | |||
@@ -78,11 +92,13 @@ struct BrowserListItem : OpaqueWidget { | |||
}; | |||
struct ModelItem : BrowserListItem { | |||
Model *model; | |||
Label *manufacturerLabel; | |||
void setModel(Model *model) { | |||
clearChildren(); | |||
assert(model); | |||
this->model = model; | |||
@@ -95,9 +111,8 @@ struct ModelItem : BrowserListItem { | |||
manufacturerLabel->text = model->manufacturer; | |||
addChild(manufacturerLabel); | |||
SequentialLayout *layout2 = Widget::create<SequentialLayout>(Vec(0, BND_WIDGET_HEIGHT)); | |||
layout2->margin = 7; | |||
layout2->padding = 10; | |||
SequentialLayout *layout2 = Widget::create<SequentialLayout>(Vec(7, BND_WIDGET_HEIGHT)); | |||
layout2->spacing = 10; | |||
addChild(layout2); | |||
FavoriteRadioButton *favoriteButton = new FavoriteRadioButton(); | |||
@@ -138,14 +153,17 @@ struct ManufacturerItem : BrowserListItem { | |||
Label *manufacturerLabel; | |||
ManufacturerItem() { | |||
manufacturerLabel = Widget::create<Label>(Vec(0, 0)); | |||
manufacturerLabel->text = "Show all modules"; | |||
addChild(manufacturerLabel); | |||
} | |||
void setManufacturer(std::string manufacturer) { | |||
clearChildren(); | |||
this->manufacturer = manufacturer; | |||
manufacturerLabel->text = manufacturer; | |||
manufacturerLabel = Widget::create<Label>(Vec(0, 0)); | |||
if (manufacturer.empty()) | |||
manufacturerLabel->text = "Show all modules"; | |||
else | |||
manufacturerLabel->text = manufacturer; | |||
addChild(manufacturerLabel); | |||
} | |||
void step() override { | |||
@@ -163,34 +181,40 @@ struct BrowserList : List { | |||
// If we have zero children, this result doesn't matter anyway. | |||
selected = clamp(selected, 0, children.size() - 1); | |||
int i = 0; | |||
for (Widget *w : children) { | |||
BrowserListItem *item = dynamic_cast<BrowserListItem*>(w); | |||
for (Widget *child : children) { | |||
BrowserListItem *item = dynamic_cast<BrowserListItem*>(child); | |||
if (item) { | |||
item->selected = (i == selected); | |||
i++; | |||
} | |||
i++; | |||
} | |||
List::step(); | |||
} | |||
void selectChild(Widget *child) { | |||
void selectItem(Widget *w) { | |||
int i = 0; | |||
for (Widget *w : children) { | |||
if (w == child) { | |||
selected = i; | |||
break; | |||
for (Widget *child : children) { | |||
BrowserListItem *item = dynamic_cast<BrowserListItem*>(child); | |||
if (item) { | |||
if (child == w) { | |||
selected = i; | |||
break; | |||
} | |||
i++; | |||
} | |||
i++; | |||
} | |||
} | |||
Widget *getSelectedChild() { | |||
BrowserListItem *getSelectedItem() { | |||
int i = 0; | |||
for (Widget *w : children) { | |||
if (i == selected) { | |||
return w; | |||
for (Widget *child : children) { | |||
BrowserListItem *item = dynamic_cast<BrowserListItem*>(child); | |||
if (item) { | |||
if (i == selected) { | |||
return item; | |||
} | |||
i++; | |||
} | |||
i++; | |||
} | |||
return NULL; | |||
} | |||
@@ -241,6 +265,11 @@ struct ModuleBrowser : OpaqueWidget { | |||
moduleList->selected = 0; | |||
// Favorites | |||
{ | |||
SeparatorItem *item = new SeparatorItem(); | |||
item->setText("Favorites"); | |||
moduleList->addChild(item); | |||
} | |||
for (Model *model : sFavoriteModels) { | |||
if ((manufacturerFilter.empty() || manufacturerFilter == model->manufacturer) && isModelMatch(model, search)) { | |||
ModelItem *item = new ModelItem(); | |||
@@ -250,6 +279,11 @@ struct ModuleBrowser : OpaqueWidget { | |||
} | |||
// Manufacturers | |||
{ | |||
SeparatorItem *item = new SeparatorItem(); | |||
item->setText("Manufacturers"); | |||
moduleList->addChild(item); | |||
} | |||
if (manufacturerFilter.empty()) { | |||
// Collect all manufacturers | |||
std::set<std::string> manufacturers; | |||
@@ -272,10 +306,16 @@ struct ModuleBrowser : OpaqueWidget { | |||
else { | |||
// Dummy manufacturer for clearing manufacturer filter | |||
ManufacturerItem *item = new ManufacturerItem(); | |||
item->setManufacturer(""); | |||
moduleList->addChild(item); | |||
} | |||
// Models | |||
{ | |||
SeparatorItem *item = new SeparatorItem(); | |||
item->setText("Modules"); | |||
moduleList->addChild(item); | |||
} | |||
for (Plugin *plugin : gPlugins) { | |||
for (Model *model : plugin->models) { | |||
if ((manufacturerFilter.empty() || manufacturerFilter == model->manufacturer) && isModelMatch(model, search)) { | |||
@@ -327,7 +367,7 @@ void FavoriteRadioButton::onAction(EventAction &e) { | |||
void BrowserListItem::onMouseEnter(EventMouseEnter &e) { | |||
BrowserList *list = getAncestorOfType<BrowserList>(); | |||
list->selectChild(this); | |||
list->selectItem(this); | |||
} | |||
void SearchModuleField::onTextChange() { | |||
@@ -350,8 +390,7 @@ void SearchModuleField::onKey(EventKey &e) { | |||
e.consumed = true; | |||
} break; | |||
case GLFW_KEY_ENTER: { | |||
Widget *w = moduleBrowser->moduleList->getSelectedChild(); | |||
BrowserListItem *item = dynamic_cast<BrowserListItem*>(w); | |||
BrowserListItem *item = moduleBrowser->moduleList->getSelectedItem(); | |||
if (item) { | |||
item->doAction(); | |||
e.consumed = true; | |||
@@ -118,12 +118,6 @@ void RackScene::onHoverKey(EventHoverKey &e) { | |||
e.consumed = true; | |||
} | |||
} break; | |||
case GLFW_KEY_R: { | |||
if (windowIsModPressed() && !windowIsShiftPressed()) { | |||
gRackWidget->revert(); | |||
e.consumed = true; | |||
} | |||
} break; | |||
case GLFW_KEY_ENTER: { | |||
appModuleBrowserCreate(); | |||
e.consumed = true; | |||
@@ -100,96 +100,64 @@ struct EngineSampleRateChoice : ChoiceButton { | |||
Toolbar::Toolbar() { | |||
float margin = 5; | |||
box.size.y = BND_WIDGET_HEIGHT + 2*margin; | |||
float xPos = 0; | |||
xPos += margin; | |||
{ | |||
ChoiceButton *fileChoice = new FileChoice(); | |||
fileChoice->box.pos = Vec(xPos, margin); | |||
fileChoice->box.size.x = 100; | |||
fileChoice->text = "File"; | |||
addChild(fileChoice); | |||
xPos += fileChoice->box.size.x; | |||
} | |||
xPos += margin; | |||
{ | |||
EngineSampleRateChoice *srChoice = new EngineSampleRateChoice(); | |||
srChoice->box.pos = Vec(xPos, margin); | |||
srChoice->box.size.x = 100; | |||
addChild(srChoice); | |||
xPos += srChoice->box.size.x; | |||
} | |||
xPos += margin; | |||
{ | |||
wireOpacitySlider = new Slider(); | |||
wireOpacitySlider->box.pos = Vec(xPos, margin); | |||
wireOpacitySlider->box.size.x = 150; | |||
wireOpacitySlider->label = "Cable opacity"; | |||
wireOpacitySlider->precision = 0; | |||
wireOpacitySlider->unit = "%"; | |||
wireOpacitySlider->setLimits(0.0, 100.0); | |||
wireOpacitySlider->setDefaultValue(50.0); | |||
addChild(wireOpacitySlider); | |||
xPos += wireOpacitySlider->box.size.x; | |||
} | |||
xPos += margin; | |||
{ | |||
wireTensionSlider = new Slider(); | |||
wireTensionSlider->box.pos = Vec(xPos, margin); | |||
wireTensionSlider->box.size.x = 150; | |||
wireTensionSlider->label = "Cable tension"; | |||
wireTensionSlider->unit = ""; | |||
wireTensionSlider->setLimits(0.0, 1.0); | |||
wireTensionSlider->setDefaultValue(0.5); | |||
addChild(wireTensionSlider); | |||
xPos += wireTensionSlider->box.size.x; | |||
} | |||
xPos += margin; | |||
{ | |||
struct ZoomSlider : Slider { | |||
void onAction(EventAction &e) override { | |||
Slider::onAction(e); | |||
gRackScene->zoomWidget->setZoom(roundf(value) / 100.0); | |||
} | |||
}; | |||
zoomSlider = new ZoomSlider(); | |||
zoomSlider->box.pos = Vec(xPos, margin); | |||
zoomSlider->box.size.x = 150; | |||
zoomSlider->precision = 0; | |||
zoomSlider->label = "Zoom"; | |||
zoomSlider->unit = "%"; | |||
zoomSlider->setLimits(25.0, 200.0); | |||
zoomSlider->setDefaultValue(100.0); | |||
addChild(zoomSlider); | |||
xPos += zoomSlider->box.size.x; | |||
} | |||
xPos += margin; | |||
/* | |||
{ | |||
cpuUsageButton = new RadioButton(); | |||
cpuUsageButton->box.pos = Vec(xPos, margin); | |||
cpuUsageButton->box.size.x = 100; | |||
cpuUsageButton->label = "CPU usage"; | |||
addChild(cpuUsageButton); | |||
xPos += cpuUsageButton->box.size.x; | |||
} | |||
xPos += margin; | |||
*/ | |||
box.size.y = BND_WIDGET_HEIGHT + 2*5; | |||
SequentialLayout *layout = new SequentialLayout(); | |||
layout->box.pos = Vec(5, 5); | |||
layout->spacing = 5; | |||
addChild(layout); | |||
ChoiceButton *fileChoice = new FileChoice(); | |||
fileChoice->box.size.x = 100; | |||
fileChoice->text = "File"; | |||
layout->addChild(fileChoice); | |||
EngineSampleRateChoice *srChoice = new EngineSampleRateChoice(); | |||
srChoice->box.size.x = 100; | |||
layout->addChild(srChoice); | |||
wireOpacitySlider = new Slider(); | |||
wireOpacitySlider->box.size.x = 150; | |||
wireOpacitySlider->label = "Cable opacity"; | |||
wireOpacitySlider->precision = 0; | |||
wireOpacitySlider->unit = "%"; | |||
wireOpacitySlider->setLimits(0.0, 100.0); | |||
wireOpacitySlider->setDefaultValue(50.0); | |||
layout->addChild(wireOpacitySlider); | |||
wireTensionSlider = new Slider(); | |||
wireTensionSlider->box.size.x = 150; | |||
wireTensionSlider->label = "Cable tension"; | |||
wireTensionSlider->unit = ""; | |||
wireTensionSlider->setLimits(0.0, 1.0); | |||
wireTensionSlider->setDefaultValue(0.5); | |||
layout->addChild(wireTensionSlider); | |||
struct ZoomSlider : Slider { | |||
void onAction(EventAction &e) override { | |||
Slider::onAction(e); | |||
gRackScene->zoomWidget->setZoom(roundf(value) / 100.0); | |||
} | |||
}; | |||
zoomSlider = new ZoomSlider(); | |||
zoomSlider->box.size.x = 150; | |||
zoomSlider->precision = 0; | |||
zoomSlider->label = "Zoom"; | |||
zoomSlider->unit = "%"; | |||
zoomSlider->setLimits(25.0, 200.0); | |||
zoomSlider->setDefaultValue(100.0); | |||
layout->addChild(zoomSlider); | |||
/* | |||
cpuUsageButton = new RadioButton(); | |||
cpuUsageButton->box.size.x = 100; | |||
cpuUsageButton->label = "CPU usage"; | |||
layout->addChild(cpuUsageButton); | |||
*/ | |||
#if defined(RELEASE) | |||
{ | |||
Widget *pluginManager = new PluginManagerWidget(); | |||
pluginManager->box.pos = Vec(xPos, margin); | |||
addChild(pluginManager); | |||
xPos += pluginManager->box.size.x; | |||
} | |||
Widget *pluginManager = new PluginManagerWidget(); | |||
layout->addChild(pluginManager); | |||
#endif | |||
} | |||
@@ -7,7 +7,7 @@ namespace rack { | |||
void SequentialLayout::step() { | |||
Widget::step(); | |||
float offset = margin; | |||
float offset = 0.0; | |||
for (Widget *child : children) { | |||
if (!child->visible) | |||
continue; | |||
@@ -15,7 +15,7 @@ void SequentialLayout::step() { | |||
(orientation == HORIZONTAL_ORIENTATION ? child->box.pos.x : child->box.pos.y) = offset; | |||
// Increment by size | |||
offset += (orientation == HORIZONTAL_ORIENTATION ? child->box.size.x : child->box.size.y); | |||
offset += padding; | |||
offset += spacing; | |||
} | |||
// We're done if left aligned | |||
@@ -23,8 +23,7 @@ void SequentialLayout::step() { | |||
return; | |||
// Adjust positions based on width of the layout itself | |||
offset -= padding; | |||
offset += margin; | |||
offset -= spacing; | |||
if (alignment == RIGHT_ALIGNMENT) | |||
offset -= (orientation == HORIZONTAL_ORIENTATION ? box.size.x : box.size.y); | |||
else if (alignment == CENTER_ALIGNMENT) | |||