Skip to content

Commit 290e96f

Browse files
authored
Improve UX of tables of Vec (#257)
* Add width stretch flag for better layout management in vector data display. * Refactor ImGui vector widget to support expandable/collapsible rows and improve layout in SofaImGui. * Add VectorWidget for enhanced vector data display in SofaImGui, including support for expandable rows and improved layout management. * Rename VectorWidget to VecVectorWidget * prefer using ScrollY * avoid magic number
1 parent 99dab26 commit 290e96f

File tree

3 files changed

+203
-95
lines changed

3 files changed

+203
-95
lines changed

SofaImGui/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ set(HEADER_FILES
109109
${SOFAIMGUI_SOURCE_DIR}/widgets/MaterialWidget.h
110110
${SOFAIMGUI_SOURCE_DIR}/widgets/RigidMass.h
111111
${SOFAIMGUI_SOURCE_DIR}/widgets/ScalarWidget.h
112+
${SOFAIMGUI_SOURCE_DIR}/widgets/VecVectorWidget.h
112113
${SOFAIMGUI_SOURCE_DIR}/windows/Performances.h
113114
${SOFAIMGUI_SOURCE_DIR}/windows/Log.h
114115
${SOFAIMGUI_SOURCE_DIR}/windows/MouseManager.h

SofaImGui/src/SofaImGui/ImGuiDataWidget.cpp

Lines changed: 1 addition & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include <SofaImGui/widgets/LinearSpringWidget.h>
3232
#include <SofaImGui/widgets/MaterialWidget.h>
3333
#include <SofaImGui/widgets/RigidMass.h>
34+
#include <SofaImGui/widgets/VecVectorWidget.h>
3435

3536
namespace sofaimgui
3637
{
@@ -178,101 +179,6 @@ void DataWidget<type::Vec<4, float> >::showWidget(MyData& data)
178179
* Vectors of Vec
179180
**********************************************************************************************************************/
180181

181-
template< Size N, typename ValueType>
182-
void showVecTableHeader(Data<type::vector<type::Vec<N, ValueType> > >&)
183-
{
184-
ImGui::TableSetupColumn("");
185-
for (unsigned int i = 0; i < N; ++i)
186-
{
187-
ImGui::TableSetupColumn(std::to_string(i).c_str());
188-
}
189-
}
190-
191-
template<typename ValueType>
192-
void showVecTableHeader(Data<type::vector<ValueType> >&)
193-
{
194-
ImGui::TableSetupColumn("");
195-
ImGui::TableSetupColumn("Value");
196-
}
197-
198-
template<typename ValueType>
199-
void showVecTableHeader(Data<type::vector<type::Vec<1, ValueType> > >&)
200-
{
201-
ImGui::TableSetupColumn("");
202-
ImGui::TableSetupColumn("X");
203-
}
204-
205-
template<typename ValueType>
206-
void showVecTableHeader(Data<type::vector<type::Vec<2, ValueType> > >&)
207-
{
208-
ImGui::TableSetupColumn("");
209-
ImGui::TableSetupColumn("X");
210-
ImGui::TableSetupColumn("Y");
211-
}
212-
213-
template<typename ValueType>
214-
void showVecTableHeader(Data<type::vector<type::Vec<3, ValueType> > >&)
215-
{
216-
ImGui::TableSetupColumn("");
217-
ImGui::TableSetupColumn("X");
218-
ImGui::TableSetupColumn("Y");
219-
ImGui::TableSetupColumn("Z");
220-
}
221-
222-
template<Size N, typename ValueType>
223-
bool showLine(unsigned int lineNumber, const std::string& tableLabel, type::Vec<N, ValueType>& vec)
224-
{
225-
for (const auto& v : vec)
226-
{
227-
ImGui::TableNextColumn();
228-
ImGui::Text("%f", v);
229-
}
230-
return false;
231-
}
232-
233-
template<typename ValueType>
234-
bool showLine(unsigned int lineNumber, const std::string& tableLabel, ValueType& value)
235-
{
236-
ImGui::TableNextColumn();
237-
return showScalarWidget("", tableLabel + std::to_string(lineNumber), value);
238-
}
239-
240-
template<class T>
241-
void showVectorWidget(Data<T>& data)
242-
{
243-
static ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody | ImGuiTableFlags_NoHostExtendX;
244-
ImGui::Text("%d elements", data.getValue().size());
245-
const auto nbColumns = data.getValueTypeInfo()->size() + 1;
246-
const auto tableLabel = data.getName() + data.getOwner()->getPathName();
247-
if (ImGui::BeginTable(tableLabel.c_str(), nbColumns, flags))
248-
{
249-
showVecTableHeader(data);
250-
251-
ImGui::TableHeadersRow();
252-
253-
auto accessor = helper::getWriteAccessor(data);
254-
bool anyChange = false;
255-
for (std::size_t i = 0; i < accessor.size(); ++i)
256-
{
257-
ImGui::TableNextRow();
258-
ImGui::TableNextColumn();
259-
ImGui::Text("%d", i);
260-
auto& vec = accessor[i];
261-
if (showLine(i, tableLabel, vec))
262-
{
263-
anyChange = true;
264-
data.setDirtyValue();
265-
}
266-
}
267-
if (anyChange)
268-
{
269-
data.updateIfDirty();
270-
}
271-
272-
ImGui::EndTable();
273-
}
274-
}
275-
276182
template<>
277183
void DataWidget<type::vector<double> >::showWidget(MyData& data)
278184
{
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
/******************************************************************************
2+
* SOFA, Simulation Open-Framework Architecture *
3+
* (c) 2006 INRIA, USTL, UJF, CNRS, MGH *
4+
* *
5+
* This program is free software; you can redistribute it and/or modify it *
6+
* under the terms of the GNU General Public License as published by the Free *
7+
* Software Foundation; either version 2 of the License, or (at your option) *
8+
* any later version. *
9+
* *
10+
* This program is distributed in the hope that it will be useful, but WITHOUT *
11+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
12+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for *
13+
* more details. *
14+
* *
15+
* You should have received a copy of the GNU General Public License along *
16+
* with this program. If not, see <http://www.gnu.org/licenses/>. *
17+
*******************************************************************************
18+
* Authors: The SOFA Team and external contributors (see Authors.txt) *
19+
* *
20+
* Contact information: contact@sofa-framework.org *
21+
******************************************************************************/
22+
#pragma once
23+
#include <sofa/core/objectmodel/Data.h>
24+
#include <SofaImGui/widgets/ScalarWidget.h>
25+
#include <imgui.h>
26+
#include <unordered_map>
27+
#include <string>
28+
29+
namespace sofaimgui
30+
{
31+
32+
using namespace sofa;
33+
34+
/***********************************************************************************************************************
35+
* Vectors of Vec
36+
**********************************************************************************************************************/
37+
38+
template< Size N, typename ValueType>
39+
void showVecTableHeader(Data<type::vector<type::Vec<N, ValueType> > >&)
40+
{
41+
ImGui::TableSetupColumn("");
42+
for (unsigned int i = 0; i < N; ++i)
43+
{
44+
ImGui::TableSetupColumn(std::to_string(i).c_str(), ImGuiTableColumnFlags_WidthStretch);
45+
}
46+
}
47+
48+
template<typename ValueType>
49+
void showVecTableHeader(Data<type::vector<ValueType> >&)
50+
{
51+
ImGui::TableSetupColumn("");
52+
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch);
53+
}
54+
55+
template<typename ValueType>
56+
void showVecTableHeader(Data<type::vector<type::Vec<1, ValueType> > >&)
57+
{
58+
ImGui::TableSetupColumn("");
59+
ImGui::TableSetupColumn("X", ImGuiTableColumnFlags_WidthStretch);
60+
}
61+
62+
template<typename ValueType>
63+
void showVecTableHeader(Data<type::vector<type::Vec<2, ValueType> > >&)
64+
{
65+
ImGui::TableSetupColumn("");
66+
ImGui::TableSetupColumn("X", ImGuiTableColumnFlags_WidthStretch);
67+
ImGui::TableSetupColumn("Y", ImGuiTableColumnFlags_WidthStretch);
68+
}
69+
70+
template<typename ValueType>
71+
void showVecTableHeader(Data<type::vector<type::Vec<3, ValueType> > >&)
72+
{
73+
ImGui::TableSetupColumn("");
74+
ImGui::TableSetupColumn("X", ImGuiTableColumnFlags_WidthStretch);
75+
ImGui::TableSetupColumn("Y", ImGuiTableColumnFlags_WidthStretch);
76+
ImGui::TableSetupColumn("Z", ImGuiTableColumnFlags_WidthStretch);
77+
}
78+
79+
template<Size N, typename ValueType>
80+
bool showLine(unsigned int lineNumber, const std::string& tableLabel, type::Vec<N, ValueType>& vec)
81+
{
82+
for (const auto& v : vec)
83+
{
84+
ImGui::TableNextColumn();
85+
ImGui::Text("%f", v);
86+
}
87+
return false;
88+
}
89+
90+
template<typename ValueType>
91+
bool showLine(unsigned int lineNumber, const std::string& tableLabel, ValueType& value)
92+
{
93+
ImGui::TableNextColumn();
94+
return showScalarWidget("", tableLabel + std::to_string(lineNumber), value);
95+
}
96+
97+
namespace
98+
{
99+
enum class TableExpansionState
100+
{
101+
Collapsed,
102+
Expanded
103+
};
104+
105+
// Helper function to toggle expansion state
106+
void toggleExpansionState(TableExpansionState& expansionState)
107+
{
108+
expansionState = (expansionState == TableExpansionState::Expanded)
109+
? TableExpansionState::Collapsed
110+
: TableExpansionState::Expanded;
111+
}
112+
113+
// Helper function to get or initialize expanded state for a table
114+
TableExpansionState& getOrInitializeExpandedState(const std::string& tableLabel)
115+
{
116+
static std::unordered_map<std::string, TableExpansionState> expandedState;
117+
if (!expandedState.contains(tableLabel))
118+
{
119+
expandedState[tableLabel] = TableExpansionState::Collapsed;
120+
}
121+
return expandedState[tableLabel];
122+
}
123+
124+
// Helper function to display element count and toggle button
125+
void showVectorWidgetHeader(std::size_t elementsCount, const std::string& tableLabel, TableExpansionState& expansionState)
126+
{
127+
ImGui::Text("%d elements", elementsCount);
128+
ImGui::SameLine();
129+
const bool isExpanded = (expansionState == TableExpansionState::Expanded);
130+
131+
if (ImGui::Button(std::string(isExpanded ? "Collapse##" + tableLabel : "Expand##" + tableLabel).c_str()))
132+
{
133+
toggleExpansionState(expansionState);
134+
}
135+
}
136+
137+
// Helper function to render a single table row
138+
template<class AccessorType>
139+
bool renderTableRow(std::size_t index, const std::string& tableLabel, AccessorType& accessor)
140+
{
141+
ImGui::TableNextRow();
142+
ImGui::TableNextColumn();
143+
ImGui::Text("%d", index);
144+
auto& vec = accessor[index];
145+
return showLine(index, tableLabel, vec);
146+
}
147+
148+
// Helper function to render all rows when expanded
149+
template<class AccessorType>
150+
bool renderExpandedTableRows(AccessorType& accessor, const std::string& tableLabel)
151+
{
152+
bool anyChange = false;
153+
for (std::size_t i = 0; i < accessor.size(); ++i)
154+
{
155+
anyChange |= renderTableRow(i, tableLabel, accessor);
156+
}
157+
return anyChange;
158+
}
159+
}
160+
161+
template<class T>
162+
void showVectorWidget(Data<T>& data)
163+
{
164+
static ImGuiTableFlags flags = ImGuiTableFlags_ScrollY | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody | ImGuiTableFlags_NoHostExtendX;
165+
166+
const auto nbColumns = data.getValueTypeInfo()->size() + 1;
167+
const auto tableLabel = data.getName() + data.getOwner()->getPathName();
168+
const auto size = data.getValue().size();
169+
170+
TableExpansionState& expansionState = getOrInitializeExpandedState(tableLabel);
171+
showVectorWidgetHeader(size, tableLabel, expansionState);
172+
173+
if (expansionState == TableExpansionState::Collapsed)
174+
{
175+
flags |= ImGuiTableFlags_ScrollY;
176+
}
177+
else
178+
{
179+
flags &= ~ImGuiTableFlags_ScrollY;
180+
}
181+
182+
static constexpr float NumberOfLinesToShowWhenCollapsed = 8.5f;
183+
ImVec2 outerSize =ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * NumberOfLinesToShowWhenCollapsed );
184+
if (ImGui::BeginTable(tableLabel.c_str(), nbColumns, flags, outerSize))
185+
{
186+
ImGui::TableSetupScrollFreeze(0, 1);
187+
showVecTableHeader(data);
188+
ImGui::TableHeadersRow();
189+
190+
auto accessor = helper::getWriteAccessor(data);
191+
192+
if (renderExpandedTableRows(accessor, tableLabel))
193+
{
194+
data.updateIfDirty();
195+
}
196+
197+
ImGui::EndTable();
198+
}
199+
}
200+
201+
}

0 commit comments

Comments
 (0)