Divide Framework 0.1
A free and open-source 3D Framework under heavy development
Loading...
Searching...
No Matches
Utils.inl
Go to the documentation of this file.
1/*
2Copyright (c) 2021 DIVIDE-Studio
3Copyright (c) 2009 Ionut Cava
4
5This file is part of DIVIDE Framework.
6
7Permission is hereby granted, free of charge, to any person obtaining a copy
8of this software
9and associated documentation files (the "Software"), to deal in the Software
10without restriction,
11including without limitation the rights to use, copy, modify, merge, publish,
12distribute, sublicense,
13and/or sell copies of the Software, and to permit persons to whom the
14Software is furnished to do so,
15subject to the following conditions:
16
17The above copyright notice and this permission notice shall be included in
18all copies or substantial portions of the Software.
19
20THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21IMPLIED,
22INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
23PARTICULAR PURPOSE AND NONINFRINGEMENT.
24IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
25DAMAGES OR OTHER LIABILITY,
26WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
27IN CONNECTION WITH THE SOFTWARE
28OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29
30*/
31
32#pragma once
33#ifndef DVD_EDITOR_UTILS_INL_
34#define DVD_EDITOR_UTILS_INL_
35
39#include "Editor.h"
40#include "UndoManager.h"
43
44#include <imgui.h>
45
46namespace Divide::Util
47{
48
49template<typename T, bool SeparateActivate, typename Pred>
50inline void RegisterUndo(Editor& editor, PushConstantType type, const T& oldVal, const T& newVal, const char* name, Pred&& dataSetter)
51{
53
54 UndoEntry<T>& undo = _undoEntries[_ID(name)];
55 if (!SeparateActivate || ImGui::IsItemActivated())
56 {
57 undo._oldVal = oldVal;
58 }
59
60 if (ImGui::IsItemDeactivatedAfterEdit())
61 {
62 undo._type = type;
63 undo._name = name;
64 undo._newVal = newVal;
65 undo._dataSetter = dataSetter;
66 editor.registerUndoEntry(undo);
67 }
68}
69
70template<typename T, bool isSlider>
71inline DrawReturnValue DrawVecComponent(ImGuiDataType data_type,
72 const char* label,
73 T& value,
74 const T resetValue,
75 const T minValue,
76 const T maxValue,
77 const T step,
78 const T stepFast,
79 const ImVec4 buttonColour,
80 const ImVec4 buttonColourHovered,
81 const ImVec4 buttonColourActive,
82 ImGuiInputTextFlags flags,
83 const char* format)
84{
85 bool ret = false, wasDeactivated = false;
86
87 const T cStep = IS_ZERO( step ) ? 100 : (step * 100);
88 const void* step_ptr = IS_ZERO(step) ? nullptr : (void*)&step;
89 const void* step_fast_ptr = IS_ZERO(stepFast) ? (step_ptr == nullptr ? nullptr : (void*)&cStep) : (void*)&stepFast;
90
91 const F32 lineHeight = GetLineHeight();
92 const ImVec2 buttonSize = { lineHeight + 3.f, lineHeight };
93 const bool readOnly = (flags & ImGuiInputTextFlags_ReadOnly);
94 if (readOnly)
95 {
97 }
98
99 PushButtonStyle(true, buttonColour, buttonColourHovered, buttonColourActive);
100 if (ImGui::Button(label, buttonSize)) {
101 value = resetValue;
102 ret = true;
103 }
105
106 if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
107 ImGui::SetTooltip("Reset to {}", Util::StringFormat(format, resetValue).c_str());
108 }
109
110 ImGui::SameLine();
111
112 const string fieldName = Util::StringFormat( "##_value_{}_", label );
113 if constexpr(isSlider)
114 {
115 ret = ImGui::DragScalar( fieldName.c_str(), data_type, &value, 0.1f, &minValue, &maxValue, format, readOnly ? ImGuiSliderFlags_NoInput : 0u ) || ret;
116 }
117 else
118 {
119 ret = ImGui::InputScalar( fieldName.c_str(), data_type, &value, step_ptr, step_fast_ptr, format, flags) || ret;
120 }
121
122 if (ret && ImGui::IsItemDeactivated()) {
123 wasDeactivated = true;
124 }
125
126 if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled))
127 {
128 ImGui::SetTooltip(format, value);
129 }
130
131 if (readOnly)
132 {
133 PopReadOnly();
134 }
135
136 return { ret, wasDeactivated };
137}
138
139template<typename T, size_t N, bool isSlider>
140inline DrawReturnValue DrawVec(ImGuiDataType data_type,
141 const char* label,
142 const char* const compLabels[],
143 T* values,
144 const ImGuiInputTextFlags flags,
145 const T resetValue,
146 const T minValue,
147 const T maxValue,
148 const T step,
149 const T stepFast,
150 const char* format)
151{
152 bool ret = false, wasDeactivated = false;
153 BeginPropertyTable(N, label);
154 for (size_t i = 0; i < N; ++i)
155 {
156 const DrawReturnValue temp = DrawVecComponent<T, isSlider>(data_type, compLabels[i], values[i], resetValue, minValue, maxValue, step, stepFast, Colours[i % 3], ColoursHovered[i % 3], Colours[i % 3], flags, format);
157 ret = temp.wasChanged || ret;
158 wasDeactivated = temp.wasDeactivated || wasDeactivated;
159 ImGui::SameLine();
160 }
161 ImGui::Dummy(ImVec2(0,0));
163
164 return { ret, wasDeactivated };
165}
166
167template<typename T, size_t N, bool isSlider>
168FORCE_INLINE DrawReturnValue DrawVec(ImGuiDataType data_type,
169 const char* label,
170 T* values,
171 const ImGuiInputTextFlags flags,
172 T resetValue,
173 const char* format)
174{
175 return DrawVec<T, N, isSlider>(data_type, label, FieldLabels, values, flags, resetValue, T{ 0 }, T{ 0 }, T{ 0 }, T{ 0 }, format);
176}
177
178template<typename Pred>
179inline bool colourInput4(Editor& parent, const char* name, FColour4& col, const bool readOnly, Pred&& dataSetter)
180{
181 BeginPropertyTable(1, name);
182 if (readOnly)
183 {
184 PushReadOnly();
185 }
186
187 ImGui::PushID(name);
188 const bool ret = ImGui::ColorEdit4("", col._v, ImGuiColorEditFlags_DefaultOptions_);
189 ImGui::PopID();
190
191 if (readOnly)
192 {
193 PopReadOnly();
194 }
195
197
198 if (!readOnly && ret)
199 {
200 RegisterUndo<FColour4, true>(parent, PushConstantType::FCOLOUR4, col, col, name, dataSetter);
201 }
202
203 return readOnly ? false : (ret ? dataSetter(col) : false);
204}
205
206template<typename Pred>
207inline bool colourInput3(Editor& parent, const char* name, FColour3& col, const bool readOnly, Pred&& dataSetter)
208{
209 BeginPropertyTable(1, name);
210
211 if (readOnly)
212 {
213 PushReadOnly();
214 }
215
216 ImGui::PushID(name);
217 const bool ret = ImGui::ColorEdit3("", col._v, ImGuiColorEditFlags_DefaultOptions_);
218 ImGui::PopID();
219
220 if (readOnly)
221 {
222 PopReadOnly();
223 }
224
226
227 if (!readOnly && ret)
228 {
229 RegisterUndo<FColour3, true>(parent, PushConstantType::FCOLOUR3, col, col, name, dataSetter);
230 }
231
232 return readOnly ? false : (ret ? dataSetter(col) : false);
233}
234
235template<typename FieldDataType, typename ComponentType, size_t num_comp>
236inline bool inputOrSlider(Editor& parent, const bool isSlider, const char* label, const F32 stepIn, ImGuiDataType data_type, EditorComponentField& field, ImGuiInputTextFlags flags, const char* format)
237{
238 if (isSlider)
239 {
240 return inputOrSlider<FieldDataType, ComponentType, num_comp, true>(parent, label, stepIn, data_type, field, flags, format);
241 }
242
243 return inputOrSlider<FieldDataType, ComponentType, num_comp, false>(parent, label, stepIn, data_type, field, flags, format);
244}
245
246template<typename FieldDataType, typename ComponentType, size_t num_comp, bool IsSlider>
247inline bool inputOrSlider(Editor& parent, const char* label, const F32 stepIn, const ImGuiDataType data_type, EditorComponentField& field, const ImGuiInputTextFlags flags, const char* format)
248{
249 FieldDataType val = field.get<FieldDataType>();
250
251 const ComponentType cStep = static_cast<ComponentType>(stepIn * 100);
252 const ComponentType min = static_cast<ComponentType>(field._range.min);
253 const ComponentType max = static_cast<ComponentType>(field._range.max);
254
255 assert(min <= max);
256
257 // Validate bool flags vs input flags
258 DIVIDE_ASSERT(!field._readOnly || (flags & ImGuiInputTextFlags_ReadOnly));
259 DIVIDE_ASSERT(!field._hexadecimal || (flags & ImGuiInputTextFlags_CharsHexadecimal));
260
261 ComponentType* data = nullptr;
262 if constexpr (num_comp > 1)
263 {
264 data = &val[0];
265 }
266 else
267 {
268 data = &val;
269 }
270
271 const DrawReturnValue ret =
272 Util::DrawVec<ComponentType, num_comp, IsSlider>(data_type,
273 label,
274 field._labels == nullptr ? FieldLabels : field._labels,
275 data,
276 flags,
277 static_cast<ComponentType>(field._resetValue),
278 min,
279 max,
280 static_cast<ComponentType>(stepIn),
281 cStep,
282 GetFormat(data_type, format, field._hexadecimal));
283 if (ret.wasDeactivated && max > min)
284 {
285 if constexpr(num_comp > 1)
286 {
287 for (I32 i = 0; i < to_I32(num_comp); ++i)
288 {
289 val[i] = CLAMPED(val[i], min, max);
290 }
291 }
292 else
293 {
294 val = CLAMPED(val, min, max);
295 }
296 }
297
298 if (IsSlider || ret.wasChanged)
299 {
300 auto* tempData = field._data;
301 auto tempSetter = field._dataSetter;
302
303 RegisterUndo<FieldDataType, IsSlider>(parent, field._basicType, field.get<FieldDataType>(), val, label, [tempData, tempSetter](const FieldDataType& oldVal)
304 {
305 if (tempSetter != nullptr)
306 {
307 tempSetter(&oldVal);
308 }
309 else
310 {
311 *static_cast<FieldDataType*>(tempData) = oldVal;
312 }
313 });
314 }
315
316 if (!field._readOnly && ret.wasChanged && !COMPARE(val, field.get<FieldDataType>()))
317 {
318 field.set(val);
319 }
320
321 return ret.wasChanged;
322}
323
324template<typename T, size_t num_rows>
325inline bool inputMatrix(Editor & parent, const char* label, const F32 stepIn, const ImGuiDataType data_type, EditorComponentField& field, const ImGuiInputTextFlags flags, const char* format)
326{
327 ImGui::Separator();
328 ImGui::Text("[ %s ]", label);
329
330 if (Util::IsPushedTooltip() && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled))
331 {
332 ImGui::SetTooltip(Util::PushedToolTip());
333 }
334 const T cStep = static_cast<T>(stepIn * 100);
335 const void* step = IS_ZERO(stepIn) ? nullptr : (void*)&stepIn;
336 const void* step_fast = step == nullptr ? nullptr : (void*)&cStep;
337
338 T mat = field.get<T>();
339 if (field._readOnly)
340 {
341 PushReadOnly();
342 }
343
344 bool showTooltip = false, copyToClipboard = false;;
345 const char* parsedFormat = GetFormat(data_type, format, field._hexadecimal);
346 bool ret = false;
347
348 ImGui::PushItemWidth(250);
349 ret = ImGui::InputScalarN(Util::StringFormat("##{}_0", label).c_str(), data_type, (void*)mat._vec[0]._v, num_rows, step, step_fast, parsedFormat, flags);
350 showTooltip = ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) || showTooltip;
351 copyToClipboard = (showTooltip && ImGui::IsMouseClicked(0)) || copyToClipboard;
352
353 ret = ImGui::InputScalarN(Util::StringFormat("##{}_1", label).c_str(), data_type, (void*)mat._vec[1]._v, num_rows, step, step_fast, parsedFormat, flags) || ret;
354 showTooltip = ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) || showTooltip;
355 copyToClipboard = (showTooltip && ImGui::IsMouseClicked(0)) || copyToClipboard;
356
357 if constexpr(num_rows > 2)
358 {
359 ret = ImGui::InputScalarN(Util::StringFormat("##{}_2", label).c_str(), data_type, (void*)mat._vec[2]._v, num_rows, step, step_fast, parsedFormat, flags) || ret;
360 showTooltip = ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) || showTooltip;
361 copyToClipboard = (showTooltip && ImGui::IsMouseClicked(0)) || copyToClipboard;
362
363 if constexpr(num_rows > 3)
364 {
365 ret = ImGui::InputScalarN(Util::StringFormat("##{}_3", label).c_str(), data_type, (void*)mat._vec[3]._v, num_rows, step, step_fast, parsedFormat, flags) || ret;
366 showTooltip = ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) || showTooltip;
367 copyToClipboard = (showTooltip && ImGui::IsMouseClicked(0)) || copyToClipboard;
368 }
369 }
370
371 ImGui::PopItemWidth();
372 if (field._readOnly)
373 {
374 PopReadOnly();
375 }
376
377 if (showTooltip && field._readOnly)
378 {
379 const char* tooltip = "\n\nClick to copy values to clipboard.";
380 string matrixText = "";
381
382 for (size_t i = 0; i < num_rows; ++i)
383 {
384 auto& row = mat.m[i];
385 for (size_t j = 0; j < num_rows; ++j)
386 {
387 if (row[j] >= 0.f)
388 {
389 matrixText.append(" ");
390 }
391
392 matrixText.append(Util::StringFormat(parsedFormat, row[j]));
393 if (j < num_rows - 1)
394 {
395 matrixText.append(" ");
396 }
397 }
398
399 if (i < num_rows - 1)
400 {
401 matrixText.append("\n\n");
402 }
403 }
404
405 ImGui::SetTooltip((matrixText + tooltip).c_str());
406 if (copyToClipboard)
407 {
408 SetClipboardText(matrixText.c_str());
409 parent.showStatusMessage("Copied values to clipboard!", Time::SecondsToMilliseconds<F32>(3.f), false);
410 }
411 }
412
413 if (ret && !field._readOnly && mat != field.get<T>())
414 {
415 auto* tempData = field._data;
416 auto tempSetter = field._dataSetter;
417 RegisterUndo<T, false>(parent, field._basicType, field.get<T>(), mat, label, [tempData, tempSetter](const T& oldVal)
418 {
419 if (tempSetter != nullptr)
420 {
421 tempSetter(&oldVal);
422 }
423 else
424 {
425 *static_cast<T*>(tempData) = oldVal;
426 }
427 });
428 field.set<>(mat);
429 }
430
431 ImGui::Separator();
432
433 return ret;
434}
435
436} //namespace Divide::Util
437
438#endif //DVD_EDITOR_UTILS_INL_
#define DIVIDE_ASSERT(...)
#define NO_DESTROY
#define FORCE_INLINE
void registerUndoEntry(const UndoEntry< T > &entry)
Definition: Editor.inl:62
DrawReturnValue DrawVec(ImGuiDataType data_type, const char *label, T *values, ImGuiInputTextFlags flags, T resetValue=0, const char *format="%.2f")
Definition: Utils.inl:168
Str StringFormat(const char *fmt, Args &&...args)
void RegisterUndo(Editor &editor, PushConstantType type, const T &oldVal, const T &newVal, const char *name, Pred &&dataSetter)
Definition: Utils.inl:50
void PopButtonStyle()
Definition: Utils.cpp:350
F32 GetLineHeight() noexcept
Definition: Utils.cpp:224
static const ImVec4 Colours[]
Definition: Utils.h:58
bool colourInput4(Editor &parent, EditorComponentField &field)
Definition: Utils.cpp:374
static const ImVec4 ColoursHovered[]
Definition: Utils.h:63
void EndPropertyTable()
Definition: Utils.cpp:271
void PushButtonStyle(bool bold, ImVec4 buttonColour, ImVec4 buttonColourHovered, ImVec4 buttonColourActive)
Definition: Utils.cpp:336
bool colourInput3(Editor &parent, EditorComponentField &field)
Definition: Utils.cpp:390
bool inputOrSlider(Editor &parent, const bool isSlider, const char *label, const F32 stepIn, ImGuiDataType data_type, EditorComponentField &field, ImGuiInputTextFlags flags, const char *format)
Definition: Utils.inl:236
bool inputMatrix(Editor &parent, const char *label, const F32 stepIn, const ImGuiDataType data_type, EditorComponentField &field, const ImGuiInputTextFlags flags, const char *format)
Definition: Utils.inl:325
const char * GetFormat(ImGuiDataType dataType, const char *input, bool hex)
Definition: Utils.cpp:357
DrawReturnValue DrawVecComponent(ImGuiDataType data_type, const char *label, T &value, T resetValue, T minValue, T maxValue, T step, T stepFast, ImVec4 buttonColour, ImVec4 buttonColourHovered, ImVec4 buttonColourActive, ImGuiInputTextFlags flags, const char *format="%.2f")
Definition: Utils.inl:71
constexpr const char * FieldLabels[]
Definition: Utils.h:69
void BeginPropertyTable(I32 numComponents, const char *label)
Definition: Utils.cpp:237
void SetClipboardText(const char *text) noexcept
bool IS_ZERO(const T X) noexcept
void PushReadOnly(const bool fade)
Definition: Editor.cpp:2961
int32_t I32
void PopReadOnly()
Definition: Editor.cpp:2971
Project & parent
Definition: DefaultScene.h:41
constexpr U64 _ID(const char *const str, const U64 value=val_64_const) noexcept
::value constexpr T CLAMPED(T n, T min, T max) noexcept
Definition: MathHelper.inl:126
bool COMPARE(T X, U Y) noexcept
constexpr I32 to_I32(const T value)
hashAlg::unordered_map< K, V, HashFun, Predicate > hashMapDefaultAlloc
Definition: HashMap.h:52
void set(const T &dataIn)
DELEGATE_STD< void, const void * > _dataSetter
vec2< F32 > _range
Used by slider_type as a min / max range or dropdown as selected_index / count.
const char *const * _labels
PushConstantType _type
Definition: UndoManager.h:42
string _name
Definition: UndoManager.h:43
Definition: UndoManager.h:51
T _oldVal
Definition: UndoManager.h:52
T _newVal
Definition: UndoManager.h:53
DELEGATE_STD< void, const T & > _dataSetter
Definition: UndoManager.h:54