mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-11 14:39:30 +08:00
[windows] Fixes for window sizing
This commit is contained in:
parent
4f2788a294
commit
1748e8479f
@ -2,6 +2,7 @@
|
|||||||
// License included in README.md
|
// License included in README.md
|
||||||
|
|
||||||
#include "ffenestri_windows.h"
|
#include "ffenestri_windows.h"
|
||||||
|
#include "shellscalingapi.h"
|
||||||
#include "wv2ComHandler_windows.h"
|
#include "wv2ComHandler_windows.h"
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
@ -42,6 +43,30 @@ char* LPWSTRToCstr(LPWSTR input) {
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Credit: https://building.enlyze.com/posts/writing-win32-apps-like-its-2020-part-3/
|
||||||
|
typedef int (__cdecl *PGetDpiForMonitor)(HMONITOR, MONITOR_DPI_TYPE,UINT*,UINT*);
|
||||||
|
void getDPIForWindow(struct Application *app)
|
||||||
|
{
|
||||||
|
HMODULE hShcore = LoadLibraryW(L"shcore");
|
||||||
|
if (hShcore)
|
||||||
|
{
|
||||||
|
PGetDpiForMonitor pGetDpiForMonitor = reinterpret_cast<PGetDpiForMonitor>(GetProcAddress(hShcore, "GetDpiForMonitor"));
|
||||||
|
if (pGetDpiForMonitor)
|
||||||
|
{
|
||||||
|
HMONITOR hMonitor = MonitorFromWindow(app->window, MONITOR_DEFAULTTOPRIMARY);
|
||||||
|
pGetDpiForMonitor(hMonitor, (MONITOR_DPI_TYPE)0, &app->dpix, &app->dpiy);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// We couldn't get the window's DPI above, so get the DPI of the primary monitor
|
||||||
|
// using an API that is available in all Windows versions.
|
||||||
|
HDC hScreenDC = GetDC(0);
|
||||||
|
app->dpix = GetDeviceCaps(hScreenDC, LOGPIXELSX);
|
||||||
|
app->dpiy = GetDeviceCaps(hScreenDC, LOGPIXELSY);
|
||||||
|
ReleaseDC(0, hScreenDC);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct Application *NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen, int startHidden, int logLevel, int hideWindowOnClose) {
|
struct Application *NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen, int startHidden, int logLevel, int hideWindowOnClose) {
|
||||||
|
|
||||||
// Create application
|
// Create application
|
||||||
@ -88,6 +113,9 @@ struct Application *NewApplication(const char *title, int width, int height, int
|
|||||||
// Used to remember the window location when going fullscreen
|
// Used to remember the window location when going fullscreen
|
||||||
result->previousPlacement = { sizeof(result->previousPlacement) };
|
result->previousPlacement = { sizeof(result->previousPlacement) };
|
||||||
|
|
||||||
|
// DPI
|
||||||
|
result->dpix = result->dpiy = 0;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,30 +207,28 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get pixel density
|
// update DPI
|
||||||
HDC hDC = GetDC(NULL);
|
getDPIForWindow(app);
|
||||||
double DPIScaleX = GetDeviceCaps(hDC, 88)/96.0;
|
double DPIScaleX = app->dpix/96.0;
|
||||||
double DPIScaleY = GetDeviceCaps(hDC, 90)/96.0;
|
double DPIScaleY = app->dpiy/96.0;
|
||||||
ReleaseDC(NULL, hDC);
|
|
||||||
|
|
||||||
RECT rcClient, rcWind;
|
RECT rcWind;
|
||||||
POINT ptDiff;
|
POINT ptDiff;
|
||||||
GetClientRect(hwnd, &rcClient);
|
|
||||||
GetWindowRect(hwnd, &rcWind);
|
GetWindowRect(hwnd, &rcWind);
|
||||||
|
|
||||||
int widthExtra = (rcWind.right - rcWind.left) - rcClient.right;
|
int widthExtra = (rcWind.right - rcWind.left);
|
||||||
int heightExtra = (rcWind.bottom - rcWind.top) - rcClient.bottom;
|
int heightExtra = (rcWind.bottom - rcWind.top);
|
||||||
|
|
||||||
LPMINMAXINFO mmi = (LPMINMAXINFO) lParam;
|
LPMINMAXINFO mmi = (LPMINMAXINFO) lParam;
|
||||||
if (app->minWidth > 0 && app->minHeight > 0) {
|
if (app->minWidth > 0 && app->minHeight > 0) {
|
||||||
mmi->ptMinTrackSize.x = app->minWidth * DPIScaleX + widthExtra;
|
mmi->ptMinTrackSize.x = app->minWidth * DPIScaleX;
|
||||||
mmi->ptMinTrackSize.y = app->minHeight * DPIScaleY + heightExtra;
|
mmi->ptMinTrackSize.y = app->minHeight * DPIScaleY;
|
||||||
}
|
}
|
||||||
if (app->maxWidth > 0 && app->maxHeight > 0) {
|
if (app->maxWidth > 0 && app->maxHeight > 0) {
|
||||||
mmi->ptMaxSize.x = app->maxWidth * DPIScaleX + widthExtra;
|
mmi->ptMaxSize.x = app->maxWidth * DPIScaleX;
|
||||||
mmi->ptMaxSize.y = app->maxHeight * DPIScaleY + heightExtra;
|
mmi->ptMaxSize.y = app->maxHeight * DPIScaleY;
|
||||||
mmi->ptMaxTrackSize.x = app->maxWidth * DPIScaleX + widthExtra;
|
mmi->ptMaxTrackSize.x = app->maxWidth * DPIScaleX;
|
||||||
mmi->ptMaxTrackSize.y = app->maxHeight * DPIScaleY + heightExtra;
|
mmi->ptMaxTrackSize.y = app->maxHeight * DPIScaleY;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -392,6 +418,7 @@ bool initWebView2(struct Application *app, int debugEnabled, messageCallback cb)
|
|||||||
}
|
}
|
||||||
app->webviewController = controller;
|
app->webviewController = controller;
|
||||||
app->webview = webview;
|
app->webview = webview;
|
||||||
|
|
||||||
// Resize WebView to fit the bounds of the parent window
|
// Resize WebView to fit the bounds of the parent window
|
||||||
RECT bounds;
|
RECT bounds;
|
||||||
GetClientRect(app->window, &bounds);
|
GetClientRect(app->window, &bounds);
|
||||||
@ -403,7 +430,64 @@ bool initWebView2(struct Application *app, int debugEnabled, messageCallback cb)
|
|||||||
LPCWSTR html = (LPCWSTR) cstrToLPWSTR((char*)assets[0]);
|
LPCWSTR html = (LPCWSTR) cstrToLPWSTR((char*)assets[0]);
|
||||||
app->webview->Navigate(html);
|
app->webview->Navigate(html);
|
||||||
|
|
||||||
|
if( app->webviewIsTranparent ) {
|
||||||
|
wchar_t szBuff[64];
|
||||||
|
ICoreWebView2Controller2 *wc2;
|
||||||
|
wc2 = nullptr;
|
||||||
|
app->webviewController->QueryInterface(IID_ICoreWebView2Controller2, (void**)&wc2);
|
||||||
|
|
||||||
|
COREWEBVIEW2_COLOR wvColor;
|
||||||
|
wvColor.R = app->backgroundColour.R;
|
||||||
|
wvColor.G = app->backgroundColour.G;
|
||||||
|
wvColor.B = app->backgroundColour.B;
|
||||||
|
wvColor.A = app->backgroundColour.A == 0 ? 0 : 255;
|
||||||
|
if( app->windowBackgroundIsTranslucent ) {
|
||||||
|
wvColor.A = 0;
|
||||||
|
}
|
||||||
|
HRESULT result = wc2->put_DefaultBackgroundColor(wvColor);
|
||||||
|
if (!SUCCEEDED(result))
|
||||||
|
{
|
||||||
|
switch (result)
|
||||||
|
{
|
||||||
|
case HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND):
|
||||||
|
{
|
||||||
|
MessageBox(
|
||||||
|
app->window,
|
||||||
|
L"Couldn't find Edge installation. "
|
||||||
|
"Do you have a version installed that's compatible with this "
|
||||||
|
"WebView2 SDK version?",
|
||||||
|
nullptr, MB_OK);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case HRESULT_FROM_WIN32(ERROR_FILE_EXISTS):
|
||||||
|
{
|
||||||
|
MessageBox(
|
||||||
|
app->window, L"User data folder cannot be created because a file with the same name already exists.", nullptr, MB_OK);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case E_ACCESSDENIED:
|
||||||
|
{
|
||||||
|
MessageBox(
|
||||||
|
app->window, L"Unable to create user data folder, Access Denied.", nullptr, MB_OK);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case E_FAIL:
|
||||||
|
{
|
||||||
|
MessageBox(
|
||||||
|
app->window, L"Edge runtime unable to start", nullptr, MB_OK);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
MessageBox(app->window, L"Failed to create WebView2 environment", nullptr, MB_OK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
messageFromWindowCallback("Ej{\"name\":\"wails:launched\",\"data\":[]}");
|
messageFromWindowCallback("Ej{\"name\":\"wails:launched\",\"data\":[]}");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -500,61 +584,6 @@ void Run(struct Application* app, int argc, char **argv) {
|
|||||||
// Add webview2
|
// Add webview2
|
||||||
initWebView2(app, debug, initialCallback);
|
initWebView2(app, debug, initialCallback);
|
||||||
|
|
||||||
if( app->webviewIsTranparent ) {
|
|
||||||
wchar_t szBuff[64];
|
|
||||||
ICoreWebView2Controller2 *wc2;
|
|
||||||
wc2 = nullptr;
|
|
||||||
app->webviewController->QueryInterface(IID_ICoreWebView2Controller2, (void**)&wc2);
|
|
||||||
|
|
||||||
COREWEBVIEW2_COLOR wvColor;
|
|
||||||
wvColor.R = app->backgroundColour.R;
|
|
||||||
wvColor.G = app->backgroundColour.G;
|
|
||||||
wvColor.B = app->backgroundColour.B;
|
|
||||||
wvColor.A = app->backgroundColour.A == 0 ? 0 : 255;
|
|
||||||
if( app->windowBackgroundIsTranslucent ) {
|
|
||||||
wvColor.A = 0;
|
|
||||||
}
|
|
||||||
HRESULT result = wc2->put_DefaultBackgroundColor(wvColor);
|
|
||||||
if (!SUCCEEDED(result))
|
|
||||||
{
|
|
||||||
switch (result)
|
|
||||||
{
|
|
||||||
case HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND):
|
|
||||||
{
|
|
||||||
MessageBox(
|
|
||||||
app->window,
|
|
||||||
L"Couldn't find Edge installation. "
|
|
||||||
"Do you have a version installed that's compatible with this "
|
|
||||||
"WebView2 SDK version?",
|
|
||||||
nullptr, MB_OK);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case HRESULT_FROM_WIN32(ERROR_FILE_EXISTS):
|
|
||||||
{
|
|
||||||
MessageBox(
|
|
||||||
app->window, L"User data folder cannot be created because a file with the same name already exists.", nullptr, MB_OK);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case E_ACCESSDENIED:
|
|
||||||
{
|
|
||||||
MessageBox(
|
|
||||||
app->window, L"Unable to create user data folder, Access Denied.", nullptr, MB_OK);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case E_FAIL:
|
|
||||||
{
|
|
||||||
MessageBox(
|
|
||||||
app->window, L"Edge runtime unable to start", nullptr, MB_OK);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
MessageBox(app->window, L"Failed to create WebView2 environment", nullptr, MB_OK);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Main event loop
|
// Main event loop
|
||||||
MSG msg;
|
MSG msg;
|
||||||
|
@ -14,6 +14,7 @@ extern void DisableWindowIcon(struct Application* app);
|
|||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
import (
|
import (
|
||||||
|
"github.com/ztrue/tracerr"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||||
@ -105,6 +106,7 @@ been sent.
|
|||||||
|
|
||||||
func checkFatal(err error) {
|
func checkFatal(err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
tracerr.PrintSourceColor(err)
|
||||||
globalRadioGroupCache.Dump()
|
globalRadioGroupCache.Dump()
|
||||||
globalRadioGroupMap.Dump()
|
globalRadioGroupMap.Dump()
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@ -118,15 +120,17 @@ func createApplicationMenu(hwnd uintptr) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
window := win32Window(hwnd)
|
||||||
|
|
||||||
if globalApplicationMenu != nil {
|
if globalApplicationMenu != nil {
|
||||||
checkFatal(globalApplicationMenu.Destroy())
|
checkFatal(globalApplicationMenu.Destroy())
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
|
||||||
globalApplicationMenu, err = createMenu(applicationMenu, appMenuType)
|
globalApplicationMenu, err = createMenu(applicationMenu, appMenuType)
|
||||||
checkFatal(err)
|
checkFatal(err)
|
||||||
|
|
||||||
err = setWindowMenu(win32Window(hwnd), globalApplicationMenu.menu)
|
err = setWindowMenu(window, globalApplicationMenu.menu)
|
||||||
checkFatal(err)
|
checkFatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,6 +55,10 @@ struct Application{
|
|||||||
// placeholders
|
// placeholders
|
||||||
char* bindings;
|
char* bindings;
|
||||||
char* initialCode;
|
char* initialCode;
|
||||||
|
|
||||||
|
// DPI
|
||||||
|
UINT dpix;
|
||||||
|
UINT dpiy;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define ON_MAIN_THREAD(code) dispatch( [=]{ code; } )
|
#define ON_MAIN_THREAD(code) dispatch( [=]{ code; } )
|
||||||
|
@ -39,6 +39,16 @@ const MF_UNCHECKED uint32 = 0x00000000
|
|||||||
const MF_BYCOMMAND uint32 = 0x00000000
|
const MF_BYCOMMAND uint32 = 0x00000000
|
||||||
const MF_BYPOSITION uint32 = 0x00000400
|
const MF_BYPOSITION uint32 = 0x00000400
|
||||||
|
|
||||||
|
const WM_SIZE = 5
|
||||||
|
const WM_GETMINMAXINFO = 36
|
||||||
|
|
||||||
|
type Win32Rect struct {
|
||||||
|
Left int32
|
||||||
|
Top int32
|
||||||
|
Right int32
|
||||||
|
Bottom int32
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------- win32 calls -----------------------
|
// ------------------- win32 calls -----------------------
|
||||||
|
|
||||||
func createWin32Menu() (win32Menu, error) {
|
func createWin32Menu() (win32Menu, error) {
|
||||||
@ -97,3 +107,23 @@ func selectRadioItem(selectedMenuID, startMenuItemID, endMenuItemID win32MenuIte
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
//func getWindowRect(window win32Window) (*Win32Rect, error) {
|
||||||
|
// var windowRect Win32Rect
|
||||||
|
// res, _, err := win32GetWindowRect.Call(uintptr(window), uintptr(unsafe.Pointer(&windowRect)))
|
||||||
|
// if res == 0 {
|
||||||
|
// return nil, err
|
||||||
|
// }
|
||||||
|
// return &windowRect, nil
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//func getClientRect(window win32Window) (*Win32Rect, error) {
|
||||||
|
// var clientRect Win32Rect
|
||||||
|
// res, _, err := win32GetClientRect.Call(uintptr(window), uintptr(unsafe.Pointer(&clientRect)))
|
||||||
|
// if res == 0 {
|
||||||
|
// return nil, err
|
||||||
|
// }
|
||||||
|
// return &clientRect, nil
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
Loading…
Reference in New Issue
Block a user