mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-07 00:39:18 +08:00
[windows] App assets loading correctly
This commit is contained in:
parent
247df54ef0
commit
5da198de7e
1
v2/internal/bridge/windows.js
Normal file
1
v2/internal/bridge/windows.js
Normal file
File diff suppressed because one or more lines are too long
@ -1,4 +1,6 @@
|
|||||||
// Some code may be inspired by or directly used from Webview.
|
// Some code may be inspired by or directly used from Webview (c) zserge.
|
||||||
|
// License included in README.md
|
||||||
|
|
||||||
#include "ffenestri_windows.h"
|
#include "ffenestri_windows.h"
|
||||||
#include "wv2ComHandler_windows.h"
|
#include "wv2ComHandler_windows.h"
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@ -7,15 +9,35 @@
|
|||||||
#include <locale>
|
#include <locale>
|
||||||
#include <codecvt>
|
#include <codecvt>
|
||||||
#include "windows/WebView2.h"
|
#include "windows/WebView2.h"
|
||||||
|
#include <Shlobj.h>
|
||||||
|
|
||||||
int debug = 0;
|
int debug = 0;
|
||||||
DWORD mainThread;
|
DWORD mainThread;
|
||||||
|
|
||||||
|
// --- Assets
|
||||||
|
extern const unsigned char runtime;
|
||||||
|
extern const unsigned char *defaultDialogIcons[];
|
||||||
|
|
||||||
// dispatch will execute the given `func` pointer
|
// dispatch will execute the given `func` pointer
|
||||||
void dispatch(dispatchFunction func) {
|
void dispatch(dispatchFunction func) {
|
||||||
PostThreadMessage(mainThread, WM_APP, 0, (LPARAM) new dispatchFunction(func));
|
PostThreadMessage(mainThread, WM_APP, 0, (LPARAM) new dispatchFunction(func));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LPWSTR cstrToLPWSTR(const char *cstr) {
|
||||||
|
int wchars_num = MultiByteToWideChar( CP_UTF8 , 0 , cstr , -1, NULL , 0 );
|
||||||
|
wchar_t* wstr = new wchar_t[wchars_num+1];
|
||||||
|
MultiByteToWideChar( CP_UTF8 , 0 , cstr , -1, wstr , wchars_num );
|
||||||
|
return wstr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Credit: https://stackoverflow.com/a/9842450
|
||||||
|
char* LPWSTRToCstr(LPWSTR input) {
|
||||||
|
int length = WideCharToMultiByte(CP_UTF8, 0, input, -1, 0, 0, NULL, NULL);
|
||||||
|
char* output = new char[length];
|
||||||
|
WideCharToMultiByte(CP_UTF8, 0, input, -1, output , length, NULL, NULL);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
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
|
||||||
@ -60,6 +82,15 @@ void SetMaxWindowSize(struct Application* app, int maxWidth, int maxHeight) {
|
|||||||
app->maxHeight = (LONG)maxHeight;
|
app->maxHeight = (LONG)maxHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetBindings(struct Application *app, const char *bindings) {
|
||||||
|
std::string temp = std::string("window.wailsbindings = \"") + std::string(bindings) + std::string("\";");
|
||||||
|
app->bindings = new char[temp.length()+1];
|
||||||
|
memcpy(app->bindings, temp.c_str(), temp.length()+1);
|
||||||
|
printf("length = %d\n", temp.length());
|
||||||
|
printf("app->bindings: %s\n", app->bindings);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
||||||
|
|
||||||
struct Application *app = (struct Application *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
struct Application *app = (struct Application *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||||
@ -116,30 +147,77 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
|||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void init(struct Application *app, const char* js) {
|
||||||
|
LPCWSTR wjs = cstrToLPWSTR(js);
|
||||||
|
app->webview->AddScriptToExecuteOnDocumentCreated(wjs, nullptr);
|
||||||
|
delete[] wjs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadAssets(struct Application* app) {
|
||||||
|
|
||||||
|
// Load bindings
|
||||||
|
ExecJS(app, app->bindings);
|
||||||
|
delete[] app->bindings;
|
||||||
|
|
||||||
|
// Load runtime
|
||||||
|
ExecJS(app, (const char*)&runtime);
|
||||||
|
|
||||||
|
int index = 1;
|
||||||
|
while(1) {
|
||||||
|
// Get next asset pointer
|
||||||
|
const unsigned char *asset = assets[index];
|
||||||
|
|
||||||
|
// If we have no more assets, break
|
||||||
|
if (asset == 0x00) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExecJS(app, (const char*)asset);
|
||||||
|
index++;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Disable context menu if not in debug mode
|
||||||
|
if( debug != 1 ) {
|
||||||
|
ExecJS(app, "wails._.DisableDefaultContextMenu();");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show app if we need to
|
||||||
|
if( app->startHidden == false ) {
|
||||||
|
Show(app);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
bool initWebView2(struct Application *app, int debug, messageCallback cb) {
|
bool initWebView2(struct Application *app, int debugEnabled, messageCallback cb) {
|
||||||
|
|
||||||
|
debug = debugEnabled;
|
||||||
|
|
||||||
CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
|
CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
|
||||||
|
|
||||||
std::atomic_flag flag = ATOMIC_FLAG_INIT;
|
std::atomic_flag flag = ATOMIC_FLAG_INIT;
|
||||||
flag.test_and_set();
|
flag.test_and_set();
|
||||||
|
|
||||||
char currentExePath[MAX_PATH];
|
// char currentExePath[MAX_PATH];
|
||||||
GetModuleFileNameA(NULL, currentExePath, MAX_PATH);
|
// GetModuleFileNameA(NULL, currentExePath, MAX_PATH);
|
||||||
char *currentExeName = PathFindFileNameA(currentExePath);
|
// char *currentExeName = PathFindFileNameA(currentExePath);
|
||||||
|
|
||||||
|
|
||||||
// std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> wideCharConverter;
|
// std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> wideCharConverter;
|
||||||
// std::wstring userDataFolder = wideCharConverter.from_bytes(std::getenv("APPDATA"));
|
// auto exeName = wideCharConverter.from_bytes(currentExeName);
|
||||||
// std::wstring currentExeNameW = wideCharConverter.from_bytes(currentExeName);
|
//
|
||||||
|
// PWSTR path;
|
||||||
// printf("userdata folder = %s\n", userDataFolder.c_str());
|
// HRESULT appDataResult = SHGetFolderPathAndSubDir(app->window, CSIDL_LOCAL_APPDATA, nullptr, SHGFP_TYPE_CURRENT, exeName.c_str(), path);
|
||||||
|
// if ( appDataResult == false ) {
|
||||||
|
// path = nullptr;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
|
||||||
ICoreWebView2Controller *controller;
|
ICoreWebView2Controller *controller;
|
||||||
ICoreWebView2* webview;
|
ICoreWebView2* webview;
|
||||||
|
|
||||||
HRESULT res = CreateCoreWebView2EnvironmentWithOptions(
|
HRESULT res = CreateCoreWebView2EnvironmentWithOptions(
|
||||||
nullptr, /*(userDataFolder + L"/" + currentExeNameW).c_str()*/ nullptr, nullptr,
|
nullptr, nullptr, nullptr,
|
||||||
new wv2ComHandler(app->window, cb,
|
new wv2ComHandler(app, app->window, cb,
|
||||||
[&](ICoreWebView2Controller *webviewController) {
|
[&](ICoreWebView2Controller *webviewController) {
|
||||||
controller = webviewController;
|
controller = webviewController;
|
||||||
controller->get_CoreWebView2(&webview);
|
controller->get_CoreWebView2(&webview);
|
||||||
@ -163,9 +241,13 @@ bool initWebView2(struct Application *app, int debug, messageCallback cb) {
|
|||||||
GetClientRect(app->window, &bounds);
|
GetClientRect(app->window, &bounds);
|
||||||
app->webviewController->put_Bounds(bounds);
|
app->webviewController->put_Bounds(bounds);
|
||||||
|
|
||||||
// Schedule an async task to navigate to Bing
|
// Callback hack
|
||||||
app->webview->Navigate(L"https://wails.app/");
|
app->webview->AddScriptToExecuteOnDocumentCreated(L"window.chrome.webview.postMessage('I');", nullptr);
|
||||||
//// init("window.external={invoke:s=>window.chrome.webview.postMessage(s)}");
|
// Load the HTML
|
||||||
|
LPCWSTR html = (LPCWSTR) cstrToLPWSTR((char*)assets[0]);
|
||||||
|
app->webview->Navigate(html);
|
||||||
|
|
||||||
|
messageFromWindowCallback("Ej{\"name\":\"wails:launched\",\"data\":[]}");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,12 +299,6 @@ void Run(struct Application* app, int argc, char **argv) {
|
|||||||
|
|
||||||
// private center() as we are on main thread
|
// private center() as we are on main thread
|
||||||
center(app);
|
center(app);
|
||||||
// if( debug == 1 ) {
|
|
||||||
// BOOL supported = SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
|
|
||||||
// if( !supported ) {
|
|
||||||
// SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
ShowWindow(app->window, startVisibility);
|
ShowWindow(app->window, startVisibility);
|
||||||
UpdateWindow(app->window);
|
UpdateWindow(app->window);
|
||||||
SetFocus(app->window);
|
SetFocus(app->window);
|
||||||
@ -256,10 +332,13 @@ void SetDebug(struct Application* app, int flag) {
|
|||||||
debug = flag;
|
debug = flag;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetBindings(struct Application* app, const char *bindings) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void ExecJS(struct Application* app, const char *script) {
|
void ExecJS(struct Application* app, const char *script) {
|
||||||
|
printf("Exec: %s\n", script);
|
||||||
|
ON_MAIN_THREAD(
|
||||||
|
LPWSTR s = cstrToLPWSTR(script);
|
||||||
|
app->webview->ExecuteScript(s, nullptr);
|
||||||
|
delete[] s;
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void hide(struct Application* app) {
|
void hide(struct Application* app) {
|
||||||
@ -407,13 +486,12 @@ void SetPosition(struct Application* app, int x, int y) {
|
|||||||
void Quit(struct Application* app) {
|
void Quit(struct Application* app) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Credit: https://stackoverflow.com/a/6693107
|
// Credit: https://stackoverflow.com/a/6693107
|
||||||
void setTitle(struct Application* app, const char *title) {
|
void setTitle(struct Application* app, const char *title) {
|
||||||
int wchars_num = MultiByteToWideChar( CP_UTF8 , 0 , title , -1, NULL , 0 );
|
LPCTSTR text = cstrToLPWSTR(title);
|
||||||
wchar_t* wstr = new wchar_t[wchars_num];
|
SetWindowText(app->window, text);
|
||||||
MultiByteToWideChar( CP_UTF8 , 0 , title , -1, wstr , wchars_num );
|
delete[] text;
|
||||||
SetWindowText(app->window, wstr);
|
|
||||||
delete[] wstr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetTitle(struct Application* app, const char *title) {
|
void SetTitle(struct Application* app, const char *title) {
|
||||||
|
@ -9,8 +9,15 @@
|
|||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <wingdi.h>
|
#include <wingdi.h>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <codecvt>
|
||||||
#include "windows/WebView2.h"
|
#include "windows/WebView2.h"
|
||||||
|
|
||||||
|
#include "assets.h"
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
//#include "userdialogicons.h"
|
||||||
|
|
||||||
|
|
||||||
struct Application{
|
struct Application{
|
||||||
// Window specific
|
// Window specific
|
||||||
HWND window;
|
HWND window;
|
||||||
@ -34,6 +41,8 @@ struct Application{
|
|||||||
LONG maxWidth;
|
LONG maxWidth;
|
||||||
LONG maxHeight;
|
LONG maxHeight;
|
||||||
int frame;
|
int frame;
|
||||||
|
|
||||||
|
char* bindings;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define ON_MAIN_THREAD(code) dispatch( [=]{ code; } )
|
#define ON_MAIN_THREAD(code) dispatch( [=]{ code; } )
|
||||||
@ -44,5 +53,12 @@ typedef std::function<void(ICoreWebView2Controller *)> comHandlerCallback;
|
|||||||
|
|
||||||
void center(struct Application*);
|
void center(struct Application*);
|
||||||
void setTitle(struct Application* app, const char *title);
|
void setTitle(struct Application* app, const char *title);
|
||||||
|
char* LPWSTRToCstr(LPWSTR input);
|
||||||
|
void loadAssets(struct Application* app);
|
||||||
|
|
||||||
|
// Callback
|
||||||
|
extern "C" {
|
||||||
|
void messageFromWindowCallback(const char *);
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -12,14 +12,17 @@ class wv2ComHandler
|
|||||||
: public ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler,
|
: public ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler,
|
||||||
public ICoreWebView2CreateCoreWebView2ControllerCompletedHandler,
|
public ICoreWebView2CreateCoreWebView2ControllerCompletedHandler,
|
||||||
public ICoreWebView2WebMessageReceivedEventHandler,
|
public ICoreWebView2WebMessageReceivedEventHandler,
|
||||||
public ICoreWebView2PermissionRequestedEventHandler {
|
public ICoreWebView2PermissionRequestedEventHandler
|
||||||
|
{
|
||||||
|
|
||||||
|
struct Application *app;
|
||||||
HWND window;
|
HWND window;
|
||||||
messageCallback mcb;
|
messageCallback mcb;
|
||||||
comHandlerCallback cb;
|
comHandlerCallback cb;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
wv2ComHandler(HWND window, messageCallback mcb, comHandlerCallback cb) {
|
wv2ComHandler(struct Application *app, HWND window, messageCallback mcb, comHandlerCallback cb) {
|
||||||
|
this->app = app;
|
||||||
this->window = window;
|
this->window = window;
|
||||||
this->mcb = mcb;
|
this->mcb = mcb;
|
||||||
this->cb = cb;
|
this->cb = cb;
|
||||||
@ -47,17 +50,32 @@ class wv2ComHandler
|
|||||||
cb(controller);
|
cb(controller);
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is called when JS posts a message back to webkit
|
||||||
HRESULT STDMETHODCALLTYPE Invoke(
|
HRESULT STDMETHODCALLTYPE Invoke(
|
||||||
ICoreWebView2 *sender, ICoreWebView2WebMessageReceivedEventArgs *args) {
|
ICoreWebView2 *sender, ICoreWebView2WebMessageReceivedEventArgs *args) {
|
||||||
printf("CCCCCCCCCCC\n");
|
|
||||||
LPWSTR message;
|
LPWSTR message;
|
||||||
args->TryGetWebMessageAsString(&message);
|
args->TryGetWebMessageAsString(&message);
|
||||||
|
if ( message == nullptr ) {
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
const char *m = LPWSTRToCstr(message);
|
||||||
|
switch(m[0]) {
|
||||||
|
|
||||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> wideCharConverter;
|
// Standard message for backend
|
||||||
mcb(wideCharConverter.to_bytes(message));
|
case 'S':
|
||||||
sender->PostWebMessageAsString(message);
|
printf("--> Message to backend: %s\n", &m[1]);
|
||||||
|
messageFromWindowCallback(&m[1]);
|
||||||
|
break;
|
||||||
|
// DOM Initialised
|
||||||
|
case 'I':
|
||||||
|
loadAssets(app);
|
||||||
|
break;
|
||||||
|
|
||||||
CoTaskMemFree(message);
|
default:
|
||||||
|
printf("----> Unknown message type: %c\n", m[0]);
|
||||||
|
}
|
||||||
|
delete[] m;
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
HRESULT STDMETHODCALLTYPE
|
HRESULT STDMETHODCALLTYPE
|
||||||
|
1
v2/internal/runtime/assets/desktop_darwin.js
Normal file
1
v2/internal/runtime/assets/desktop_darwin.js
Normal file
File diff suppressed because one or more lines are too long
1
v2/internal/runtime/assets/desktop_windows.js
Normal file
1
v2/internal/runtime/assets/desktop_windows.js
Normal file
File diff suppressed because one or more lines are too long
@ -4,8 +4,8 @@
|
|||||||
"description": "The Javascript Wails Runtime",
|
"description": "The Javascript Wails Runtime",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build:desktop": "./node_modules/.bin/eslint core/ && ./node_modules/.bin/webpack --env desktop --colors",
|
"build:desktop": "npx eslint core/ && npx webpack --env desktop --colors",
|
||||||
"build:server": "./node_modules/.bin/eslint core/ && ./node_modules/.bin/webpack --env server --colors",
|
"build:server": "npx eslint core/ && npx webpack --env server --colors",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
|
Loading…
Reference in New Issue
Block a user