Skip to content

Commit 90471bb

Browse files
committed
[misc] multiple daily accumullated fixes for editor ui/ux and car simulation
1 parent a09f08a commit 90471bb

22 files changed

Lines changed: 8013 additions & 2828 deletions

source/editor/ImGui/ImGui_Extension.h

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2323

2424
//= INCLUDES ======================
2525
#include <string>
26-
#include <variant>
26+
#include <cstring>
2727
#include <unordered_map>
2828
#include "Definitions.h"
2929
#include "Logging/Log.h"
@@ -198,18 +198,28 @@ namespace ImGuiSp
198198
);
199199
}
200200

201+
// self contained drag drop payload, paths are embedded directly so the
202+
// payload survives any reallocation or refresh of the source asset list
201203
struct DragDropPayload
202204
{
203-
using DataVariant = std::variant<const char*, uint64_t>;
204-
DragDropPayload(const DragPayloadType type = DragPayloadType::Undefined, const DataVariant data = nullptr, const char* path_relative = nullptr)
205+
static constexpr size_t max_path_length = 512;
206+
DragPayloadType type = DragPayloadType::Undefined;
207+
char path[max_path_length] = {};
208+
char path_relative[max_path_length] = {};
209+
210+
void set_paths(const char* full, const char* relative)
205211
{
206-
this->type = type;
207-
this->data = data;
208-
this->path_relative = path_relative;
212+
path[0] = '\0';
213+
path_relative[0] = '\0';
214+
if (full)
215+
{
216+
strncpy_s(path, max_path_length, full, _TRUNCATE);
217+
}
218+
if (relative)
219+
{
220+
strncpy_s(path_relative, max_path_length, relative, _TRUNCATE);
221+
}
209222
}
210-
DragPayloadType type;
211-
DataVariant data; // full/absolute path (for backward compatibility)
212-
const char* path_relative; // relative path
213223
};
214224

215225
static void create_drag_drop_payload(const DragDropPayload& payload)
@@ -221,11 +231,12 @@ namespace ImGuiSp
221231
{
222232
if (ImGui::BeginDragDropTarget())
223233
{
224-
if (const ImGuiPayload* payload_imgui = ImGui::AcceptDragDropPayload(GDragDropTypes[(int)type].data()))
234+
const ImGuiPayload* payload_imgui = ImGui::AcceptDragDropPayload(GDragDropTypes[(int)type].data());
235+
ImGui::EndDragDropTarget();
236+
if (payload_imgui && payload_imgui->DataSize >= static_cast<int>(sizeof(DragDropPayload)))
225237
{
226238
return static_cast<DragDropPayload*>(payload_imgui->Data);
227239
}
228-
ImGui::EndDragDropTarget();
229240
}
230241

231242
return nullptr;
@@ -306,14 +317,13 @@ namespace ImGuiSp
306317
// drop target
307318
if (auto payload = receive_drag_drop_payload(DragPayloadType::Texture))
308319
{
309-
try
320+
if (payload->path[0] != '\0')
310321
{
311-
if (const auto tex = spartan::ResourceCache::Load<spartan::RHI_Texture>(std::get<const char*>(payload->data)).get())
322+
if (const auto tex = spartan::ResourceCache::Load<spartan::RHI_Texture>(payload->path).get())
312323
{
313324
setter(tex);
314325
}
315326
}
316-
catch (const std::bad_variant_access& e) { SP_LOG_ERROR("%s", e.what()); }
317327
}
318328

319329
return clicked_for_browse;

source/editor/Widgets/FileDialog.cpp

Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ FileDialog::FileDialog(const bool standalone_window, const FileDialog_Type type,
160160
m_hover_animation = 0.0f;
161161
m_is_renaming = false;
162162
m_rename_request_focus = false;
163+
m_rename_select_pending = false;
163164
m_rename_item_id = UINT32_MAX;
164165
m_context_menu_id = 0;
165166
}
@@ -291,6 +292,8 @@ void FileDialog::ShowOverwriteDialog(string* directory, string* file_path)
291292
ImGui::SetNextWindowSize(ImVec2(420, 0), ImGuiCond_Appearing);
292293
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(16, 16));
293294
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 8.0f);
295+
// suppress the dim overlay that imgui draws behind modal popups so the rest of the editor stays visible
296+
ImGui::PushStyleColor(ImGuiCol_ModalWindowDimBg, ImVec4(0, 0, 0, 0));
294297

295298
if (ImGui::BeginPopupModal("##overwrite_dialog", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar))
296299
{
@@ -337,6 +340,7 @@ void FileDialog::ShowOverwriteDialog(string* directory, string* file_path)
337340
ImGui::EndPopup();
338341
}
339342

343+
ImGui::PopStyleColor();
340344
ImGui::PopStyleVar(2);
341345
}
342346

@@ -766,13 +770,17 @@ void FileDialog::RenderGridView()
766770

767771
ImGui::PushID(static_cast<int>(i));
768772

773+
// wrap the whole cell in a group so the rename input text (if any) cannot
774+
// become the trailing item that SameLine() snaps to, which would break tiling
775+
ImGui::BeginGroup();
776+
769777
ImVec2 screen_pos = ImGui::GetCursorScreenPos();
770778

771779
// card dimensions
772780
ImVec2 card_min = screen_pos;
773781
ImVec2 card_max = ImVec2(screen_pos.x + item_width - 4, screen_pos.y + item_height - 4);
774782

775-
// invisible button for interaction - this is the only item we submit
783+
// invisible button for interaction
776784
ImGui::InvisibleButton("##card", ImVec2(item_width - 4, item_height - 4));
777785
bool is_hovered = ImGui::IsItemHovered();
778786
bool is_selected = (m_selected_item_id == item.GetId());
@@ -919,6 +927,8 @@ void FileDialog::RenderGridView()
919927
ItemClick(&item);
920928
ItemContextMenu(&item);
921929

930+
ImGui::EndGroup();
931+
922932
ImGui::PopID();
923933

924934
// layout: new row when columns are full
@@ -1170,9 +1180,8 @@ void FileDialog::ItemDrag(FileDialogItem* item)
11701180
m_was_dragging = true;
11711181
const auto set_payload = [this](const ImGuiSp::DragPayloadType type, const string& path_full, const string& path_relative)
11721182
{
1173-
m_drag_drop_payload.type = type;
1174-
m_drag_drop_payload.data = path_full.c_str();
1175-
m_drag_drop_payload.path_relative = path_relative.c_str();
1183+
m_drag_drop_payload.type = type;
1184+
m_drag_drop_payload.set_paths(path_full.c_str(), path_relative.c_str());
11761185
ImGuiSp::create_drag_drop_payload(m_drag_drop_payload);
11771186
};
11781187

@@ -1227,10 +1236,11 @@ void FileDialog::ItemContextMenu(FileDialogItem* item)
12271236

12281237
if (ImGui::MenuItem("Rename"))
12291238
{
1230-
m_is_renaming = true;
1231-
m_rename_request_focus = true;
1232-
m_rename_buffer = item->GetLabel();
1233-
m_rename_item_id = item->GetId();
1239+
m_is_renaming = true;
1240+
m_rename_request_focus = true;
1241+
m_rename_select_pending = true;
1242+
m_rename_buffer = item->GetLabel();
1243+
m_rename_item_id = item->GetId();
12341244
}
12351245

12361246
if (FileSystem::IsEngineLuaFile(item->GetPath()))
@@ -1288,10 +1298,11 @@ void FileDialog::DialogUpdateFromDirectory(const string& file_path)
12881298

12891299
lock_guard<mutex> lock(m_mutex_items);
12901300
m_items.clear();
1291-
m_selected_item_id = UINT32_MAX;
1292-
m_is_renaming = false;
1293-
m_rename_request_focus = false;
1294-
m_rename_item_id = UINT32_MAX;
1301+
m_selected_item_id = UINT32_MAX;
1302+
m_is_renaming = false;
1303+
m_rename_request_focus = false;
1304+
m_rename_select_pending = false;
1305+
m_rename_item_id = UINT32_MAX;
12951306

12961307
// directories first
12971308
auto directories = FileSystem::GetDirectoriesInDirectory(file_path);
@@ -1420,8 +1431,32 @@ void FileDialog::RenameItemInline(FileDialogItem* item, float width)
14201431
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4, 2));
14211432
ImGui::SetNextItemWidth(width);
14221433

1423-
const ImGuiInputTextFlags flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll;
1424-
const bool committed = ImGui::InputText("##rename_inline", &m_rename_buffer, flags);
1434+
// on first activation, select only the stem (filename without the extension)
1435+
// so the user does not accidentally type over the dot extension
1436+
auto select_stem_callback = [](ImGuiInputTextCallbackData* data) -> int
1437+
{
1438+
bool* pending = static_cast<bool*>(data->UserData);
1439+
if (pending && *pending)
1440+
{
1441+
int stem_len = data->BufTextLen;
1442+
for (int i = data->BufTextLen - 1; i > 0; --i)
1443+
{
1444+
if (data->Buf[i] == '.')
1445+
{
1446+
stem_len = i;
1447+
break;
1448+
}
1449+
}
1450+
data->SelectionStart = 0;
1451+
data->SelectionEnd = stem_len;
1452+
data->CursorPos = stem_len;
1453+
*pending = false;
1454+
}
1455+
return 0;
1456+
};
1457+
1458+
const ImGuiInputTextFlags flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CallbackAlways;
1459+
const bool committed = ImGui::InputText("##rename_inline", &m_rename_buffer, flags, select_stem_callback, &m_rename_select_pending);
14251460
const bool deactivated = ImGui::IsItemDeactivated();
14261461
const bool escape_pressed = ImGui::IsKeyPressed(ImGuiKey_Escape);
14271462

source/editor/Widgets/FileDialog.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ class FileDialog
190190
// renaming
191191
bool m_is_renaming;
192192
bool m_rename_request_focus;
193+
bool m_rename_select_pending;
193194
std::string m_rename_buffer;
194195
uint32_t m_rename_item_id;
195196

source/editor/Widgets/MenuBar.cpp

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,23 @@ namespace
8989
show_file_dialog = true;
9090
}
9191

92+
// save the current world to its existing path, falls back to the save-as dialog
93+
// when there is no loaded world yet so the user can pick a destination
94+
void SaveWorld()
95+
{
96+
const std::string& world_file_path = spartan::World::GetFilePath();
97+
if (world_file_path.empty())
98+
{
99+
ShowWorldSaveDialog();
100+
return;
101+
}
102+
103+
spartan::ThreadPool::AddTask([world_file_path]()
104+
{
105+
spartan::World::SaveToFile(world_file_path);
106+
});
107+
}
108+
92109
void ShowWorldLoadDialog()
93110
{
94111
file_dialog->SetOperation(FileDialog_Op_Load);
@@ -192,10 +209,10 @@ namespace
192209

193210
if (ImGui::MenuItem("Save", "Ctrl+S"))
194211
{
195-
windows::ShowWorldSaveDialog();
212+
windows::SaveWorld();
196213
}
197214

198-
if (ImGui::MenuItem("Save As...", "Ctrl+S"))
215+
if (ImGui::MenuItem("Save As...", "Ctrl+Shift+S"))
199216
{
200217
windows::ShowWorldSaveDialog();
201218
}
@@ -662,6 +679,24 @@ void MenuBar::Tick()
662679
{
663680
buttons_toolbar::toggle_paused();
664681
}
682+
683+
// world save shortcuts, gated on no text input so we don't hijack typing in fields
684+
if (!ImGui::GetIO().WantTextInput)
685+
{
686+
const bool ctrl = ImGui::GetIO().KeyCtrl;
687+
const bool shift = ImGui::GetIO().KeyShift;
688+
if (ctrl && ImGui::IsKeyPressed(ImGuiKey_S, false))
689+
{
690+
if (shift)
691+
{
692+
windows::ShowWorldSaveDialog();
693+
}
694+
else
695+
{
696+
windows::SaveWorld();
697+
}
698+
}
699+
}
665700
}
666701

667702
// menu bar

source/editor/Widgets/Properties.cpp

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -847,7 +847,10 @@ void Properties::ShowScript(spartan::Script* script) const
847847
// drag-drop support for lua files
848848
if (auto* payload = ImGuiSp::receive_drag_drop_payload(ImGuiSp::DragPayloadType::Lua))
849849
{
850-
script->LoadScriptFile(std::get<const char*>(payload->data));
850+
if (payload->path[0] != '\0')
851+
{
852+
script->LoadScriptFile(payload->path);
853+
}
851854
}
852855

853856
// status
@@ -1319,7 +1322,10 @@ void Properties::ShowRender(spartan::Render* renderable) const
13191322
// drag drop for material
13201323
if (auto payload = ImGuiSp::receive_drag_drop_payload(ImGuiSp::DragPayloadType::Material))
13211324
{
1322-
renderable->SetMaterial(std::get<const char*>(payload->data));
1325+
if (payload->path[0] != '\0')
1326+
{
1327+
renderable->SetMaterial(payload->path);
1328+
}
13231329
}
13241330

13251331
// browse
@@ -1561,7 +1567,8 @@ void Properties::ShowMaterial(Material* material, Render* renderable) const
15611567
return;
15621568
}
15631569

1564-
if (component_begin("Material", design::accent_material(), nullptr, false))
1570+
const bool default_open = renderable == nullptr;
1571+
if (component_begin("Material", design::accent_material(), nullptr, false, true, default_open))
15651572
{
15661573
// when shown with a renderable, uv edits go to the renderable's override, not the material asset,
15671574
// so multiple renderables sharing this material can each have their own uv tweak
@@ -2565,7 +2572,10 @@ void Properties::ShowAudioSource(spartan::AudioSource* audio_source) const
25652572

25662573
if (auto payload = ImGuiSp::receive_drag_drop_payload(ImGuiSp::DragPayloadType::Audio))
25672574
{
2568-
audio_source->SetAudioClip(std::get<const char*>(payload->data));
2575+
if (payload->path[0] != '\0')
2576+
{
2577+
audio_source->SetAudioClip(payload->path);
2578+
}
25692579
}
25702580

25712581
layout::separator();

0 commit comments

Comments
 (0)