Olympe Engine 2.0
2D Game Engine with ECS Architecture
Loading...
Searching...
No Matches
PanelManager.cpp
Go to the documentation of this file.
1/*
2Olympe Engine V2 2025
3Nicolas Chereau
4nchereau@gmail.com
5
6Purpose:
7- PanelManager implementation. Creates simple Win32 child/floating windows
8 to host debug panels (log window, object inspector, tree view). The
9 actual rendering/content of windows will be implemented later.
10
11Notes:
12- This file uses minimal Win32 APIs guarded by #ifdef _WIN32 so it remains
13 portable on other platforms (stubs will be used).
14*/
15
16#include "PanelManager.h"
17#include "GameEngine.h"
18#include "system/EventManager.h"
19#include <mutex>
20#include "inputsmanager.h"
21
22#ifdef _WIN32
23#include <strsafe.h>
24#if defined(__has_include)
25 #if __has_include(<SDL3/SDL_syswm.h>)
26 #include <SDL3/SDL_syswm.h>
27 #define HAVE_SDL_SYSWM 1
28 #endif
29#endif
30#endif
31
32//----------------------------------------------------------------------
33// initialize static vairables for panel sizes and positions
46
47//----------------------------------------------------------------------
49{
50 name = "PanelManager";
51}
52
54{
55 SYSTEM_LOG << "PanelManager destroyed\n";
56 Shutdown();
57}
58
64
66{
73
75
76#ifdef _WIN32
77 // Register a small window class for panels
78 WNDCLASSEX wc = {};
79 wc.cbSize = sizeof(wc);
80 wc.style = CS_HREDRAW | CS_VREDRAW;
81 wc.lpfnWndProc = PanelWndProc;
82 wc.hInstance = GetModuleHandle(nullptr);
83 wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
84 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
85 wc.lpszClassName = TEXT("OlympePanelClass");
87 if (!m_wndClassAtom) SYSTEM_LOG << "PanelManager: RegisterClassEx failed\n";
88#endif
89}
90
92{
93#ifdef _WIN32
94 for (auto &kv : m_panels_)
95 {
96 if (kv.second.hwnd) DestroyWindow(kv.second.hwnd);
97 kv.second.hwnd = nullptr;
98 kv.second.hwndChild = nullptr;
99 if (kv.second.hMenu) {
100 DestroyMenu(kv.second.hMenu);
101 kv.second.hMenu = nullptr;
102 }
103 }
104 if (m_mainMenu && m_mainHwnd) {
105 SetMenu(m_mainHwnd, nullptr);
107 m_mainMenu = nullptr;
108 }
109 if (m_wndClassAtom) UnregisterClass(TEXT("OlympePanelClass"), GetModuleHandle(nullptr));
110 m_wndClassAtom = 0;
111#else
112 m_panels_.clear();
113#endif
114}
115
116void PanelManager::CreatePanel(const std::string& id, const std::string& title)
117{
118 auto it = m_panels_.find(id);
119 if (it != m_panels_.end()) return; // already created
120
121 Panel p;
122 p.id = id;
123 p.title = title;
124 p.visible = true;
125
126#ifdef _WIN32
127 HWND parent = nullptr;
128 HWND hwnd = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_WINDOWEDGE, TEXT("OlympePanelClass"), TEXT(""),
130 CW_USEDEFAULT, CW_USEDEFAULT, 400, 300,
131 parent, nullptr, GetModuleHandle(nullptr), nullptr);
132 if (hwnd)
133 {
134 SetWindowTextA(hwnd, title.c_str());
135 p.hwnd = hwnd;
136
137 if (id == "log_window")
138 {
139 HWND hEdit = CreateWindowEx(0, TEXT("EDIT"), TEXT(""),
141 0, 0, 400, 300, hwnd, nullptr, GetModuleHandle(nullptr), nullptr);
142 if (hEdit)
143 {
146 p.hwndChild = hEdit;
147 }
148 }
149 else if (id == "inputs_inspector")
150 {
151 // create a multiline edit control to display joystick list
152 HWND hEdit = CreateWindowEx(0, TEXT("EDIT"), TEXT(""),
154 0, 0, 400, 300, hwnd, nullptr, GetModuleHandle(nullptr), nullptr);
155 if (hEdit)
156 {
159 p.hwndChild = hEdit;
160 // populate initial list
161 std::string text = "(initializing)\r\n";
162 SetWindowTextA(hEdit, text.c_str());
163 }
164 }
165
166 ShowWindow(hwnd, SW_SHOW);
167 if (p.hwndChild) ShowWindow(p.hwndChild, SW_SHOW);
168 }
169#endif
170
171 m_panels_.emplace(id, std::move(p));
172}
173
175{
176 CreatePanel("log_window", "Log Window");
177#ifdef _WIN32
178 auto it = m_panels_.find("log_window");
179 if (it != m_panels_.end() && it->second.hwnd)
180 {
181 HWND hwnd = it->second.hwnd;
183 if (it->second.hwndChild)
184 {
185 RECT rc; GetClientRect(hwnd, &rc);
186 MoveWindow(it->second.hwndChild, 0, 0, rc.right - rc.left, rc.bottom - rc.top, TRUE);
188 }
189 }
190#endif
191}
192
194{
195 CreatePanel("object_inspector", "Object Inspector");
196}
197
199{
200 CreatePanel("tree_view", "Tree View");
201}
202
204{
205 CreatePanel("main_menu", "Olympe Main Menu");
206#ifdef _WIN32
207 auto it = m_panels_.find("main_menu");
208 if (it == m_panels_.end() || !it->second.hwnd) return;
209
210 HWND hwnd = it->second.hwnd;
211
213 AppendMenuA(hFile, MF_STRING, IDM_FILE_NEW, "New\tCtrl+N");
214 AppendMenuA(hFile, MF_STRING, IDM_FILE_LOAD, "Load\tCtrl+L");
215 AppendMenuA(hFile, MF_STRING, IDM_FILE_SAVE, "Save\tCtrl+S");
216
218 UINT fl = IsPanelVisible("log_window") ? MF_CHECKED : MF_UNCHECKED;
219 UINT fi = IsPanelVisible("object_inspector") ? MF_CHECKED : MF_UNCHECKED;
220 UINT ft = IsPanelVisible("tree_view") ? MF_CHECKED : MF_UNCHECKED;
221 UINT fi2 = IsPanelVisible("inputs_inspector") ? MF_CHECKED : MF_UNCHECKED;
222 AppendMenuA(hOptions, MF_STRING | fl, IDM_WINDOW_LOG, "Display Log Window");
223 AppendMenuA(hOptions, MF_STRING | fi, IDM_WINDOW_OBJECT_INSPECTOR, "Display Object Inspector");
224 AppendMenuA(hOptions, MF_STRING | ft, IDM_WINDOW_OBJECT_HIERARCHY, "Display Objects Hierarchy");
225 AppendMenuA(hOptions, MF_STRING | fi2, IDM_WINDOW_INPUTS, "Display Inputs Inspector");
226
228 AppendMenuA(hAbout, MF_STRING, IDM_ABOUT, "About Olympe Engine");
229
234
235 SetMenu(hwnd, hMenu);
236 it->second.hMenu = hMenu;
238 m_mainHwnd = hwnd;
239#endif
240}
241
242void PanelManager::ShowPanel(const std::string& id)
243{
244 auto it = m_panels_.find(id);
245 if (it == m_panels_.end()) return;
246 it->second.visible = true;
247#ifdef _WIN32
248 if (it->second.hwnd) ShowWindow(it->second.hwnd, SW_SHOW);
249 if (it->second.hwndChild) ShowWindow(it->second.hwndChild, SW_SHOW);
250#endif
251}
252
253void PanelManager::HidePanel(const std::string& id)
254{
255 auto it = m_panels_.find(id);
256 if (it == m_panels_.end()) return;
257 it->second.visible = false;
258#ifdef _WIN32
259 if (it->second.hwnd) ShowWindow(it->second.hwnd, SW_HIDE);
260 if (it->second.hwndChild) ShowWindow(it->second.hwndChild, SW_HIDE);
261#endif
262}
263
264bool PanelManager::IsPanelVisible(const std::string& id) const
265{
266 auto it = m_panels_.find(id);
267 if (it == m_panels_.end()) return false;
268 return it->second.visible;
269}
270
271void PanelManager::AppendLog(const std::string& text)
272{
273 auto it = m_panels_.find("log_window");
274 if (it == m_panels_.end()) return;
275#ifdef _WIN32
276 HWND hEdit = it->second.hwndChild;
277 if (!hEdit) return;
280 std::string s = text + "\r\n";
283#endif
284}
285
287{
288#ifdef _WIN32
289 MSG msg;
290 while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
291 {
292 if (msg.message == WM_COMMAND)
293 {
294 int id = LOWORD(msg.wParam);
295 switch (id)
296 {
297 case IDM_PANEL_LOG:
298 case IDM_WINDOW_LOG:
299 if (IsPanelVisible("log_window")) HidePanel("log_window"); else ShowPanel("log_window");
301 continue;
304 if (IsPanelVisible("object_inspector")) HidePanel("object_inspector"); else ShowPanel("object_inspector");
306 continue;
307 case IDM_PANEL_TREE:
309 if (IsPanelVisible("tree_view")) HidePanel("tree_view"); else ShowPanel("tree_view");
311 continue;
312 case IDM_PANEL_INPUTS:
314 if (IsPanelVisible("inputs_inspector")) HidePanel("inputs_inspector"); else ShowPanel("inputs_inspector");
316 // Update list when shown
317 if (IsPanelVisible("inputs_inspector")) {
318 auto pit = m_panels_.find("inputs_inspector");
319 if (pit != m_panels_.end() && pit->second.hwndChild) {
321 }
322 }
323 continue;
324 case IDM_FILE_NEW:
325 SYSTEM_LOG << "Menu: File->New selected (not implemented)\n";
326 continue;
327 case IDM_FILE_LOAD:
328 SYSTEM_LOG << "Menu: File->Load selected (not implemented)\n";
329 continue;
330 case IDM_FILE_SAVE:
331 SYSTEM_LOG << "Menu: File->Save selected (not implemented)\n";
332 continue;
333 case IDM_ABOUT:
334 MessageBoxA(m_mainHwnd, "Olympe Engine V2\n\nNicolas Chereau - 2025-2026\n\n n chereau@gmail.com\nhttps://github.com/Atlasbruce/Olympe-Engine/", "About Olympe Engine", MB_OK | MB_ICONINFORMATION);
335 continue;
336 default:
337 break;
338 }
339 }
340
343 }
344#endif
345
346 // Handle SDL_Event Message
347
348}
349//----------------------------------------------------------------------
351{
352 switch (msg.msg_type)
353 {
360 {
361 if (IsPanelVisible("inputs_inspector"))
362 {
363 auto pit = m_panels_.find("inputs_inspector");
364 if (pit != m_panels_.end() && pit->second.hwndChild)
365 {
367 }
368 }
369 break;
370 }
371 default:
372 break;
373 }
374}
375#ifdef _WIN32
377{
378#ifdef HAVE_SDL_SYSWM
379 if (!sdlWindow) return;
381 SDL_VERSION(&wmi.version);
383 return;
384 }
385
386 HWND hwnd = (HWND)wmi.info.win.window;
387 if (!hwnd) return;
388
389 m_mainHwnd = hwnd;
390
392 AppendMenuA(hFile, MF_STRING, IDM_FILE_NEW, "New\tCtrl+N");
393 AppendMenuA(hFile, MF_STRING, IDM_FILE_LOAD, "Load\tCtrl+L");
394 AppendMenuA(hFile, MF_STRING, IDM_FILE_SAVE, "Save\tCtrl+S");
395
397 AppendMenuA(hWindow, MF_STRING, IDM_WINDOW_OBJECT_INSPECTOR, "Object Inspector\tF2");
398 AppendMenuA(hWindow, MF_STRING, IDM_WINDOW_OBJECT_HIERARCHY, "Object Hierarchy\tF3");
400
402 AppendMenuA(hAbout, MF_STRING, IDM_ABOUT, "About Olympe Engine");
403
408
409 SetMenu(hwnd, hMenu);
411#else
413#endif
414}
415#endif
416
417#if defined(_WIN32)
418LRESULT CALLBACK PanelManager::PanelWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
419{
420 switch (msg)
421 {
422 case WM_CLOSE:
423 ShowWindow(hwnd, SW_HIDE);
424 return 0;
425 case WM_SIZE:
426 {
428 std::lock_guard<std::mutex> lock(mgr.m_mutex_);
429 for (auto &kv : mgr.m_panels_)
430 {
431 if (kv.second.hwnd == hwnd)
432 {
433 HWND hChild = kv.second.hwndChild;
434 if (hChild)
435 {
436 RECT rc; GetClientRect(hwnd, &rc);
437 MoveWindow(hChild, 0, 0, rc.right - rc.left, rc.bottom - rc.top, TRUE);
438 }
439 break;
440 }
441 }
442 return 0;
443 }
444 case WM_DESTROY:
445 return 0;
446 default:
447 return DefWindowProc(hwnd, msg, wParam, lParam);
448 }
449}
450#endif
451
453{
454 void AppendToLogWindow(const std::string& text)
455 {
457 }
458}
459
461{
462 CreatePanel("inputs_inspector", "Inputs Inspector");
463#ifdef _WIN32
464 auto it = m_panels_.find("inputs_inspector");
465 if (it != m_panels_.end() && it->second.hwnd)
466 {
467 HWND hwnd = it->second.hwnd;
468 // default size similar to inspector
470 if (it->second.hwndChild)
471 {
472 RECT rc; GetClientRect(hwnd, &rc);
473 MoveWindow(it->second.hwndChild, 0, 0, rc.right - rc.left, rc.bottom - rc.top, TRUE);
474 // Position the panel near inspector by default
476 }
477 // populate initial list
479 }
480#endif
481}
482
484{
485#ifdef _WIN32
486 auto it = m_panels_.find("inputs_inspector");
487 if (it == m_panels_.end()) return;
488 if (!it->second.hwndChild) return;
489 std::string text = InputsManager::Get().GetDevicesStatusUpdate();
490 SetWindowTextA(it->second.hwndChild, text.c_str());
491#endif
492}
ComponentTypeID GetComponentTypeID_Static()
Definition ECS_Entity.h:56
Core game engine class.
void Register(void *owner, EventType type, Listener callback)
static EventManager & Get()
static InputsManager & Get()
string GetDevicesStatusUpdate()
void ShowPanel(const std::string &id)
static int LogPanelHeight
void CreateMainMenuWindow()
void UpdateInputsInspectorList()
static int TreeViewPanelPosX
virtual ~PanelManager()
static int LogPanelPosX
void AppendLog(const std::string &text)
static int InspectorPanelHeight
void CreateInputsInspectorWindow()
void HidePanel(const std::string &id)
void CreateObjectInspectorWindow()
std::unordered_map< std::string, Panel > m_panels_
bool IsPanelVisible(const std::string &id) const
static int InspectorPanelWidth
static int InspectorPanelPosY
static int LogPanelWidth
void CreateTreeViewWindow()
void CreateLogWindow()
static int TreeViewPanelPosY
void CreatePanel(const std::string &id, const std::string &title)
std::string name
void AttachToSDLWindow(SDL_Window *sdlWindow)
static int LogPanelPosY
static PanelManager & GetInstance()
void HandleEvent(const SDL_Event *ev)
virtual void OnEvent(const Message &msg)
static int InspectorPanelPosX
static int TreeViewPanelHeight
static int TreeViewPanelWidth
void AppendToLogWindow(const std::string &text)
EventType
@ Olympe_EventType_Keyboard_Connected
@ Olympe_EventType_Keyboard_Disconnected
@ Olympe_EventType_Joystick_Connected
@ Olympe_EventType_Mouse_Connected
@ Olympe_EventType_Joystick_Disconnected
@ Olympe_EventType_Mouse_Disconnected
#define SYSTEM_LOG