5
0
mirror of https://github.com/wailsapp/wails.git synced 2025-05-07 05:11:36 +08:00

[windows] App assets loading correctly

This commit is contained in:
Lea Anthony 2021-04-30 21:14:09 +10:00
parent 247df54ef0
commit 5da198de7e
7 changed files with 154 additions and 39 deletions

File diff suppressed because one or more lines are too long

View File

@ -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) {

View File

@ -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

View File

@ -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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -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": {