Fully undecorated window in Windows OS

This commit is contained in:
fgnm
2022-01-06 12:44:20 +01:00
parent 4e4766600b
commit ef4e15ee50
4 changed files with 140 additions and 203 deletions
@@ -8,6 +8,7 @@ import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Window;
import games.rednblack.editor.proxy.SettingsManager;
import games.rednblack.editor.splash.SplashScreenAdapter;
import games.rednblack.editor.utils.AppConfig;
import games.rednblack.editor.utils.HyperLap2DUtils;
import games.rednblack.h2d.common.view.ui.StandardWidgetsFactory;
import org.apache.commons.lang3.SystemUtils;
@@ -54,27 +55,26 @@ public class HyperLap2DApp extends ApplicationAdapter {
splashWindow = app.newWindow(new SplashScreenAdapter(), config2);
Gdx.app.postRunnable(new Runnable() {
@Override
public void run() {
hyperlap2D = new HyperLap2D(settingsManager);
Gdx.app.postRunnable(() -> {
hyperlap2D = new HyperLap2D(settingsManager);
Lwjgl3ApplicationConfiguration config = new Lwjgl3ApplicationConfiguration();
config.setTitle("HyperLap2D - Beta v" + AppConfig.getInstance().versionString);
config.setResizable(true);
Lwjgl3ApplicationConfiguration config = new Lwjgl3ApplicationConfiguration();
config.setTitle("HyperLap2D - Beta v" + AppConfig.getInstance().versionString);
config.setResizable(true);
if (!SystemUtils.IS_OS_WINDOWS)
config.setWindowedMode((int) (windowWidth), (int) (windowHeight));
config.setIdleFPS(60);
config.setForegroundFPS(settingsManager.editorConfigVO.fpsLimit);
config.useVsync(false);
config.setInitialVisible(false);
config.setMaximized(true);
config.setWindowIcon("hyperlap_icon_96.png");
config.setWindowSizeLimits(920, 800, -1, -1);
if (SystemUtils.IS_OS_WINDOWS)
config.setWindowPosition(0, (int) (windowHeight * .04));
config.setIdleFPS(60);
config.setForegroundFPS(settingsManager.editorConfigVO.fpsLimit);
config.useVsync(false);
config.setInitialVisible(false);
config.setMaximized(true);
config.setWindowIcon("hyperlap_icon_96.png");
config.setWindowSizeLimits(920, 800, -1, -1);
mainWindow = app.newWindow(hyperlap2D, config);
mainWindow.setWindowListener(hyperlap2D);
mainWindow = app.newWindow(hyperlap2D, config);
mainWindow.setWindowListener(hyperlap2D);
if (SystemUtils.IS_OS_WINDOWS) {
Gdx.app.postRunnable(() -> HyperLap2DUtils.overwriteWindowProc2(mainWindow.getWindowHandle()));
}
});
}
@@ -1,39 +1,26 @@
/*
* ******************************************************************************
* * Copyright 2015 See AUTHORS file.
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
* *****************************************************************************
*/
package games.rednblack.editor.utils;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.InputListener;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
import games.rednblack.editor.HyperLap2DFacade;
import games.rednblack.editor.view.ui.UIWindowAction;
import games.rednblack.editor.view.ui.UIWindowActionMediator;
import games.rednblack.editor.view.ui.UIWindowTitle;
import games.rednblack.editor.view.ui.UIWindowTitleMediator;
import org.apache.commons.io.filefilter.SuffixFileFilter;
import org.apache.commons.lang3.SystemUtils;
import org.lwjgl.BufferUtils;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWNativeWin32;
import org.lwjgl.system.JNI;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.system.windows.RECT;
import org.lwjgl.system.windows.User32;
import org.lwjgl.system.windows.WINDOWPLACEMENT;
import org.lwjgl.system.windows.WindowProc;
import java.io.File;
import java.io.FilenameFilter;
import java.nio.DoubleBuffer;
import java.nio.IntBuffer;
/**
* Created by sargis on 4/1/15.
@@ -64,15 +51,27 @@ public class HyperLap2DUtils {
return System.getProperty("user.home") + File.separator + "Documents";
}
public static void setWindowDragListener(Actor actor) {
actor.addListener(new InputListener() {
private final long context = GLFW.glfwGetCurrentContext();
private float startX = 0;
private float startY = 0;
/**
* Function to override Windows behavior to use undecorated style while preserving snap to edge and resize.
* Works only on Windows OS
* From: https://gist.github.com/SWinxy/d48469ac83834219a0044c2c5df55f56
* @author SWinxy
*
* @param lwjglWindow lwjgl pointer to window object
*/
public static void overwriteWindowProc2(long lwjglWindow) {
if (!SystemUtils.IS_OS_WINDOWS) return;
long hwnd = GLFWNativeWin32.glfwGetWin32Window(lwjglWindow);
long pWindowProc = User32.GetWindowLongPtr(hwnd, User32.GWL_WNDPROC);
System.out.println("oldptr: " + pWindowProc);
WindowProc proc = new WindowProc() {
private final Vector2 tmp = new Vector2();
private final Rectangle titleBounds = new Rectangle();
private final DoubleBuffer cursorX = BufferUtils.createDoubleBuffer(1);
private final DoubleBuffer cursorY = BufferUtils.createDoubleBuffer(1);
private final IntBuffer windowX = BufferUtils.createIntBuffer(1);
private final IntBuffer windowY = BufferUtils.createIntBuffer(1);
private RECT rect;
private int getX() {
return MathUtils.floor((float) cursorX.get(0));
@@ -82,154 +81,90 @@ public class HyperLap2DUtils {
}
@Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
UIWindowActionMediator uiWindowActionMediator = HyperLap2DFacade.getInstance().retrieveMediator(UIWindowActionMediator.NAME);
UIWindowAction uiWindowAction = uiWindowActionMediator.getViewComponent();
if (uiWindowAction.isMaximized())
return false;
GLFW.glfwGetCursorPos(context, cursorX, cursorY);
startX = getX();
startY = getY();
return true;
}
public long invoke(long hwnd, int uMsg, long wParam, long lParam) {
if (uMsg == User32.WM_NCHITTEST) {
try (MemoryStack stack = MemoryStack.stackPush()) {
short x = (short) (lParam & 0xFFFF);
short y = (short) ((lParam & 0xFFFF0000) >> 16);
GLFW.glfwGetCursorPos(GLFW.glfwGetCurrentContext(), cursorX, cursorY);
@Override
public void touchDragged(InputEvent event, float x, float y, int pointer) {
GLFW.glfwGetCursorPos(context, cursorX, cursorY);
float offsetX = getX() - startX;
float offsetY = getY() - startY;
GLFW.glfwGetWindowPos(context, windowX, windowY);
GLFW.glfwSetWindowPos(context, (int)(windowX.get(0) + offsetX), (int)(windowY.get(0) + offsetY));
}
});
}
if (rect == null)
rect = RECT.calloc(stack);
User32.GetWindowRect(hwnd, rect);
public static void setWindowResizeListener(Actor actor) {
actor.addListener(new InputListener() {
private static final int TOP_BORDER = 0;
private static final int BOTTOM_BORDER = 1;
private static final int LEFT_BORDER = 2;
private static final int RIGHT_BORDER = 3;
private static final int BOTTOM_LEFT_CORNER = 4;
private static final int BOTTOM_RIGHT_CORNER = 5;
private static final int TOP_LEFT_CORNER = 6;
private static final int TOP_RIGHT_CORNER = 7;
if (y < rect.top() + 16 && x < rect.left() + 16) {
return User32.HTTOPLEFT;
}
if (y > rect.bottom() - 16 && x > rect.right() - 16) {
return User32.HTBOTTOMRIGHT;
}
if (y < rect.top() + 12 && x > rect.right() - 16) {
return User32.HTTOPRIGHT;
}
if (y > rect.bottom() - 16 && x < rect.left() + 16) {
return User32.HTBOTTOMLEFT;
}
private static final int BORDER_SIZE = 10;
if (y < rect.top() + 8) {
return User32.HTTOP;
}
if (x < rect.left() + 16) {
return User32.HTLEFT;
}
if (y > rect.bottom() - 16) {
return User32.HTBOTTOM;
}
if (x > rect.right() - 16) {
return User32.HTRIGHT;
}
private final long context = GLFW.glfwGetCurrentContext();
private float startX = 0;
private float startY = 0;
private float startW = 0;
private float startH = 0;
private float startWindowX = 0;
private float startWindowY = 0;
private int anchorPoint = -1;
private final DoubleBuffer cursorX = BufferUtils.createDoubleBuffer(1);
private final DoubleBuffer cursorY = BufferUtils.createDoubleBuffer(1);
//Test if the pointer is in Title Bar
UIWindowTitleMediator uiWindowTitleMediator = HyperLap2DFacade.getInstance().retrieveMediator(UIWindowTitleMediator.NAME);
UIWindowTitle uiWindowTitle = uiWindowTitleMediator.getViewComponent();
uiWindowTitle.localToScreenCoordinates(tmp.set(0, 0));
titleBounds.set(tmp.x, tmp.y, uiWindowTitle.getWidth() + ((11 * uiWindowTitle.getWidth() / 100)), uiWindowTitle.getHeight());
private final IntBuffer windowX = BufferUtils.createIntBuffer(1);
private final IntBuffer windowY = BufferUtils.createIntBuffer(1);
private final IntBuffer windowW = BufferUtils.createIntBuffer(1);
private final IntBuffer windowH = BufferUtils.createIntBuffer(1);
private int getX() {
return MathUtils.floor((float) cursorX.get(0));
}
private int getY() {
return MathUtils.floor((float) cursorY.get(0));
}
private int getWidth() {
return MathUtils.floor((float) windowW.get(0));
}
private int getHeight() {
return MathUtils.floor((float) windowH.get(0));
}
private int getWindowX() {
return MathUtils.floor((float) windowX.get(0));
}
private int getWindowY() {
return MathUtils.floor((float) windowY.get(0));
}
@Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
UIWindowActionMediator uiWindowActionMediator = HyperLap2DFacade.getInstance().retrieveMediator(UIWindowActionMediator.NAME);
UIWindowAction uiWindowAction = uiWindowActionMediator.getViewComponent();
if (uiWindowAction.isMaximized())
return false;
GLFW.glfwGetCursorPos(context, cursorX, cursorY);
GLFW.glfwGetWindowSize(context, windowW, windowH);
GLFW.glfwGetWindowPos(context, windowX, windowY);
anchorPoint = getAnchorPoint();
if (anchorPoint == -1)
return false;
startX = getX();
startY = getY();
startW = getWidth();
startH = getHeight();
startWindowX = getWindowX();
startWindowY = getWindowY();
return true;
}
@Override
public void touchDragged(InputEvent event, float x, float y, int pointer) {
GLFW.glfwGetWindowPos(context, windowX, windowY);
GLFW.glfwGetCursorPos(context, cursorX, cursorY);
float offsetX = getX() - startX + (startWindowX - getWindowX());
float offsetY = getY() - startY;
GLFW.glfwGetWindowSize(context, windowW, windowH);
switch (anchorPoint) {
case BOTTOM_BORDER:
GLFW.glfwSetWindowSize(context, (int) startW, (int)(startH + offsetY));
break;
case TOP_BORDER:
GLFW.glfwSetWindowSize(context, (int) startW, (int)(startH - offsetY));
break;
case LEFT_BORDER:
GLFW.glfwSetWindowSize(context, (int)(startW - offsetX), (int) startH);
break;
case RIGHT_BORDER:
GLFW.glfwSetWindowSize(context, (int)(startW + offsetX), (int) startH);
break;
case BOTTOM_RIGHT_CORNER:
GLFW.glfwSetWindowSize(context, (int)(startW + offsetX), (int)(startH + offsetY));
break;
case TOP_RIGHT_CORNER:
GLFW.glfwSetWindowSize(context, (int)(startW + offsetX), (int)(startH - offsetY));
break;
case BOTTOM_LEFT_CORNER:
GLFW.glfwSetWindowSize(context, (int)(startW - offsetX), (int)(startH + offsetY));
break;
case TOP_LEFT_CORNER:
GLFW.glfwSetWindowSize(context, (int)(startW - offsetX), (int)(startH - offsetY));
break;
int glfwX = getX();
int glfwY = getY();
if (titleBounds.x <= glfwX && titleBounds.x + titleBounds.width >= glfwX
&& titleBounds.y >= glfwY && titleBounds.y - titleBounds.height <= glfwY) {
return User32.HTCAPTION;
}
return JNI.callPPPP(hwnd, uMsg, wParam, lParam, pWindowProc);
}
}
}
if (uMsg == User32.WM_NCCALCSIZE) {
if (wParam == 1) {
try (MemoryStack stack = MemoryStack.stackPush()) {
WINDOWPLACEMENT windowplacement = WINDOWPLACEMENT.calloc(stack);
User32.GetWindowPlacement(hwnd, windowplacement);
// ...but instead we're gonna just pretend it's just a RECT struct
// the NCCALCSIZE_PARAMS struct conveniently has what we need
// at the very start, so we can quietly say it's a RECT struct lol
// hacky because LWJGL doesn't include the structs to the aforementioned
// struct, nor some of the other structs contained
RECT rect = RECT.create(lParam);
if (windowplacement.showCmd() != User32.SW_MAXIMIZE) {
rect.left(rect.left() + 8);
// rect.top(rect.top() + 0);
rect.right(rect.right() - 8);
rect.bottom(rect.bottom() - 8);
} else {
rect.left(rect.left() + 8);
rect.top(rect.top() + 8);
rect.right(rect.right() - 8);
rect.bottom(rect.bottom() - 8);
}
private int getAnchorPoint() {
if (getX() < BORDER_SIZE && getY() > BORDER_SIZE && getY() < getHeight() - BORDER_SIZE)
return LEFT_BORDER;
if (getX() < BORDER_SIZE && getY() < BORDER_SIZE)
return TOP_LEFT_CORNER;
if (getX() < BORDER_SIZE && getY() > getHeight() - BORDER_SIZE)
return BOTTOM_LEFT_CORNER;
if (getX() > BORDER_SIZE && getY() > getHeight() - BORDER_SIZE && getX() < getWidth() - BORDER_SIZE)
return BOTTOM_BORDER;
if (getX() > getWidth() - BORDER_SIZE && getY() > getHeight() - BORDER_SIZE)
return BOTTOM_RIGHT_CORNER;
if (getX() > getWidth() - BORDER_SIZE && getY() > BORDER_SIZE && getY() < getHeight() - BORDER_SIZE)
return RIGHT_BORDER;
if (getX() > getWidth() - BORDER_SIZE && getY() < BORDER_SIZE)
return TOP_RIGHT_CORNER;
if (getX() > BORDER_SIZE && getY() < BORDER_SIZE && getX() < getWidth() - BORDER_SIZE)
return TOP_BORDER;
return -1;
return rect.address();
}
}
}
return JNI.callPPPP(hwnd, uMsg, wParam, lParam, pWindowProc);
}
});
};
System.out.println("procaddr: " + proc.address());
System.out.println("setptr: " + User32.SetWindowLongPtr(hwnd, User32.GWL_WNDPROC, proc.address()));
System.out.println("setwinptr: " + User32.SetWindowPos(hwnd, 0, 0, 0, 0, 0, User32.SWP_NOMOVE | User32.SWP_NOSIZE | User32.SWP_NOZORDER | User32.SWP_FRAMECHANGED));
}
}
@@ -20,11 +20,11 @@ package games.rednblack.editor.view.ui;
import com.kotcrab.vis.ui.widget.VisTable;
import games.rednblack.editor.HyperLap2DFacade;
import games.rednblack.editor.utils.HyperLap2DUtils;
import games.rednblack.editor.view.menu.HyperLap2DMenuBar;
import games.rednblack.editor.view.menu.HyperLap2DMenuBarMediator;
import games.rednblack.editor.view.ui.box.*;
import games.rednblack.editor.view.ui.widget.H2DLogo;
import org.apache.commons.lang3.SystemUtils;
public class UIMainTable extends VisTable {
private final VisTable topTable, middleTable;
@@ -47,8 +47,6 @@ public class UIMainTable extends VisTable {
initToolsPanel();
initLeftBoxesPanel();
initRightBoxesPanel();
HyperLap2DUtils.setWindowDragListener(topTable);
}
private void initMenuBar() {
@@ -56,16 +54,17 @@ public class UIMainTable extends VisTable {
HyperLap2DMenuBarMediator hyperlap2DMenuBarMediator = facade.retrieveMediator(HyperLap2DMenuBarMediator.NAME);
HyperLap2DMenuBar menuBar = hyperlap2DMenuBarMediator.getViewComponent();
topTable.add(menuBar.getTable()).height(32).growX();
topTable.add(menuBar.getTable()).height(32);
//TODO Undecorated window is cool but too much glitches
/*UIWindowTitleMediator uiWindowTitleMediator = facade.retrieveMediator(UIWindowTitleMediator.NAME);
UIWindowTitle uiWindowTitle = uiWindowTitleMediator.getViewComponent();
topTable.add(uiWindowTitle).growX().fillY();
if (SystemUtils.IS_OS_WINDOWS) {
UIWindowTitleMediator uiWindowTitleMediator = facade.retrieveMediator(UIWindowTitleMediator.NAME);
UIWindowTitle uiWindowTitle = uiWindowTitleMediator.getViewComponent();
topTable.add(uiWindowTitle).growX().fillY();
UIWindowActionMediator uiWindowActionMediator = facade.retrieveMediator(UIWindowActionMediator.NAME);
UIWindowAction uiWindowAction = uiWindowActionMediator.getViewComponent();
topTable.add(uiWindowAction).padTop(-1).fillY();*/
UIWindowActionMediator uiWindowActionMediator = facade.retrieveMediator(UIWindowActionMediator.NAME);
UIWindowAction uiWindowAction = uiWindowActionMediator.getViewComponent();
topTable.add(uiWindowAction).padTop(-1).fillY();
}
}
private void initSupportMenus() {
@@ -5,6 +5,7 @@ import com.badlogic.gdx.utils.Align;
import com.kotcrab.vis.ui.VisUI;
import com.kotcrab.vis.ui.widget.VisLabel;
import com.kotcrab.vis.ui.widget.VisTable;
import games.rednblack.editor.utils.AppConfig;
import games.rednblack.h2d.common.view.ui.StandardWidgetsFactory;
public class UIWindowTitle extends VisTable {
@@ -13,8 +14,10 @@ public class UIWindowTitle extends VisTable {
public UIWindowTitle() {
setBackground(VisUI.getSkin().getDrawable("menu-bg"));
title = StandardWidgetsFactory.createLabel("", "default", Align.center);
add(title);
title = StandardWidgetsFactory.createLabel("HyperLap2D - Beta v" + AppConfig.getInstance().versionString, "default", Align.center);
title.setEllipsis(true);
title.setWrap(true);
add(title).growX();
setTouchable(Touchable.enabled);
}