@@ -2,6 +2,9 @@ | |||||
In this document, Ctrl means Cmd on Mac. | In this document, Ctrl means Cmd on Mac. | ||||
### 2.5.2 (in development) | |||||
- Add configurable cable color labels. | |||||
### 2.5.1 (2024-04-16) | ### 2.5.1 (2024-04-16) | ||||
- Remember the last selected cable color from the port's "New cable" menu item. When "Auto-rotate cables" is enabled, the next color in the rotation is used. | - Remember the last selected cable color from the port's "New cable" menu item. When "Auto-rotate cables" is enabled, the next color in the rotation is used. | ||||
- Fix crash when modules such as VCV MIDI-Map bind parameters. | - Fix crash when modules such as VCV MIDI-Map bind parameters. | ||||
@@ -1 +1 @@ | |||||
Subproject commit 562596968dbd07c67ad8d9d57ec5ff54bc827a9b | |||||
Subproject commit 64482bde25a8e19cc38342ed21aa0e38c2751f6c |
@@ -77,6 +77,7 @@ extern float autosaveInterval; | |||||
extern bool skipLoadOnLaunch; | extern bool skipLoadOnLaunch; | ||||
extern std::list<std::string> recentPatchPaths; | extern std::list<std::string> recentPatchPaths; | ||||
extern std::vector<NVGcolor> cableColors; | extern std::vector<NVGcolor> cableColors; | ||||
extern std::vector<std::string> cableLabels; | |||||
extern bool cableAutoRotate; | extern bool cableAutoRotate; | ||||
extern bool autoCheckUpdates; | extern bool autoCheckUpdates; | ||||
extern bool verifyHttpsCerts; | extern bool verifyHttpsCerts; | ||||
@@ -118,7 +119,7 @@ struct PluginWhitelist { | |||||
extern std::map<std::string, PluginWhitelist> moduleWhitelist; | extern std::map<std::string, PluginWhitelist> moduleWhitelist; | ||||
bool isModuleWhitelisted(const std::string& pluginSlug, const std::string& moduleSlug); | bool isModuleWhitelisted(const std::string& pluginSlug, const std::string& moduleSlug); | ||||
void cableColorsReset(); | |||||
void resetCables(); | |||||
PRIVATE void init(); | PRIVATE void init(); | ||||
PRIVATE void destroy(); | PRIVATE void destroy(); | ||||
@@ -468,49 +468,88 @@ struct ViewButton : MenuButton { | |||||
// Add color items | // Add color items | ||||
for (size_t i = 0; i < settings::cableColors.size(); i++) { | for (size_t i = 0; i < settings::cableColors.size(); i++) { | ||||
NVGcolor color = settings::cableColors[i]; | NVGcolor color = settings::cableColors[i]; | ||||
ui::ColorDotMenuItem* item = createSubmenuItem<ui::ColorDotMenuItem>(string::uppercase(color::toHexString(color)), "", [=](ui::Menu* menu) { | |||||
std::string label = get(settings::cableLabels, i); | |||||
std::string labelFallback = (label != "") ? label : string::f("Color #%lld", (long long) (i + 1)); | |||||
ui::ColorDotMenuItem* item = createSubmenuItem<ui::ColorDotMenuItem>(labelFallback, "", [=](ui::Menu* menu) { | |||||
// Helper for launching color dialog | // Helper for launching color dialog | ||||
auto selectColor = [](NVGcolor color) { | |||||
auto selectColor = [](NVGcolor& color) -> bool { | |||||
osdialog_color c = { | osdialog_color c = { | ||||
uint8_t(color.r * 255.f), | uint8_t(color.r * 255.f), | ||||
uint8_t(color.g * 255.f), | uint8_t(color.g * 255.f), | ||||
uint8_t(color.b * 255.f), | uint8_t(color.b * 255.f), | ||||
uint8_t(color.a * 255.f), | uint8_t(color.a * 255.f), | ||||
}; | }; | ||||
osdialog_color_picker(&c, false); | |||||
return nvgRGBA(c.r, c.g, c.b, c.a); | |||||
if (!osdialog_color_picker(&c, false)) | |||||
return false; | |||||
color = nvgRGBA(c.r, c.g, c.b, c.a); | |||||
return true; | |||||
}; | }; | ||||
menu->addChild(createMenuItem("Set label", "", [=]() { | |||||
if (i >= settings::cableColors.size()) | |||||
return; | |||||
char* s = osdialog_prompt(OSDIALOG_INFO, "", label.c_str()); | |||||
if (!s) | |||||
return; | |||||
settings::cableLabels.resize(settings::cableColors.size()); | |||||
settings::cableLabels[i] = s; | |||||
free(s); | |||||
}, false, true)); | |||||
menu->addChild(createMenuItem("Set color", "", [=]() { | menu->addChild(createMenuItem("Set color", "", [=]() { | ||||
if (i >= settings::cableColors.size()) | if (i >= settings::cableColors.size()) | ||||
return; | return; | ||||
NVGcolor newColor = selectColor(color); | |||||
NVGcolor newColor = color; | |||||
if (!selectColor(newColor)) | |||||
return; | |||||
std::memcpy(&settings::cableColors[i], &newColor, sizeof(newColor)); | std::memcpy(&settings::cableColors[i], &newColor, sizeof(newColor)); | ||||
}, false, true)); | }, false, true)); | ||||
menu->addChild(createMenuItem("New color above", "", [=]() { | menu->addChild(createMenuItem("New color above", "", [=]() { | ||||
if (i >= settings::cableColors.size()) | if (i >= settings::cableColors.size()) | ||||
return; | return; | ||||
settings::cableColors.insert(settings::cableColors.begin() + i, selectColor(color)); | |||||
NVGcolor newColor = color; | |||||
if (!selectColor(newColor)) | |||||
return; | |||||
settings::cableLabels.resize(settings::cableColors.size()); | |||||
settings::cableColors.insert(settings::cableColors.begin() + i, newColor); | |||||
settings::cableLabels.insert(settings::cableLabels.begin() + i, ""); | |||||
}, false, true)); | }, false, true)); | ||||
menu->addChild(createMenuItem("New color below", "", [=]() { | menu->addChild(createMenuItem("New color below", "", [=]() { | ||||
if (i >= settings::cableColors.size()) | if (i >= settings::cableColors.size()) | ||||
return; | return; | ||||
settings::cableColors.insert(settings::cableColors.begin() + i + 1, selectColor(color)); | |||||
NVGcolor newColor = color; | |||||
if (!selectColor(newColor)) | |||||
return; | |||||
settings::cableLabels.resize(settings::cableColors.size()); | |||||
settings::cableColors.insert(settings::cableColors.begin() + i + 1, newColor); | |||||
settings::cableLabels.insert(settings::cableLabels.begin() + i + 1, ""); | |||||
}, false, true)); | }, false, true)); | ||||
menu->addChild(createMenuItem("Move up", "", [=]() { | menu->addChild(createMenuItem("Move up", "", [=]() { | ||||
if (i < 1 || i >= settings::cableColors.size()) | if (i < 1 || i >= settings::cableColors.size()) | ||||
return; | return; | ||||
settings::cableLabels.resize(settings::cableColors.size()); | |||||
std::swap(settings::cableColors[i], settings::cableColors[i - 1]); | std::swap(settings::cableColors[i], settings::cableColors[i - 1]); | ||||
std::swap(settings::cableLabels[i], settings::cableLabels[i - 1]); | |||||
}, i < 1, true)); | }, i < 1, true)); | ||||
menu->addChild(createMenuItem("Move down", "", [=]() { | menu->addChild(createMenuItem("Move down", "", [=]() { | ||||
if (i + 1 >= settings::cableColors.size()) | if (i + 1 >= settings::cableColors.size()) | ||||
return; | return; | ||||
settings::cableLabels.resize(settings::cableColors.size()); | |||||
std::swap(settings::cableColors[i], settings::cableColors[i + 1]); | std::swap(settings::cableColors[i], settings::cableColors[i + 1]); | ||||
std::swap(settings::cableLabels[i], settings::cableLabels[i + 1]); | |||||
}, i + 1 >= settings::cableColors.size())); | }, i + 1 >= settings::cableColors.size())); | ||||
menu->addChild(createMenuItem("Delete", "", [=]() { | menu->addChild(createMenuItem("Delete", "", [=]() { | ||||
if (i >= settings::cableColors.size()) | if (i >= settings::cableColors.size()) | ||||
return; | return; | ||||
settings::cableLabels.resize(settings::cableColors.size()); | |||||
settings::cableColors.erase(settings::cableColors.begin() + i); | settings::cableColors.erase(settings::cableColors.begin() + i); | ||||
settings::cableLabels.erase(settings::cableLabels.begin() + i); | |||||
}, settings::cableColors.size() <= 1, true)); | }, settings::cableColors.size() <= 1, true)); | ||||
}); | }); | ||||
item->color = color; | item->color = color; | ||||
@@ -528,7 +567,7 @@ struct ViewButton : MenuButton { | |||||
menu->addChild(createMenuItem("Restore factory colors", "", [=]() { | menu->addChild(createMenuItem("Restore factory colors", "", [=]() { | ||||
if (!osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK_CANCEL, "Overwrite colors with factory defaults?")) | if (!osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK_CANCEL, "Overwrite colors with factory defaults?")) | ||||
return; | return; | ||||
settings::cableColorsReset(); | |||||
settings::resetCables(); | |||||
}, false, true)); | }, false, true)); | ||||
})); | })); | ||||
@@ -263,8 +263,10 @@ void PortWidget::createContextMenu() { | |||||
// New cable items | // New cable items | ||||
for (size_t colorId = 0; colorId < settings::cableColors.size(); colorId++) { | for (size_t colorId = 0; colorId < settings::cableColors.size(); colorId++) { | ||||
NVGcolor color = settings::cableColors[colorId]; | NVGcolor color = settings::cableColors[colorId]; | ||||
// Include extra leading spaces for the color circle | |||||
PortCreateCableItem* item = createMenuItem<PortCreateCableItem>("New cable", "Click+drag"); | |||||
std::string label = get(settings::cableLabels, colorId); | |||||
if (label == "") | |||||
label = string::f("Color #%lld", (long long) (colorId + 1)); | |||||
PortCreateCableItem* item = createMenuItem<PortCreateCableItem>(label, "Click+drag"); | |||||
item->pw = this; | item->pw = this; | ||||
item->color = color; | item->color = color; | ||||
item->colorId = colorId; | item->colorId = colorId; | ||||
@@ -55,6 +55,7 @@ bool skipLoadOnLaunch = false; | |||||
std::list<std::string> recentPatchPaths; | std::list<std::string> recentPatchPaths; | ||||
bool cableAutoRotate = true; | bool cableAutoRotate = true; | ||||
std::vector<NVGcolor> cableColors; | std::vector<NVGcolor> cableColors; | ||||
std::vector<std::string> cableLabels; | |||||
bool autoCheckUpdates = true; | bool autoCheckUpdates = true; | ||||
bool verifyHttpsCerts = true; | bool verifyHttpsCerts = true; | ||||
bool showTipsOnLaunch = true; | bool showTipsOnLaunch = true; | ||||
@@ -94,7 +95,7 @@ bool isModuleWhitelisted(const std::string& pluginSlug, const std::string& modul | |||||
} | } | ||||
void cableColorsReset() { | |||||
void resetCables() { | |||||
cableColors = { | cableColors = { | ||||
color::fromHexString("#f3374b"), // red | color::fromHexString("#f3374b"), // red | ||||
color::fromHexString("#ffb437"), // yellow | color::fromHexString("#ffb437"), // yellow | ||||
@@ -102,12 +103,14 @@ void cableColorsReset() { | |||||
color::fromHexString("#3695ef"), // blue | color::fromHexString("#3695ef"), // blue | ||||
color::fromHexString("#8b4ade"), // purple | color::fromHexString("#8b4ade"), // purple | ||||
}; | }; | ||||
cableLabels.clear(); | |||||
cableLabels.resize(cableColors.size()); | |||||
} | } | ||||
void init() { | void init() { | ||||
settingsPath = asset::user("settings.json"); | settingsPath = asset::user("settings.json"); | ||||
cableColorsReset(); | |||||
resetCables(); | |||||
} | } | ||||
@@ -187,12 +190,18 @@ json_t* toJson() { | |||||
json_object_set_new(rootJ, "recentPatchPaths", recentPatchPathsJ); | json_object_set_new(rootJ, "recentPatchPaths", recentPatchPathsJ); | ||||
json_t* cableColorsJ = json_array(); | json_t* cableColorsJ = json_array(); | ||||
for (NVGcolor cableColor : cableColors) { | |||||
for (const NVGcolor& cableColor : cableColors) { | |||||
std::string colorStr = color::toHexString(cableColor); | std::string colorStr = color::toHexString(cableColor); | ||||
json_array_append_new(cableColorsJ, json_string(colorStr.c_str())); | json_array_append_new(cableColorsJ, json_string(colorStr.c_str())); | ||||
} | } | ||||
json_object_set_new(rootJ, "cableColors", cableColorsJ); | json_object_set_new(rootJ, "cableColors", cableColorsJ); | ||||
json_t* cableLabelsJ = json_array(); | |||||
for (const std::string& cableLabel : cableLabels) { | |||||
json_array_append_new(cableLabelsJ, json_string(cableLabel.c_str())); | |||||
} | |||||
json_object_set_new(rootJ, "cableLabels", cableLabelsJ); | |||||
json_object_set_new(rootJ, "cableAutoRotate", json_boolean(cableAutoRotate)); | json_object_set_new(rootJ, "cableAutoRotate", json_boolean(cableAutoRotate)); | ||||
json_object_set_new(rootJ, "autoCheckUpdates", json_boolean(autoCheckUpdates)); | json_object_set_new(rootJ, "autoCheckUpdates", json_boolean(autoCheckUpdates)); | ||||
@@ -419,6 +428,16 @@ void fromJson(json_t* rootJ) { | |||||
} | } | ||||
} | } | ||||
cableLabels.clear(); | |||||
json_t* cableLabelsJ = json_object_get(rootJ, "cableLabels"); | |||||
if (cableLabelsJ) { | |||||
size_t i; | |||||
json_t* cableLabelJ; | |||||
json_array_foreach(cableLabelsJ, i, cableLabelJ) { | |||||
cableLabels.push_back(json_string_value(cableLabelJ)); | |||||
} | |||||
} | |||||
json_t* cableAutoRotateJ = json_object_get(rootJ, "cableAutoRotate"); | json_t* cableAutoRotateJ = json_object_get(rootJ, "cableAutoRotate"); | ||||
if (cableAutoRotateJ) | if (cableAutoRotateJ) | ||||
cableAutoRotate = json_boolean_value(cableAutoRotateJ); | cableAutoRotate = json_boolean_value(cableAutoRotateJ); | ||||