[glass] Use JSON files for storage instead of imgui ini

Storage is now nested.

Separate "roots" can be configured which save to separate files.
In particular, this is used to save wpigui and ImGui window position
to a -window.json file.

ImGui's ini (for window position) is mapped to JSON.

You can optionally specify a directory to load from on the command line.
If one isn't provided, it uses the global system directory.
Any changes made are automatically saved here.

Workspace | Open: select directory, the current layout is replaced with that
workspace, and future auto-saves also switch to that location. The main
window size/location is not changed, only the contents.

Workspace | Save As: select directory, the current layout is saved there,
and future auto-saves also switch to that location.

Workspace | Reset: window locations are preserved, but all other settings
are reset to default (including e.g. removing plot windows). This will also
end up clearing the current save file. as with load, the main window
size/location is not changed.

Workspace | Save As Global: "save as" to the global system location

Notably, the main window size/location is only loaded at startup, but is
auto-saved as part of the current workspace.
This commit is contained in:
Peter Johnson
2021-11-25 00:51:00 -08:00
parent 0bbf51d566
commit 0587b7043a
70 changed files with 3007 additions and 2358 deletions

View File

@@ -8,6 +8,7 @@
#include "glass/Context.h"
#include "glass/DataSource.h"
#include "glass/Storage.h"
using namespace glass;
@@ -18,10 +19,10 @@ void glass::DisplayAnalogInput(AnalogInputModel* model, int index) {
}
// build label
std::string* name = GetStorage().GetStringRef("name");
std::string& name = GetStorage().GetString("name");
char label[128];
if (!name->empty()) {
std::snprintf(label, sizeof(label), "%s [%d]###name", name->c_str(), index);
if (!name.empty()) {
std::snprintf(label, sizeof(label), "%s [%d]###name", name.c_str(), index);
} else {
std::snprintf(label, sizeof(label), "In[%d]###name", index);
}
@@ -42,8 +43,8 @@ void glass::DisplayAnalogInput(AnalogInputModel* model, int index) {
}
// context menu to change name
if (PopupEditName("name", name)) {
voltageData->SetName(name->c_str());
if (PopupEditName("name", &name)) {
voltageData->SetName(name);
}
}

View File

@@ -6,6 +6,7 @@
#include "glass/Context.h"
#include "glass/DataSource.h"
#include "glass/Storage.h"
#include "glass/other/DeviceTree.h"
using namespace glass;
@@ -26,10 +27,10 @@ void glass::DisplayAnalogOutputsDevice(AnalogOutputsModel* model) {
PushID(i);
// build label
std::string* name = GetStorage().GetStringRef("name");
std::string& name = GetStorage().GetString("name");
char label[128];
if (!name->empty()) {
std::snprintf(label, sizeof(label), "%s [%d]###name", name->c_str(), i);
if (!name.empty()) {
std::snprintf(label, sizeof(label), "%s [%d]###name", name.c_str(), i);
} else {
std::snprintf(label, sizeof(label), "Out[%d]###name", i);
}
@@ -37,9 +38,9 @@ void glass::DisplayAnalogOutputsDevice(AnalogOutputsModel* model) {
double value = analogOutData->GetValue();
DeviceDouble(label, true, &value, analogOutData);
if (PopupEditName("name", name)) {
if (PopupEditName("name", &name)) {
if (analogOutData) {
analogOutData->SetName(name->c_str());
analogOutData->SetName(name);
}
}
PopID();

View File

@@ -8,7 +8,7 @@
#include "glass/DataSource.h"
#include "glass/hardware/Encoder.h"
#include "glass/support/IniSaverInfo.h"
#include "glass/support/NameSetting.h"
using namespace glass;
@@ -28,17 +28,18 @@ void DisplayDIOImpl(DIOModel* model, int index, bool outputsEnabled) {
auto dutyCycleData = dutyCycle ? dutyCycle->GetValueData() : nullptr;
bool exists = model->Exists();
auto& info = dioData->GetNameInfo();
NameSetting dioName{dioData->GetName()};
char label[128];
if (exists && dpwmData) {
dpwmData->GetNameInfo().GetLabel(label, sizeof(label), "PWM", index);
NameSetting{dpwmData->GetName()}.GetLabel(label, sizeof(label), "PWM",
index);
if (auto simDevice = dpwm->GetSimDevice()) {
LabelSimDevice(label, simDevice);
} else {
dpwmData->LabelText(label, "%0.3f", dpwmData->GetValue());
}
} else if (exists && encoder) {
info.GetLabel(label, sizeof(label), " In", index);
dioName.GetLabel(label, sizeof(label), " In", index);
if (auto simDevice = encoder->GetSimDevice()) {
LabelSimDevice(label, simDevice);
} else {
@@ -48,7 +49,8 @@ void DisplayDIOImpl(DIOModel* model, int index, bool outputsEnabled) {
ImGui::PopStyleColor();
}
} else if (exists && dutyCycleData) {
dutyCycleData->GetNameInfo().GetLabel(label, sizeof(label), "Dty", index);
NameSetting{dutyCycleData->GetName()}.GetLabel(label, sizeof(label), "Dty",
index);
if (auto simDevice = dutyCycle->GetSimDevice()) {
LabelSimDevice(label, simDevice);
} else {
@@ -60,10 +62,10 @@ void DisplayDIOImpl(DIOModel* model, int index, bool outputsEnabled) {
} else {
const char* name = model->GetName();
if (name[0] != '\0') {
info.GetLabel(label, sizeof(label), name);
dioName.GetLabel(label, sizeof(label), name);
} else {
info.GetLabel(label, sizeof(label), model->IsInput() ? " In" : "Out",
index);
dioName.GetLabel(label, sizeof(label), model->IsInput() ? " In" : "Out",
index);
}
if (auto simDevice = model->GetSimDevice()) {
LabelSimDevice(label, simDevice);
@@ -87,12 +89,12 @@ void DisplayDIOImpl(DIOModel* model, int index, bool outputsEnabled) {
}
}
}
if (info.PopupEditName(index)) {
if (dioName.PopupEditName(index)) {
if (dpwmData) {
dpwmData->SetName(info.GetName());
dpwmData->SetName(dioName.GetName());
}
if (dutyCycleData) {
dutyCycleData->SetName(info.GetName());
dutyCycleData->SetName(dioName.GetName());
}
}
}

View File

@@ -9,6 +9,7 @@
#include "glass/Context.h"
#include "glass/DataSource.h"
#include "glass/Storage.h"
using namespace glass;
@@ -66,10 +67,10 @@ void glass::DisplayEncoder(EncoderModel* model) {
int chB = model->GetChannelB();
// build header label
std::string* name = GetStorage().GetStringRef("name");
std::string& name = GetStorage().GetString("name");
char label[128];
if (!name->empty()) {
std::snprintf(label, sizeof(label), "%s [%d,%d]###name", name->c_str(), chA,
if (!name.empty()) {
std::snprintf(label, sizeof(label), "%s [%d,%d]###name", name.c_str(), chA,
chB);
} else {
std::snprintf(label, sizeof(label), "Encoder[%d,%d]###name", chA, chB);
@@ -79,8 +80,8 @@ void glass::DisplayEncoder(EncoderModel* model) {
bool open = CollapsingHeader(label);
// context menu to change name
if (PopupEditName("name", name)) {
model->SetName(name->c_str());
if (PopupEditName("name", &name)) {
model->SetName(name);
}
if (!open) {

View File

@@ -7,6 +7,7 @@
#include <wpi/SmallVector.h>
#include "glass/Context.h"
#include "glass/Storage.h"
#include "glass/support/ExtraGuiWidgets.h"
using namespace glass;
@@ -25,27 +26,27 @@ void glass::DisplayLEDDisplay(LEDDisplayModel* model, int index) {
bool running = model->IsRunning();
auto& storage = GetStorage();
int* numColumns = storage.GetIntRef("columns", 10);
bool* serpentine = storage.GetBoolRef("serpentine", false);
int* order = storage.GetIntRef("order", LEDConfig::RowMajor);
int* start = storage.GetIntRef("start", LEDConfig::UpperLeft);
int& numColumns = storage.GetInt("columns", 10);
bool& serpentine = storage.GetBool("serpentine", false);
int& order = storage.GetInt("order", LEDConfig::RowMajor);
int& start = storage.GetInt("start", LEDConfig::UpperLeft);
ImGui::PushItemWidth(ImGui::GetFontSize() * 6);
ImGui::LabelText("Length", "%d", length);
ImGui::LabelText("Running", "%s", running ? "Yes" : "No");
ImGui::InputInt("Columns", numColumns);
ImGui::InputInt("Columns", &numColumns);
{
static const char* options[] = {"Row Major", "Column Major"};
ImGui::Combo("Order", order, options, 2);
ImGui::Combo("Order", &order, options, 2);
}
{
static const char* options[] = {"Upper Left", "Lower Left", "Upper Right",
"Lower Right"};
ImGui::Combo("Start", start, options, 4);
ImGui::Combo("Start", &start, options, 4);
}
ImGui::Checkbox("Serpentine", serpentine);
if (*numColumns < 1) {
*numColumns = 1;
ImGui::Checkbox("Serpentine", &serpentine);
if (numColumns < 1) {
numColumns = 1;
}
ImGui::PopItemWidth();
@@ -74,12 +75,12 @@ void glass::DisplayLEDDisplay(LEDDisplayModel* model, int index) {
}
LEDConfig config;
config.serpentine = *serpentine;
config.order = static_cast<LEDConfig::Order>(*order);
config.start = static_cast<LEDConfig::Start>(*start);
config.serpentine = serpentine;
config.order = static_cast<LEDConfig::Order>(order);
config.start = static_cast<LEDConfig::Start>(start);
DrawLEDs(iData->values.data(), length, *numColumns, iData->colors.data(), 0,
0, config);
DrawLEDs(iData->values.data(), length, numColumns, iData->colors.data(), 0, 0,
config);
}
void glass::DisplayLEDDisplays(LEDDisplaysModel* model) {

View File

@@ -12,9 +12,10 @@
#include "glass/Context.h"
#include "glass/DataSource.h"
#include "glass/Storage.h"
#include "glass/other/DeviceTree.h"
#include "glass/support/ExtraGuiWidgets.h"
#include "glass/support/IniSaverInfo.h"
#include "glass/support/NameSetting.h"
using namespace glass;
@@ -42,10 +43,10 @@ bool glass::DisplayPCMSolenoids(PCMModel* model, int index,
}
// build header label
std::string* name = GetStorage().GetStringRef("name");
std::string& name = GetStorage().GetString("name");
char label[128];
if (!name->empty()) {
std::snprintf(label, sizeof(label), "%s [%d]###name", name->c_str(), index);
if (!name.empty()) {
std::snprintf(label, sizeof(label), "%s [%d]###name", name.c_str(), index);
} else {
std::snprintf(label, sizeof(label), "PCM[%d]###name", index);
}
@@ -53,7 +54,7 @@ bool glass::DisplayPCMSolenoids(PCMModel* model, int index,
// header
bool open = CollapsingHeader(label);
PopupEditName("name", name);
PopupEditName("name", &name);
ImGui::SetItemAllowOverlap();
ImGui::SameLine();
@@ -68,11 +69,11 @@ bool glass::DisplayPCMSolenoids(PCMModel* model, int index,
model->ForEachSolenoid([&](SolenoidModel& solenoid, int j) {
if (auto data = solenoid.GetOutputData()) {
PushID(j);
char solenoidName[64];
auto& info = data->GetNameInfo();
info.GetLabel(solenoidName, sizeof(solenoidName), "Solenoid", j);
data->LabelText(solenoidName, "%s", channels[j] == 1 ? "On" : "Off");
info.PopupEditName(j);
char label[64];
NameSetting name{data->GetName()};
name.GetLabel(label, sizeof(label), "Solenoid", j);
data->LabelText(label, "%s", channels[j] == 1 ? "On" : "Off");
name.PopupEditName(j);
PopID();
}
});

View File

@@ -8,6 +8,7 @@
#include "glass/Context.h"
#include "glass/DataSource.h"
#include "glass/Storage.h"
using namespace glass;
@@ -18,10 +19,10 @@ void glass::DisplayPWM(PWMModel* model, int index, bool outputsEnabled) {
}
// build label
std::string* name = GetStorage().GetStringRef("name");
std::string& name = GetStorage().GetString("name");
char label[128];
if (!name->empty()) {
std::snprintf(label, sizeof(label), "%s [%d]###name", name->c_str(), index);
if (!name.empty()) {
std::snprintf(label, sizeof(label), "%s [%d]###name", name.c_str(), index);
} else {
std::snprintf(label, sizeof(label), "PWM[%d]###name", index);
}
@@ -35,8 +36,8 @@ void glass::DisplayPWM(PWMModel* model, int index, bool outputsEnabled) {
float val = outputsEnabled ? data->GetValue() : 0;
data->LabelText(label, "%0.3f", val);
}
if (PopupEditName("name", name)) {
data->SetName(name->c_str());
if (PopupEditName("name", &name)) {
data->SetName(name);
}
}

View File

@@ -11,7 +11,7 @@
#include "glass/Context.h"
#include "glass/DataSource.h"
#include "glass/support/IniSaverInfo.h"
#include "glass/support/NameSetting.h"
using namespace glass;
@@ -19,16 +19,16 @@ static float DisplayChannel(PowerDistributionModel& pdp, int channel) {
float width = 0;
if (auto currentData = pdp.GetCurrentData(channel)) {
ImGui::PushID(channel);
auto& leftInfo = currentData->GetNameInfo();
NameSetting leftName{currentData->GetName()};
char name[64];
leftInfo.GetLabel(name, sizeof(name), "", channel);
leftName.GetLabel(name, sizeof(name), "", channel);
double val = currentData->GetValue();
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 4);
if (currentData->InputDouble(name, &val, 0, 0, "%.3f")) {
pdp.SetCurrent(channel, val);
}
width = ImGui::GetItemRectSize().x;
leftInfo.PopupEditName(channel);
leftName.PopupEditName(channel);
ImGui::PopID();
}
return width;

View File

@@ -8,6 +8,7 @@
#include "glass/Context.h"
#include "glass/DataSource.h"
#include "glass/Storage.h"
#include "glass/support/ExtraGuiWidgets.h"
using namespace glass;
@@ -31,20 +32,20 @@ void glass::DisplayRelay(RelayModel* model, int index, bool outputsEnabled) {
}
}
std::string* name = GetStorage().GetStringRef("name");
std::string& name = GetStorage().GetString("name");
ImGui::PushID("name");
if (!name->empty()) {
ImGui::Text("%s [%d]", name->c_str(), index);
if (!name.empty()) {
ImGui::Text("%s [%d]", name.c_str(), index);
} else {
ImGui::Text("Relay[%d]", index);
}
ImGui::PopID();
if (PopupEditName("name", name)) {
if (PopupEditName("name", &name)) {
if (forwardData) {
forwardData->SetName(name->c_str());
forwardData->SetName(name);
}
if (reverseData) {
reverseData->SetName(name->c_str());
reverseData->SetName(name);
}
}
ImGui::SameLine();