From bb184ed481e506ee40109fcfa2545e962b2c3134 Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Mon, 20 Jan 2020 21:49:03 -0800 Subject: [PATCH] Simulation GUI: Refactor ini saving (#2291) --- .../src/main/native/cpp/AddressableLEDGui.cpp | 102 ++++++------------ .../src/main/native/cpp/EncoderGui.cpp | 58 ++-------- .../src/main/native/cpp/IniSaverInfo.cpp | 61 +++++++++++ .../src/main/native/cpp/SimDeviceGui.cpp | 58 ++-------- .../src/main/native/include/IniSaver.h | 47 ++++++++ .../src/main/native/include/IniSaver.inl | 56 ++++++++++ .../src/main/native/include/IniSaverInfo.h | 42 ++++++++ .../src/main/native/include/IniSaverString.h | 48 +++++++++ .../main/native/include/IniSaverString.inl | 57 ++++++++++ 9 files changed, 367 insertions(+), 162 deletions(-) create mode 100644 simulation/halsim_gui/src/main/native/cpp/IniSaverInfo.cpp create mode 100644 simulation/halsim_gui/src/main/native/include/IniSaver.h create mode 100644 simulation/halsim_gui/src/main/native/include/IniSaver.inl create mode 100644 simulation/halsim_gui/src/main/native/include/IniSaverInfo.h create mode 100644 simulation/halsim_gui/src/main/native/include/IniSaverString.h create mode 100644 simulation/halsim_gui/src/main/native/include/IniSaverString.inl diff --git a/simulation/halsim_gui/src/main/native/cpp/AddressableLEDGui.cpp b/simulation/halsim_gui/src/main/native/cpp/AddressableLEDGui.cpp index 606eac5cc6..2fbc4ba95a 100644 --- a/simulation/halsim_gui/src/main/native/cpp/AddressableLEDGui.cpp +++ b/simulation/halsim_gui/src/main/native/cpp/AddressableLEDGui.cpp @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------*/ -/* Copyright (c) 2019 FIRST. All Rights Reserved. */ +/* Copyright (c) 2019-2020 FIRST. All Rights Reserved. */ /* Open Source Software - may be modified and shared by FRC teams. The code */ /* must be accompanied by the FIRST BSD license file in the root directory of */ /* the project. */ @@ -7,10 +7,6 @@ #include "AddressableLEDGui.h" -#include -#include -#include - #include #include #include @@ -20,77 +16,55 @@ #include "ExtraGuiWidgets.h" #include "HALSimGui.h" +#include "IniSaver.h" +#include "IniSaverInfo.h" using namespace halsimgui; namespace { -struct LEDDisplaySettings { +struct LEDDisplayInfo { int numColumns = 10; LEDConfig config; + + bool ReadIni(wpi::StringRef name, wpi::StringRef value); + void WriteIni(ImGuiTextBuffer* out); }; } // namespace -static std::vector displaySettings; +static IniSaver gDisplaySettings{"AddressableLED"}; -// read/write columns setting to ini file -static void* AddressableLEDReadOpen(ImGuiContext* ctx, - ImGuiSettingsHandler* handler, - const char* name) { - int num; - if (wpi::StringRef{name}.getAsInteger(10, num)) return nullptr; - if (num < 0) return nullptr; - if (num >= static_cast(displaySettings.size())) - displaySettings.resize(num + 1); - return &displaySettings[num]; -} - -static void AddressableLEDReadLine(ImGuiContext* ctx, - ImGuiSettingsHandler* handler, void* entry, - const char* lineStr) { - auto* settings = static_cast(entry); - // format: columns=# - wpi::StringRef line{lineStr}; - auto [name, value] = line.split('='); - name = name.trim(); - value = value.trim(); +bool LEDDisplayInfo::ReadIni(wpi::StringRef name, wpi::StringRef value) { if (name == "columns") { int num; - if (value.getAsInteger(10, num)) return; - settings->numColumns = num; + if (value.getAsInteger(10, num)) return true; + numColumns = num; } else if (name == "serpentine") { int num; - if (value.getAsInteger(10, num)) return; - settings->config.serpentine = num != 0; + if (value.getAsInteger(10, num)) return true; + config.serpentine = num != 0; } else if (name == "order") { int num; - if (value.getAsInteger(10, num)) return; - settings->config.order = static_cast(num); + if (value.getAsInteger(10, num)) return true; + config.order = static_cast(num); } else if (name == "start") { int num; - if (value.getAsInteger(10, num)) return; - settings->config.start = static_cast(num); + if (value.getAsInteger(10, num)) return true; + config.start = static_cast(num); + } else { + return false; } + return true; } -static void AddressableLEDWriteAll(ImGuiContext* ctx, - ImGuiSettingsHandler* handler, - ImGuiTextBuffer* out_buf) { - for (size_t i = 0; i < displaySettings.size(); ++i) { - out_buf->appendf( - "[AddressableLED][%d]\ncolumns=%d\nserpentine=%d\norder=%d\n" - "start=%d\n\n", - static_cast(i), displaySettings[i].numColumns, - displaySettings[i].config.serpentine ? 1 : 0, - static_cast(displaySettings[i].config.order), - static_cast(displaySettings[i].config.start)); - } +void LEDDisplayInfo::WriteIni(ImGuiTextBuffer* out) { + out->appendf("columns=%d\nserpentine=%d\norder=%d\nstart=%d\n", numColumns, + config.serpentine ? 1 : 0, static_cast(config.order), + static_cast(config.start)); } static void DisplayAddressableLEDs() { bool hasAny = false; static const int numLED = HAL_GetNumAddressableLEDs(); - if (numLED > static_cast(displaySettings.size())) - displaySettings.resize(numLED); for (int i = 0; i < numLED; ++i) { if (!HALSIM_GetAddressableLEDInitialized(i)) continue; @@ -101,26 +75,27 @@ static void DisplayAddressableLEDs() { static HAL_AddressableLEDData data[HAL_kAddressableLEDMaxLength]; int length = HALSIM_GetAddressableLEDData(i, data); bool running = HALSIM_GetAddressableLEDRunning(i); + auto& info = gDisplaySettings[i]; ImGui::PushItemWidth(ImGui::GetFontSize() * 6); ImGui::LabelText("Length", "%d", length); ImGui::LabelText("Running", "%s", running ? "Yes" : "No"); - ImGui::InputInt("Columns", &displaySettings[i].numColumns); + ImGui::InputInt("Columns", &info.numColumns); { static const char* options[] = {"Row Major", "Column Major"}; - int val = displaySettings[i].config.order; + int val = info.config.order; if (ImGui::Combo("Order", &val, options, 2)) - displaySettings[i].config.order = static_cast(val); + info.config.order = static_cast(val); } { static const char* options[] = {"Upper Left", "Lower Left", "Upper Right", "Lower Right"}; - int val = displaySettings[i].config.start; + int val = info.config.start; if (ImGui::Combo("Start", &val, options, 4)) - displaySettings[i].config.start = static_cast(val); + info.config.start = static_cast(val); } - ImGui::Checkbox("Serpentine", &displaySettings[i].config.serpentine); - if (displaySettings[i].numColumns < 1) displaySettings[i].numColumns = 1; + ImGui::Checkbox("Serpentine", &info.config.serpentine); + if (info.numColumns < 1) info.numColumns = 1; ImGui::PopItemWidth(); // show as LED indicators @@ -137,22 +112,13 @@ static void DisplayAddressableLEDs() { } } - DrawLEDs(values, length, displaySettings[i].numColumns, colors, 0, 0, - displaySettings[i].config); + DrawLEDs(values, length, info.numColumns, colors, 0, 0, info.config); } if (!hasAny) ImGui::Text("No addressable LEDs"); } void AddressableLEDGui::Initialize() { - // hook ini handler to save columns settings - ImGuiSettingsHandler iniHandler; - iniHandler.TypeName = "AddressableLED"; - iniHandler.TypeHash = ImHashStr(iniHandler.TypeName); - iniHandler.ReadOpenFn = AddressableLEDReadOpen; - iniHandler.ReadLineFn = AddressableLEDReadLine; - iniHandler.WriteAllFn = AddressableLEDWriteAll; - ImGui::GetCurrentContext()->SettingsHandlers.push_back(iniHandler); - + gDisplaySettings.Initialize(); HALSimGui::AddWindow("Addressable LEDs", DisplayAddressableLEDs, ImGuiWindowFlags_AlwaysAutoResize); HALSimGui::SetWindowVisibility("Addressable LEDs", HALSimGui::kHide); diff --git a/simulation/halsim_gui/src/main/native/cpp/EncoderGui.cpp b/simulation/halsim_gui/src/main/native/cpp/EncoderGui.cpp index 55650fb51d..bf8c52e5f9 100644 --- a/simulation/halsim_gui/src/main/native/cpp/EncoderGui.cpp +++ b/simulation/halsim_gui/src/main/native/cpp/EncoderGui.cpp @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------*/ -/* Copyright (c) 2019 FIRST. All Rights Reserved. */ +/* Copyright (c) 2019-2020 FIRST. All Rights Reserved. */ /* Open Source Software - may be modified and shared by FRC teams. The code */ /* must be accompanied by the FIRST BSD license file in the root directory of */ /* the project. */ @@ -11,45 +11,16 @@ #include #include -#include #include #include -#include -#include #include "HALSimGui.h" +#include "IniSaver.h" +#include "IniSaverInfo.h" using namespace halsimgui; -static wpi::DenseMap gEncodersOpen; // indexed by channel A - -// read/write open state to ini file -static void* EncodersReadOpen(ImGuiContext* ctx, ImGuiSettingsHandler* handler, - const char* name) { - int num; - if (wpi::StringRef{name}.getAsInteger(10, num)) return nullptr; - return &gEncodersOpen[num]; -} - -static void EncodersReadLine(ImGuiContext* ctx, ImGuiSettingsHandler* handler, - void* entry, const char* lineStr) { - bool* element = static_cast(entry); - wpi::StringRef line{lineStr}; - auto [name, value] = line.split('='); - name = name.trim(); - value = value.trim(); - if (name == "open") { - int num; - if (value.getAsInteger(10, num)) return; - *element = num; - } -} - -static void EncodersWriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, - ImGuiTextBuffer* out_buf) { - for (auto it : gEncodersOpen) - out_buf->appendf("[Encoder][%d]\nopen=%d\n\n", it.first, it.second ? 1 : 0); -} +static IniSaver gEncoders{"Encoder"}; // indexed by channel A static void DisplayEncoders() { bool hasAny = false; @@ -66,10 +37,11 @@ static void DisplayEncoders() { ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(96, 96, 96, 255)); ImGui::Text("%s", HALSIM_GetSimDeviceName(simDevice)); ImGui::PopStyleColor(); - } else if (ImGui::CollapsingHeader( - name, - gEncodersOpen[chA] ? ImGuiTreeNodeFlags_DefaultOpen : 0)) { - gEncodersOpen[chA] = true; + } else if (ImGui::CollapsingHeader(name, + gEncoders[chA].IsOpen() + ? ImGuiTreeNodeFlags_DefaultOpen + : 0)) { + gEncoders[chA].SetOpen(true); ImGui::PushID(i); @@ -105,7 +77,7 @@ static void DisplayEncoders() { ImGui::PopID(); } else { - gEncodersOpen[chA] = false; + gEncoders[chA].SetOpen(false); } } } @@ -114,15 +86,7 @@ static void DisplayEncoders() { } void EncoderGui::Initialize() { - // hook ini handler to save settings - ImGuiSettingsHandler iniHandler; - iniHandler.TypeName = "Encoder"; - iniHandler.TypeHash = ImHashStr(iniHandler.TypeName); - iniHandler.ReadOpenFn = EncodersReadOpen; - iniHandler.ReadLineFn = EncodersReadLine; - iniHandler.WriteAllFn = EncodersWriteAll; - ImGui::GetCurrentContext()->SettingsHandlers.push_back(iniHandler); - + gEncoders.Initialize(); HALSimGui::AddWindow("Encoders", DisplayEncoders, ImGuiWindowFlags_AlwaysAutoResize); HALSimGui::SetDefaultWindowPos("Encoders", 640, 215); diff --git a/simulation/halsim_gui/src/main/native/cpp/IniSaverInfo.cpp b/simulation/halsim_gui/src/main/native/cpp/IniSaverInfo.cpp new file mode 100644 index 0000000000..0393d523d9 --- /dev/null +++ b/simulation/halsim_gui/src/main/native/cpp/IniSaverInfo.cpp @@ -0,0 +1,61 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2020 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +#include "IniSaverInfo.h" + +#include +#include + +#include + +using namespace halsimgui; + +void NameInfo::GetName(char* buf, size_t size, const char* defaultName, + int index) { + if (m_name[0] != '\0') { + std::snprintf(buf, size, "%s [%d]###Name%d", m_name, index, index); + } else { + std::snprintf(buf, size, "%s[%d]###Name%d", defaultName, index, index); + } +} + +bool NameInfo::ReadIni(wpi::StringRef name, wpi::StringRef value) { + if (name != "name") return false; + size_t len = (std::min)(value.size(), sizeof(m_name) - 1); + std::memcpy(m_name, value.data(), len); + m_name[len] = '\0'; + return true; +} + +void NameInfo::WriteIni(ImGuiTextBuffer* out) { + out->appendf("name=%s\n", m_name); +} + +void NameInfo::PopupEditName(int index) { + char id[64]; + std::snprintf(id, sizeof(id), "Name%d", index); + if (ImGui::BeginPopupContextItem(id)) { + ImGui::Text("Edit name:"); + if (ImGui::InputText("##edit", m_name, sizeof(m_name), + ImGuiInputTextFlags_EnterReturnsTrue)) + ImGui::CloseCurrentPopup(); + if (ImGui::Button("Close")) ImGui::CloseCurrentPopup(); + ImGui::EndPopup(); + } +} + +bool OpenInfo::ReadIni(wpi::StringRef name, wpi::StringRef value) { + if (name != "open") return false; + int num; + if (value.getAsInteger(10, num)) return true; + m_open = num; + return true; +} + +void OpenInfo::WriteIni(ImGuiTextBuffer* out) { + out->appendf("open=%d\n", m_open ? 1 : 0); +} diff --git a/simulation/halsim_gui/src/main/native/cpp/SimDeviceGui.cpp b/simulation/halsim_gui/src/main/native/cpp/SimDeviceGui.cpp index 2063007099..90d66f3e8b 100644 --- a/simulation/halsim_gui/src/main/native/cpp/SimDeviceGui.cpp +++ b/simulation/halsim_gui/src/main/native/cpp/SimDeviceGui.cpp @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------*/ -/* Copyright (c) 2019 FIRST. All Rights Reserved. */ +/* Copyright (c) 2019-2020 FIRST. All Rights Reserved. */ /* Open Source Software - may be modified and shared by FRC teams. The code */ /* must be accompanied by the FIRST BSD license file in the root directory of */ /* the project. */ @@ -13,51 +13,22 @@ #include #include -#include #include -#include #include "HALSimGui.h" +#include "IniSaverInfo.h" +#include "IniSaverString.h" using namespace halsimgui; namespace { -struct ElementInfo { - bool open = false; - bool visible = true; +struct ElementInfo : public OpenInfo { + bool visible = true; // not saved }; } // namespace static std::vector> gDeviceExecutors; -static wpi::StringMap gElements; - -// read/write open state to ini file -static void* DevicesReadOpen(ImGuiContext* ctx, ImGuiSettingsHandler* handler, - const char* name) { - return &gElements[name]; -} - -static void DevicesReadLine(ImGuiContext* ctx, ImGuiSettingsHandler* handler, - void* entry, const char* lineStr) { - ElementInfo* element = static_cast(entry); - wpi::StringRef line{lineStr}; - auto [name, value] = line.split('='); - name = name.trim(); - value = value.trim(); - if (name == "open") { - int num; - if (value.getAsInteger(10, num)) return; - element->open = num; - } -} - -static void DevicesWriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, - ImGuiTextBuffer* out_buf) { - for (auto&& entry : gElements) { - out_buf->appendf("[Device][%s]\nopen=%d\n\n", entry.getKey().data(), - entry.getValue().open ? 1 : 0); - } -} +static IniSaverString gElements{"Device"}; void SimDeviceGui::Hide(const char* name) { gElements[name].visible = false; } @@ -70,12 +41,13 @@ bool SimDeviceGui::StartDevice(const char* label, ImGuiTreeNodeFlags flags) { if (!element.visible) return false; if (ImGui::CollapsingHeader( - label, flags | (element.open ? ImGuiTreeNodeFlags_DefaultOpen : 0))) { + label, + flags | (element.IsOpen() ? ImGuiTreeNodeFlags_DefaultOpen : 0))) { ImGui::PushID(label); - element.open = true; + element.SetOpen(true); return true; } - element.open = false; + element.SetOpen(false); return false; } @@ -201,15 +173,7 @@ static void DisplayDeviceTree() { } void SimDeviceGui::Initialize() { - // hook ini handler to save device settings - ImGuiSettingsHandler iniHandler; - iniHandler.TypeName = "Device"; - iniHandler.TypeHash = ImHashStr(iniHandler.TypeName); - iniHandler.ReadOpenFn = DevicesReadOpen; - iniHandler.ReadLineFn = DevicesReadLine; - iniHandler.WriteAllFn = DevicesWriteAll; - ImGui::GetCurrentContext()->SettingsHandlers.push_back(iniHandler); - + gElements.Initialize(); HALSimGui::AddWindow("Other Devices", DisplayDeviceTree); HALSimGui::SetDefaultWindowPos("Other Devices", 1025, 20); HALSimGui::SetDefaultWindowSize("Other Devices", 250, 695); diff --git a/simulation/halsim_gui/src/main/native/include/IniSaver.h b/simulation/halsim_gui/src/main/native/include/IniSaver.h new file mode 100644 index 0000000000..a36fa8b1b7 --- /dev/null +++ b/simulation/halsim_gui/src/main/native/include/IniSaver.h @@ -0,0 +1,47 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2020 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +#pragma once + +#include +#include +#include + +namespace halsimgui { + +template +class IniSaver { + public: + explicit IniSaver(const char* typeName) : m_typeName(typeName) {} + void Initialize(); + + // pass through useful functions to map + Info& operator[](int index) { return m_map[index]; } + + auto begin() { return m_map.begin(); } + auto end() { return m_map.end(); } + auto find(int index) { return m_map.find(index); } + + auto begin() const { return m_map.begin(); } + auto end() const { return m_map.end(); } + auto find(int index) const { return m_map.find(index); } + + private: + static void* ReadOpen(ImGuiContext* ctx, ImGuiSettingsHandler* handler, + const char* name); + static void ReadLine(ImGuiContext* ctx, ImGuiSettingsHandler* handler, + void* entry, const char* lineStr); + static void WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, + ImGuiTextBuffer* out_buf); + + const char* m_typeName; + wpi::DenseMap m_map; +}; + +} // namespace halsimgui + +#include "IniSaver.inl" diff --git a/simulation/halsim_gui/src/main/native/include/IniSaver.inl b/simulation/halsim_gui/src/main/native/include/IniSaver.inl new file mode 100644 index 0000000000..007ad5a8f1 --- /dev/null +++ b/simulation/halsim_gui/src/main/native/include/IniSaver.inl @@ -0,0 +1,56 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2020 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +#pragma once + +namespace halsimgui { + +template +void IniSaver::Initialize() { + // hook ini handler to save settings + ImGuiSettingsHandler iniHandler; + iniHandler.TypeName = m_typeName; + iniHandler.TypeHash = ImHashStr(m_typeName); + iniHandler.ReadOpenFn = ReadOpen; + iniHandler.ReadLineFn = ReadLine; + iniHandler.WriteAllFn = WriteAll; + iniHandler.UserData = this; + ImGui::GetCurrentContext()->SettingsHandlers.push_back(iniHandler); +} + +template +void* IniSaver::ReadOpen(ImGuiContext* ctx, ImGuiSettingsHandler* handler, + const char* name) { + auto self = static_cast(handler->UserData); + int num; + if (wpi::StringRef{name}.getAsInteger(10, num)) return nullptr; + return &self->m_map[num]; +} + +template +void IniSaver::ReadLine(ImGuiContext* ctx, ImGuiSettingsHandler* handler, + void* entry, const char* lineStr) { + auto element = static_cast(entry); + wpi::StringRef line{lineStr}; + auto [name, value] = line.split('='); + name = name.trim(); + value = value.trim(); + element->ReadIni(name, value); +} + +template +void IniSaver::WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, + ImGuiTextBuffer* out_buf) { + auto self = static_cast(handler->UserData); + for (auto&& it : self->m_map) { + out_buf->appendf("[%s][%d]\n", self->m_typeName, it.first); + it.second.WriteIni(out_buf); + out_buf->append("\n"); + } +} + +} // namespace halsimgui diff --git a/simulation/halsim_gui/src/main/native/include/IniSaverInfo.h b/simulation/halsim_gui/src/main/native/include/IniSaverInfo.h new file mode 100644 index 0000000000..12dd3defc7 --- /dev/null +++ b/simulation/halsim_gui/src/main/native/include/IniSaverInfo.h @@ -0,0 +1,42 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2020 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +#pragma once + +#include +#include + +namespace halsimgui { + +class NameInfo { + public: + NameInfo() { m_name[0] = '\0'; } + + void GetName(char* buf, size_t size, const char* defaultName, int index); + bool ReadIni(wpi::StringRef name, wpi::StringRef value); + void WriteIni(ImGuiTextBuffer* out); + void PopupEditName(int index); + + private: + char m_name[64]; +}; + +class OpenInfo { + public: + OpenInfo() = default; + explicit OpenInfo(bool open) : m_open(open) {} + + bool IsOpen() const { return m_open; } + void SetOpen(bool open) { m_open = open; } + bool ReadIni(wpi::StringRef name, wpi::StringRef value); + void WriteIni(ImGuiTextBuffer* out); + + private: + bool m_open = false; +}; + +} // namespace halsimgui diff --git a/simulation/halsim_gui/src/main/native/include/IniSaverString.h b/simulation/halsim_gui/src/main/native/include/IniSaverString.h new file mode 100644 index 0000000000..206d695482 --- /dev/null +++ b/simulation/halsim_gui/src/main/native/include/IniSaverString.h @@ -0,0 +1,48 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2020 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include + +namespace halsimgui { + +template +class IniSaverString { + public: + explicit IniSaverString(const char* typeName) : m_typeName(typeName) {} + void Initialize(); + + // pass through useful functions to map + Info& operator[](wpi::StringRef key) { return m_map[key]; } + + auto begin() { return m_map.begin(); } + auto end() { return m_map.end(); } + auto find(wpi::StringRef key) { return m_map.find(key); } + + auto begin() const { return m_map.begin(); } + auto end() const { return m_map.end(); } + auto find(wpi::StringRef key) const { return m_map.find(key); } + + private: + static void* ReadOpen(ImGuiContext* ctx, ImGuiSettingsHandler* handler, + const char* name); + static void ReadLine(ImGuiContext* ctx, ImGuiSettingsHandler* handler, + void* entry, const char* lineStr); + static void WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, + ImGuiTextBuffer* out_buf); + + const char* m_typeName; + wpi::StringMap m_map; +}; + +} // namespace halsimgui + +#include "IniSaverString.inl" diff --git a/simulation/halsim_gui/src/main/native/include/IniSaverString.inl b/simulation/halsim_gui/src/main/native/include/IniSaverString.inl new file mode 100644 index 0000000000..5ac7dc29b3 --- /dev/null +++ b/simulation/halsim_gui/src/main/native/include/IniSaverString.inl @@ -0,0 +1,57 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2020 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +#pragma once + +namespace halsimgui { + +template +void IniSaverString::Initialize() { + // hook ini handler to save settings + ImGuiSettingsHandler iniHandler; + iniHandler.TypeName = m_typeName; + iniHandler.TypeHash = ImHashStr(m_typeName); + iniHandler.ReadOpenFn = ReadOpen; + iniHandler.ReadLineFn = ReadLine; + iniHandler.WriteAllFn = WriteAll; + iniHandler.UserData = this; + ImGui::GetCurrentContext()->SettingsHandlers.push_back(iniHandler); +} + +template +void* IniSaverString::ReadOpen(ImGuiContext* ctx, + ImGuiSettingsHandler* handler, + const char* name) { + auto self = static_cast(handler->UserData); + return &self->m_map[name]; +} + +template +void IniSaverString::ReadLine(ImGuiContext* ctx, + ImGuiSettingsHandler* handler, void* entry, + const char* lineStr) { + auto element = static_cast(entry); + wpi::StringRef line{lineStr}; + auto [name, value] = line.split('='); + name = name.trim(); + value = value.trim(); + element->ReadIni(name, value); +} + +template +void IniSaverString::WriteAll(ImGuiContext* ctx, + ImGuiSettingsHandler* handler, + ImGuiTextBuffer* out_buf) { + auto self = static_cast(handler->UserData); + for (auto&& it : self->m_map) { + out_buf->appendf("[%s][%s]\n", self->m_typeName, it.getKey().data()); + it.second.WriteIni(out_buf); + out_buf->append("\n"); + } +} + +} // namespace halsimgui