1
0
mirror of synced 2025-02-17 18:59:21 +01:00

impr: Implement borderless window mode for macOS

This commit is contained in:
WerWolv 2024-02-03 22:39:31 +01:00
parent 3555fc01c5
commit fe3facfc95
5 changed files with 97 additions and 61 deletions

View File

@ -9,6 +9,7 @@
void errorMessageMacos(const char *message);
void openWebpageMacos(const char *url);
bool isMacosSystemDarkModeEnabled();
bool isMacosFullScreenModeEnabled(GLFWwindow *window);
float getBackingScaleFactor();
void setupMacosWindowStyle(GLFWwindow *window);

View File

@ -46,13 +46,19 @@
void setupMacosWindowStyle(GLFWwindow *window) {
NSWindow* cocoaWindow = glfwGetCocoaWindow(window);
cocoaWindow.titleVisibility = NSWindowTitleHidden;
cocoaWindow.titlebarAppearsTransparent = YES;
cocoaWindow.styleMask |= NSWindowStyleMaskFullSizeContentView;
NSVisualEffectView *visualEffectView = [[NSVisualEffectView alloc] init];
[visualEffectView setMaterial:NSVisualEffectMaterialAppearanceBased];
[visualEffectView setBlendingMode:NSVisualEffectBlendingModeBehindWindow];
[cocoaWindow setOpaque:NO];
[cocoaWindow setHasShadow:YES];
[cocoaWindow setBackgroundColor:[NSColor colorWithWhite: 0 alpha: 0.001f]];
}
[cocoaWindow.contentView addSubview:visualEffectView positioned:NSWindowBelow relativeTo:nil];
bool isMacosFullScreenModeEnabled(GLFWwindow *window) {
NSWindow* cocoaWindow = glfwGetCocoaWindow(window);
return (cocoaWindow.styleMask & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen;
}
@interface HexDocument : NSDocument

View File

@ -41,6 +41,8 @@ namespace hex {
}
void Window::setupNativeWindow() {
ImHexApi::System::impl::setBorderlessWindowMode(true);
bool themeFollowSystem = ImHexApi::System::usesSystemThemeDetection();
EventOSThemeChanged::subscribe(this, [themeFollowSystem] {
if (!themeFollowSystem) return;

View File

@ -517,34 +517,38 @@ namespace hex::plugin::builtin {
static void createViewMenu() {
ContentRegistry::Interface::registerMainMenuItem("hex.builtin.menu.view", 3000);
ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.view", "hex.builtin.menu.view.always_on_top" }, ICON_VS_PINNED, 1000, Keys::F10, [] {
static bool state = false;
#if !defined(OS_WEB)
ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.view", "hex.builtin.menu.view.always_on_top" }, ICON_VS_PINNED, 1000, Keys::F10, [] {
static bool state = false;
state = !state;
glfwSetWindowAttrib(ImHexApi::System::getMainWindowHandle(), GLFW_FLOATING, state);
}, []{ return true; }, []{ return glfwGetWindowAttrib(ImHexApi::System::getMainWindowHandle(), GLFW_FLOATING); });
state = !state;
glfwSetWindowAttrib(ImHexApi::System::getMainWindowHandle(), GLFW_FLOATING, state);
}, []{ return true; }, []{ return glfwGetWindowAttrib(ImHexApi::System::getMainWindowHandle(), GLFW_FLOATING); });
#endif
ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.view", "hex.builtin.menu.view.fullscreen" }, ICON_VS_SCREEN_FULL, 2000, Keys::F11, [] {
static bool state = false;
static ImVec2 position, size;
#if !defined(OS_MACOS) && !defined(OS_WEB)
ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.view", "hex.builtin.menu.view.fullscreen" }, ICON_VS_SCREEN_FULL, 2000, Keys::F11, [] {
static bool state = false;
static ImVec2 position, size;
state = !state;
state = !state;
const auto window = ImHexApi::System::getMainWindowHandle();
if (state) {
position = ImHexApi::System::getMainWindowPosition();
size = ImHexApi::System::getMainWindowSize();
const auto window = ImHexApi::System::getMainWindowHandle();
if (state) {
position = ImHexApi::System::getMainWindowPosition();
size = ImHexApi::System::getMainWindowSize();
const auto monitor = glfwGetPrimaryMonitor();
const auto videoMode = glfwGetVideoMode(monitor);
const auto monitor = glfwGetPrimaryMonitor();
const auto videoMode = glfwGetVideoMode(monitor);
glfwSetWindowMonitor(window, monitor, 0, 0, videoMode->width, videoMode->height, videoMode->refreshRate);
} else {
glfwSetWindowMonitor(window, nullptr, position.x, position.y, size.x, size.y, 0);
}
glfwSetWindowMonitor(window, monitor, 0, 0, videoMode->width, videoMode->height, videoMode->refreshRate);
} else {
glfwSetWindowMonitor(window, nullptr, position.x, position.y, size.x, size.y, 0);
}
}, []{ return true; }, []{ return glfwGetWindowMonitor(ImHexApi::System::getMainWindowHandle()) != nullptr; });
}, []{ return true; }, []{ return glfwGetWindowMonitor(ImHexApi::System::getMainWindowHandle()) != nullptr; });
#endif
ContentRegistry::Interface::addMenuItemSeparator({ "hex.builtin.menu.view" }, 3000);

View File

@ -132,21 +132,55 @@ namespace hex::plugin::builtin {
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImGui::GetColorU32(ImGuiCol_ScrollbarGrabActive));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::GetColorU32(ImGuiCol_ScrollbarGrabHovered));
auto framePadding = ImGui::GetStyle().FramePadding * 2;
const auto windowSize = ImHexApi::System::getMainWindowSize();
const auto searchBoxSize = ImVec2(windowSize.x / 2.5, titleBarHeight - 3_scaled);
const auto searchBoxPos = ImVec2((windowSize / 2 - searchBoxSize / 2).x, 3_scaled);
const auto searchBoxSize = ImVec2(windowSize.x / 2.5, titleBarHeight);
const auto searchBoxPos = ImVec2((windowSize / 2 - searchBoxSize / 2).x, framePadding.y);
s_searchBarPosition = searchBoxPos.x;
// Custom titlebar buttons implementation for borderless window mode
auto window = ImHexApi::System::getMainWindowHandle();
bool titleBarButtonsVisible = false;
if (ImHexApi::System::isBorderlessWindowModeEnabled() && glfwGetWindowMonitor(window) == nullptr) {
#if defined(OS_WINDOWS)
titleBarButtonsVisible = true;
// Draw minimize, restore and maximize buttons
ImGui::SetCursorPosX(ImGui::GetWindowWidth() - buttonSize.x * 3);
if (ImGuiExt::TitleBarButton(ICON_VS_CHROME_MINIMIZE, buttonSize))
glfwIconifyWindow(window);
if (glfwGetWindowAttrib(window, GLFW_MAXIMIZED)) {
if (ImGuiExt::TitleBarButton(ICON_VS_CHROME_RESTORE, buttonSize))
glfwRestoreWindow(window);
} else {
if (ImGuiExt::TitleBarButton(ICON_VS_CHROME_MAXIMIZE, buttonSize))
glfwMaximizeWindow(window);
}
ImGui::PushStyleColor(ImGuiCol_ButtonActive, 0xFF7A70F1);
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, 0xFF2311E8);
// Draw close button
if (ImGuiExt::TitleBarButton(ICON_VS_CHROME_CLOSE, buttonSize)) {
ImHexApi::System::closeImHex();
}
ImGui::PopStyleColor(2);
#endif
}
auto &titleBarButtons = ContentRegistry::Interface::impl::getTitleBarButtons();
// Draw custom title bar buttons
if (!titleBarButtons.empty()) {
ImGui::SetCursorPosX(ImGui::GetWindowWidth() - buttonSize.x * float(4 + titleBarButtons.size()));
ImGui::SetCursorPosX(ImGui::GetWindowWidth() - buttonSize.x * float((titleBarButtonsVisible ? 4 : 0) + titleBarButtons.size()));
if (ImGui::GetCursorPosX() > (searchBoxPos.x + searchBoxSize.x)) {
for (const auto &[icon, tooltip, callback]: titleBarButtons) {
for (const auto &[icon, tooltip, callback] : titleBarButtons) {
ImGui::SetCursorPosY(framePadding.y);
if (ImGuiExt::TitleBarButton(icon.c_str(), buttonSize)) {
callback();
}
@ -155,31 +189,6 @@ namespace hex::plugin::builtin {
}
}
auto window = ImHexApi::System::getMainWindowHandle();
if (ImHexApi::System::isBorderlessWindowModeEnabled() && glfwGetWindowMonitor(window) == nullptr) {
// Draw minimize, restore and maximize buttons
ImGui::SetCursorPosX(ImGui::GetWindowWidth() - buttonSize.x * 3);
if (ImGuiExt::TitleBarButton(ICON_VS_CHROME_MINIMIZE, buttonSize))
glfwIconifyWindow(window);
if (glfwGetWindowAttrib(window, GLFW_MAXIMIZED)) {
if (ImGuiExt::TitleBarButton(ICON_VS_CHROME_RESTORE, buttonSize))
glfwRestoreWindow(window);
} else {
if (ImGuiExt::TitleBarButton(ICON_VS_CHROME_MAXIMIZE, buttonSize))
glfwMaximizeWindow(window);
}
ImGui::PushStyleColor(ImGuiCol_ButtonActive, 0xFF7A70F1);
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, 0xFF2311E8);
// Draw close button
if (ImGuiExt::TitleBarButton(ICON_VS_CHROME_CLOSE, buttonSize)) {
ImHexApi::System::closeImHex();
}
ImGui::PopStyleColor(2);
}
ImGui::PopStyleColor(3);
ImGui::PopStyleVar();
@ -213,22 +222,33 @@ namespace hex::plugin::builtin {
}
}
void drawMainMenu(float menuBarHeight) {
void drawMainMenu([[maybe_unused]] float menuBarHeight) {
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0F);
ImGui::SetNextWindowScroll(ImVec2(0, 0));
#if defined(OS_MACOS)
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(ImGui::GetStyle().FramePadding.x, 8_scaled));
#endif
if (ImGui::BeginMainMenuBar()) {
auto window = ImHexApi::System::getMainWindowHandle();
ImGui::PopStyleVar();
if (ImHexApi::System::isBorderlessWindowModeEnabled()) {
ImGui::SetCursorPosX(5);
ImGui::Image(s_logoTexture, ImVec2(menuBarHeight, menuBarHeight));
ImGui::SetCursorPosX(5);
ImGui::InvisibleButton("##logo", ImVec2(menuBarHeight, menuBarHeight));
ImGui::OpenPopupOnItemClick("WindowingMenu", ImGuiPopupFlags_MouseButtonLeft);
#if defined(OS_WINDOWS)
ImGui::SetCursorPosX(5_scaled);
ImGui::Image(s_logoTexture, ImVec2(menuBarHeight, menuBarHeight));
ImGui::SetCursorPosX(5_scaled);
ImGui::InvisibleButton("##logo", ImVec2(menuBarHeight, menuBarHeight));
ImGui::OpenPopupOnItemClick("WindowingMenu", ImGuiPopupFlags_MouseButtonLeft);
#elif defined(OS_MACOS)
if (!isMacosFullScreenModeEnabled(window))
ImGui::SetCursorPosX(68_scaled);
#endif
}
if (ImGui::BeginPopup("WindowingMenu")) {
auto window = ImHexApi::System::getMainWindowHandle();
bool maximized = glfwGetWindowAttrib(window, GLFW_MAXIMIZED);
ImGui::BeginDisabled(!maximized);
@ -289,7 +309,10 @@ namespace hex::plugin::builtin {
drawTitleBar();
ImGui::EndMainMenuBar();
} else {
ImGui::PopStyleVar();
}
ImGui::PopStyleVar();
}