Skip to content

Commit

Permalink
Remove default title bar, replace with custom on Win32
Browse files Browse the repository at this point in the history
  • Loading branch information
Mormert committed Apr 3, 2024
1 parent 22ccfee commit 6fe1528
Show file tree
Hide file tree
Showing 8 changed files with 275 additions and 8 deletions.
Binary file added engine/EditorResources/icons/cross.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added engine/EditorResources/icons/maximize.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added engine/EditorResources/icons/minimize.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
89 changes: 83 additions & 6 deletions engine/editor/jleEditorWindowsPanel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,20 @@
#include "jleEditorWindowsPanel.h"

#include "jleGameEditorWindow.h"
#include "jleEngineSettings.h"
#include "jleWindow.h"

#include <ImGui/imgui.h>
#include <plog/Log.h>


jleEditorWindowsPanel::jleEditorWindowsPanel(const std::string &window_name)
: jleEditorWindowInterface{window_name}, _gameController{"Game Controller"}
{
_crossIcon = gEngine->resources().loadResourceFromFile<jleTexture>(jlePath{"ED:/icons/cross.png"});
_maximizeIcon = gEngine->resources().loadResourceFromFile<jleTexture>(jlePath{"ED:/icons/maximize.png"});
_minimizeIcon = gEngine->resources().loadResourceFromFile<jleTexture>(jlePath{"ED:/icons/minimize.png"});
_jleIcon = gEngine->resources().loadResourceFromFile<jleTexture>(gEngine->settings().windowSettings.iconPath);
}

void
Expand Down Expand Up @@ -104,6 +111,15 @@ void
jleEditorWindowsPanel::menuButtonsupdate(jleGameEngine &ge)
{
if (ImGui::BeginMenuBar()) {

const float menuBarWidth = ImGui::GetWindowWidth();
const float menuBarHeight = ImGui::GetFrameHeightWithSpacing();

ImGui::SetCursorPosX(1.f);

const float iconDimWidthHeight = std::max(_jleIcon->width() * 0.5f, menuBarHeight);
ImGui::Image((void *)(intptr_t)_jleIcon->id(), ImVec2{iconDimWidthHeight, iconDimWidthHeight});

if (ImGui::BeginMenu("Manage Windows")) {
for (auto &&window : windows) {
if (window->opened()) {
Expand All @@ -125,11 +141,8 @@ jleEditorWindowsPanel::menuButtonsupdate(jleGameEngine &ge)
}

auto windowWidth = ImGui::GetWindowSize().x;
ImGui::SetCursorPosX((windowWidth)*0.5f);

ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetColumnWidth() -
ImGui::CalcTextSize("Avg FPS: XXXX Run Time: HH:MM:SS.MMM").x - ImGui::GetScrollX() -
2 * ImGui::GetStyle().ItemSpacing.x);
const float textOffset = ImGui::CalcTextSize("Avg FPS: XXXX | Run Time: HH:MM:SS.MMM").x * 0.5f;
ImGui::SetCursorPosX((windowWidth) * 0.5f - textOffset);

// clang-format off
const auto formatTime = [](int value)
Expand All @@ -150,10 +163,74 @@ jleEditorWindowsPanel::menuButtonsupdate(jleGameEngine &ge)
// clang-format on

const auto rolling120FramesAvgFps = (int)ImGui::GetIO().Framerate;
ImGui::Text("Avg FPS: %4d Run Time: %s",
ImGui::Text("Avg FPS: %4d | Run Time: %s",
rolling120FramesAvgFps,
formatTime(static_cast<int>(ge.currentFrameTime() * 1000.f)).c_str());

{
ImGuiStyle &style = ImGui::GetStyle();

const float originalFrameRounding = style.FrameRounding;
const ImVec2 originalItemSpacing = style.ItemSpacing;

// Set FrameRounding to 0 to create sharp corners
style.FrameRounding = 0.0f;

// Set ItemSpacing to 0 to remove the gap between buttons
style.ItemSpacing = ImVec2(0, style.ItemSpacing.y);

const ImVec4 menuBarColor = style.Colors[ImGuiCol_MenuBarBg];
const ImVec4 buttonHoveredColor = ImVec4(menuBarColor.x * 1.1f,
menuBarColor.y * 1.1f,
menuBarColor.z * 1.1f,
menuBarColor.w);

const float ratio = 0.5f * menuBarHeight / _crossIcon->height();
const ImVec2 buttonSize{(float)_crossIcon->width() * ratio, (float)_crossIcon->height() * ratio};
const float buttonWidth = buttonSize.x + style.FramePadding.x * 2;
const float totalButtonWidth = 3 * buttonWidth;
const float offset = menuBarWidth - totalButtonWidth - style.ItemSpacing.x * 2;

ImGui::SetCursorPosX(offset);

{
ImGui::PushStyleColor(ImGuiCol_Button, menuBarColor);
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, buttonHoveredColor);
ImGui::PushStyleColor(ImGuiCol_ButtonActive, buttonHoveredColor);
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, 0));

if (ImGui::ImageButton((void *)(intptr_t)_minimizeIcon->id(), buttonSize)) {
glfwIconifyWindow(gEngine->window().glfwWindow());
}

if (ImGui::ImageButton((void *)(intptr_t)_maximizeIcon->id(), buttonSize)) {
const auto window = gEngine->window().glfwWindow();
if (glfwGetWindowAttrib(window, GLFW_MAXIMIZED)) {
glfwRestoreWindow(window);
} else {
glfwMaximizeWindow(window);
}
}

ImVec4 grayRedHoveredColor = ImVec4(0.7f, 0.2f, 0.2f, 1.0f);
{
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, grayRedHoveredColor);

if (ImGui::ImageButton((void *)(intptr_t)_crossIcon->id(), buttonSize)) {
glfwSetWindowShouldClose(gEngine->window().glfwWindow(), true);
}
ImGui::PopStyleColor();
}

// Pop the style colors
ImGui::PopStyleColor(4);
}

// Restore the original FrameRounding and ItemSpacing values
style.FrameRounding = originalFrameRounding;
style.ItemSpacing = originalItemSpacing;
}

ImGui::EndMenuBar();
}
}
5 changes: 5 additions & 0 deletions engine/editor/jleEditorWindowsPanel.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ class jleEditorWindowsPanel : public jleEditorWindowInterface
inline void dockspaceupdate(jleGameEngine &ge);

private:
std::shared_ptr<jleTexture> _crossIcon; // X
std::shared_ptr<jleTexture> _maximizeIcon; // [ ]
std::shared_ptr<jleTexture> _minimizeIcon; // -
std::shared_ptr<jleTexture> _jleIcon;

std::vector<std::shared_ptr<jleEditorWindowInterface>> windows;

jleEditorGameControllerWidget _gameController;
Expand Down
184 changes: 184 additions & 0 deletions engine/jleWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@
#include "stb_image.h"
#include <plog/Log.h>

#ifdef WIN32
#define GLFW_EXPOSE_NATIVE_WIN32
#include <GLFW/glfw3native.h>
#include <windef.h>
#include <windowsx.h>
#endif

// JLE_EXTERN_TEMPLATE_CEREAL_CPP(WindowSettings)

void
Expand Down Expand Up @@ -137,6 +144,179 @@ jleWindow::width() const
return windowSettings.width;
}

#ifdef WIN32

WNDPROC g_glfwWndProc = nullptr;

LRESULT CALLBACK
WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
constexpr int customTitleBarHeight = 30;
static bool resizing = false;
static POINT lastCursorPosition;

RECT windowRect{};

switch (uMsg) {
case WM_NCCALCSIZE:
// Remove standard window frame
return 0;
case WM_NCHITTEST:
POINT cursor;
GetCursorPos(&cursor);
ScreenToClient(hWnd, &cursor);

if (ImGui::IsAnyItemHovered()) {
return CallWindowProc(g_glfwWndProc, hWnd, uMsg, wParam, lParam);
}

GetClientRect(hWnd, &windowRect);

// Adjust the bottom border for resizing
windowRect.bottom += 5;

if (cursor.y >= windowRect.bottom - 5) {
if (cursor.x < 5)
return HTBOTTOMLEFT;
else if (cursor.x > windowRect.right - 5)
return HTBOTTOMRIGHT;
else
return HTBOTTOM;
} else if (cursor.x < 5) {
if (cursor.y < 5)
return HTTOPLEFT;
else
return HTLEFT;
} else if (cursor.x > windowRect.right - 5) {
if (cursor.y < 5)
return HTTOPRIGHT;
else
return HTRIGHT;
} else if (cursor.y < customTitleBarHeight) {
if (cursor.y < 5) {
if (cursor.x < windowRect.right / 2)
return HTTOPLEFT;
else
return HTTOPRIGHT;
} else
return HTCAPTION;
}
return CallWindowProc(g_glfwWndProc, hWnd, uMsg, wParam, lParam); // Return default handling for other areas
case WM_LBUTTONDOWN:
if (wParam == HTTOPLEFT || wParam == HTTOPRIGHT || wParam == HTBOTTOMLEFT || wParam == HTBOTTOMRIGHT ||
wParam == HTBOTTOM || wParam == HTLEFT || wParam == HTRIGHT) {
resizing = true;
lastCursorPosition = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
SetCapture(hWnd);
return 0;
}
return CallWindowProc(g_glfwWndProc, hWnd, uMsg, wParam, lParam);
case WM_MOUSEMOVE:
if (resizing) {
POINT currentCursorPosition = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
int deltaX = currentCursorPosition.x - lastCursorPosition.x;
int deltaY = currentCursorPosition.y - lastCursorPosition.y;

if (wParam == HTTOPLEFT || wParam == HTLEFT || wParam == HTBOTTOMLEFT) {
windowRect.left += deltaX;
windowRect.top += deltaY;
}
if (wParam == HTTOPLEFT || wParam == HTTOP || wParam == HTTOPRIGHT) {
windowRect.top += deltaY;
}
if (wParam == HTTOPLEFT || wParam == HTTOPRIGHT || wParam == HTBOTTOMRIGHT) {
windowRect.right += deltaX;
}
if (wParam == HTTOPRIGHT || wParam == HTRIGHT || wParam == HTBOTTOMRIGHT) {
windowRect.bottom += deltaY;
}

SetWindowPos(hWnd,
NULL,
windowRect.left,
windowRect.top,
windowRect.right - windowRect.left,
windowRect.bottom - windowRect.top,
SWP_NOZORDER | SWP_NOREDRAW);
lastCursorPosition = currentCursorPosition;
return 0;
}
return CallWindowProc(g_glfwWndProc, hWnd, uMsg, wParam, lParam);
case WM_LBUTTONUP:
if (resizing) {
resizing = false;
ReleaseCapture();
return 0;
}
return CallWindowProc(g_glfwWndProc, hWnd, uMsg, wParam, lParam);
case WM_GETMINMAXINFO: {
// Set maximum and minimum window size and position based on screen dimensions
auto *pmmi = (MINMAXINFO *)lParam;

pmmi->ptMaxPosition.x = 0;
pmmi->ptMaxPosition.y = 0;

// Get the monitor where the window is located
HMONITOR hMonitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST);

if (hMonitor) {
MONITORINFO monitorInfo;
monitorInfo.cbSize = sizeof(MONITORINFO);
if (GetMonitorInfo(hMonitor, &monitorInfo)) {
RECT workArea = monitorInfo.rcWork;
int workAreaWidth = workArea.right - workArea.left;
int workAreaHeight = workArea.bottom - workArea.top;

pmmi->ptMaxSize.x = workAreaWidth;
pmmi->ptMaxSize.y = workAreaHeight;
}
}

return 0;
}
default:
return CallWindowProc(g_glfwWndProc, hWnd, uMsg, wParam, lParam);
}
}

GLFWwindow *
initWindowForEditor_Win32()
{
const GLFWvidmode *mode = glfwGetVideoMode(glfwGetPrimaryMonitor());

RECT workArea;
int workAreaWidth = mode->width;
int workAreaHeight = mode->height;

if (SystemParametersInfo(SPI_GETWORKAREA, 0, &workArea, 0)) {
workAreaWidth = ((float)workArea.right - (float)workArea.left);
workAreaHeight = ((float)workArea.bottom - (float)workArea.top);
}

auto glfwWindow = jleWindow::initGlfwWindow(workAreaWidth, workAreaHeight, "");

// Get the native window handle
HWND hwnd = glfwGetWin32Window(glfwWindow);

// Saves a reference to the WndProc function set from GLFW (found in glfw's win32_window.c)
g_glfwWndProc = (WNDPROC)GetWindowLongPtr(hwnd, GWLP_WNDPROC);

// Set the window procedure callback
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)WindowProc);

// WE_EX_TOOLWINDOW is used to combat an issue where the window borders would be "outside the screen"
// WS_EX_APPWINDOW is to combat an issue which is caused by WE_EX_TOOLWINDOW, which is that it disables the
// program from showing up in the Windows taskbar
SetWindowLong(hwnd, GWL_EXSTYLE, WS_EX_TOOLWINDOW | WS_EX_APPWINDOW);

// Maximises the window on startup
ShowWindow(hwnd, SW_MAXIMIZE);

return glfwWindow;
}

#endif // WIN32

void
jleWindow::initWindow()
{
Expand All @@ -156,9 +336,13 @@ jleWindow::initWindow()

JLE_EXEC_IF(JLE_BUILD_EDITOR)
{
#ifdef WIN32
_glfwWindow = initWindowForEditor_Win32();
#else
glfwWindowHint(GLFW_MAXIMIZED, GLFW_TRUE);
const GLFWvidmode *mode = glfwGetVideoMode(glfwGetPrimaryMonitor());
_glfwWindow = initGlfwWindow(mode->width, mode->height, windowSettings.WindowTitle.c_str());
#endif
}
else
{
Expand Down
3 changes: 2 additions & 1 deletion engine/jleWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ class jleWindow {
GLFWwindow *
glfwWindow();

static GLFWwindow *initGlfwWindow(int width, int height, const char *title);

protected:
GLFWwindow *_glfwWindow;
static inline jleWindow *_activeWindow;
Expand All @@ -93,7 +95,6 @@ class jleWindow {
bool cursorVisible{false};

private:
static GLFWwindow *initGlfwWindow(int width, int height, const char *title);
inline static bool sPressedKeys[512];
inline static bool sReleasedKeys[512];

Expand Down
2 changes: 1 addition & 1 deletion engine/jleWindowSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class WindowSettings {
unsigned int width = 1920, height = 1080;
unsigned int widthMin = 500, heightMin = 500;

jlePath iconPath{"ED:/jle_icon48.png.png"};
jlePath iconPath{"ED:/jle_icon48.png"};

bool isRezisable = true;
bool shouldDisplayCursor = true;
Expand Down

0 comments on commit 6fe1528

Please sign in to comment.