diff --git a/Build-CPP.ps1 b/Build-CPP.ps1 index c05a923..238d8fb 100644 --- a/Build-CPP.ps1 +++ b/Build-CPP.ps1 @@ -1,4 +1,4 @@ -#usage -----> powershell -ExecutionPolicy Bypass -Command "& {. .\Build-CPP.ps1; Dist -major 0 -minor 0 -patch 5}" +#usage -----> powershell -ExecutionPolicy Bypass -Command "& {. .\Build-CPP.ps1; Dist -major 0 -minor 0 -patch 6}" $msbuild = "C:\Program Files\Microsoft Visual Studio\2022\Community\Msbuild\Current\Bin\MSBuild.exe" function Add-RMSkinFooter { diff --git a/Resources/Skins/WebView2/BangCommand/BangCommand.ini b/Resources/Skins/WebView2/BangCommand/BangCommand.ini index 8de6710..54485b4 100644 --- a/Resources/Skins/WebView2/BangCommand/BangCommand.ini +++ b/Resources/Skins/WebView2/BangCommand/BangCommand.ini @@ -7,16 +7,20 @@ DynamicWindowSize=1 Name=BangCommand Author=nstechbytes Information=Demonstrates controlling WebView2 via !CommandMeasure bangs. -Version=0.0.6 +Version=0.0.7 License=Creative Commons Attribution-Non-Commercial-Share Alike 3.0 [Variables] WebURL=https://nstechbytes.pages.dev/ WebW=600 -WebH=350 +WebH=500 WebX=300 -WebY=25 +WebY=45 +ZoomFactor=1.0 Hidden=0 +ClickThrough=0 +AllowDualControl=1 + ; ======================================== ; Measure ; ======================================== @@ -28,7 +32,17 @@ W=#WebW# H=#WebH# X=#WebX# Y=#WebY# +ZoomFactor=#ZoomFactor# Hidden=#Hidden# +ClickThrough=#ClickThrough# +AllowDualControl=#AllowDualControl# +OnWebViewLoadAction=[!log "WebView loaded succesfully!"] +OnWebViewFailAction=[!log "WebView failed :("] +OnPageFirstLoadAction=[!log "First time on this page!"] +OnPageLoadStartAction=[!log "Navigation has started!"] +OnPageLoadingAction=[!log "Page is loading!"] +OnPageLoadFinishAction=[!log "Navigation has finished!"] +OnPageReloadAction=[!log "Page has been reloaded!"] DynamicVariables=1 ; ======================================== @@ -36,7 +50,7 @@ DynamicVariables=1 ; ======================================== [MeterBackground] Meter=Shape -Shape=Rectangle 0,0,950,420,12 | FillColor 0,0,0,200 | StrokeWidth 0 +Shape=Rectangle 0,0,920,570,12 | FillColor 0,0,0,200 | StrokeWidth 0 [Title] Meter=String @@ -65,7 +79,7 @@ MeterStyle=StyleButtonText Text=Navigate to Google (via Variable) X=150 Y=58 -LeftMouseUpAction=[!SetVariable WebURL "https://www.google.com"][!UpdateMeasure MeasureWebView][!Redraw] +LeftMouseUpAction=[!SetVariable WebURL "https://www.google.com"][!UpdateMeasure MeasureWebView] [TxtNavigateGitHub] Meter=String @@ -73,15 +87,15 @@ MeterStyle=StyleButtonText Text=Navigate to GitHub (via Variable) X=150 Y=88 -LeftMouseUpAction=[!SetVariable WebURL "https://github.com"][!UpdateMeasure MeasureWebView][!Redraw] +LeftMouseUpAction=[!SetVariable WebURL "https://github.com"][!UpdateMeasure MeasureWebView] -[TxtNavigateExample] +[TxtNavigateForum] Meter=String MeterStyle=StyleButtonText -Text=Navigate to Example (via Bang) +Text=Navigate to Forum (via Bang) X=150 Y=118 -LeftMouseUpAction=[!CommandMeasure MeasureWebView "Navigate https://example.com"] +LeftMouseUpAction=[!CommandMeasure MeasureWebView "Navigate https://forum.rainmeter.net/"] [TxtReload] Meter=String @@ -119,7 +133,7 @@ W=122 H=24 X=86 Y=208 -LeftMouseUpAction=[!SetVariable Hidden 0][!UpdateMeasure MeasureWebView][!Redraw] +LeftMouseUpAction=[!SetVariable Hidden 0][!UpdateMeasure MeasureWebView] [TxtHide] Meter=String @@ -129,7 +143,7 @@ W=125 H=24 X=212 Y=208 -LeftMouseUpAction=[!SetVariable Hidden 1][!UpdateMeasure MeasureWebView][!Redraw] +LeftMouseUpAction=[!SetVariable Hidden 1][!UpdateMeasure MeasureWebView] [TxtExecuteScript] Meter=String @@ -137,7 +151,7 @@ MeterStyle=StyleButtonText Text=Execute Script X=150 Y=238 -LeftMouseUpAction=[!CommandMeasure MeasureWebView "ExecuteScript alert('Hello!')"] +LeftMouseUpAction=[!CommandMeasure MeasureWebView "ExecuteScript console.log('Script has been executed.')"] [TxtOpenDevTools] Meter=String @@ -153,7 +167,8 @@ MeterStyle=StyleButtonText Text=Set Width: 500 (via Variable) X=150 Y=298 -LeftMouseUpAction=[!SetVariable WebW 500][!UpdateMeasure MeasureWebView][!Redraw] +LeftMouseUpAction=[!SetVariable WebW (#WebW#=600?500:600)][!UpdateMeasure MeasureWebView][!UpdateMeter #CURRENTSECTION#][!Redraw] +DynamicVariables=1 [TxtSetHeight] Meter=String @@ -161,15 +176,17 @@ MeterStyle=StyleButtonText Text=Set Height: 400 (via Variable) X=150 Y=328 -LeftMouseUpAction=[!SetVariable WebH 400][!UpdateMeasure MeasureWebView][!Redraw] +LeftMouseUpAction=[!SetVariable WebH (#WebH#=410?470:410)][!UpdateMeasure MeasureWebView][!UpdateMeter #CURRENTSECTION#][!Redraw] +DynamicVariables=1 [TxtSetX] Meter=String MeterStyle=StyleButtonText -Text=Set X: 100 (via Variable) +Text=Set X: 320 (via Variable) X=150 Y=358 -LeftMouseUpAction=[!SetVariable WebX 100][!UpdateMeasure MeasureWebView][!Redraw] +LeftMouseUpAction=[!SetVariable WebX (#WebX#=300?320:300)][!UpdateMeasure MeasureWebView][!UpdateMeter #CURRENTSECTION#][!Redraw] +DynamicVariables=1 [TxtSetY] Meter=String @@ -177,7 +194,46 @@ MeterStyle=StyleButtonText Text=Set Y: 50 (via Variable) X=150 Y=388 -LeftMouseUpAction=[!SetVariable WebY 50][!UpdateMeasure MeasureWebView][!Redraw] +LeftMouseUpAction=[!SetVariable WebY (#WebY#=45?50:45)][!UpdateMeasure MeasureWebView][!UpdateMeter #CURRENTSECTION#][!Redraw] +DynamicVariables=1 +[TxtSetZoom] +Meter=String +MeterStyle=StyleButtonText +Text=Set ZoomFactor: 2.0 (via Variable) +X=150 +Y=418 +LeftMouseUpAction=[!SetVariable ZoomFactor (#ZoomFactor#=1?2:1)][!UpdateMeasure MeasureWebView][!UpdateMeter #CURRENTSECTION#][!Redraw] +DynamicVariables=1 +[TxtSetEnableClickThrough] +Meter=String +MeterStyle=StyleButtonText +Text=Set ClickThrough: 1 (via Variable) +X=150 +Y=448 +LeftMouseUpAction=[!SetVariable ClickThrough 1][!UpdateMeasure MeasureWebView] +[TxtSetDisableClickThrough] +Meter=String +MeterStyle=StyleButtonText +Text=Set ClickThrough: 0 (via Variable) +X=150 +Y=478 +LeftMouseUpAction=[!SetVariable ClickThrough 0][!UpdateMeasure MeasureWebView] + +[TxtSetEnableAllowDualControl] +Meter=String +MeterStyle=StyleButtonText +Text=Set AllowDualControl: 1 (Variable) +X=150 +Y=508 +LeftMouseUpAction=[!SetVariable AllowDualControl 1][!UpdateMeasure MeasureWebView] + +[TxtSetDisableAllowDualControl] +Meter=String +MeterStyle=StyleButtonText +Text=Set AllowDualControl: 0 (Variable) +X=150 +Y=538 +LeftMouseUpAction=[!SetVariable AllowDualControl 0][!UpdateMeasure MeasureWebView] diff --git a/Resources/Skins/WebView2/Calendar/Calendar.ini b/Resources/Skins/WebView2/Calendar/Calendar.ini index f1fcfc0..65c57a6 100644 --- a/Resources/Skins/WebView2/Calendar/Calendar.ini +++ b/Resources/Skins/WebView2/Calendar/Calendar.ini @@ -5,7 +5,7 @@ Update=1000 Name=Calendar Author=nstechbytes Information=Calendar Widget using WebView2 -Version=0.0.6 +Version=0.0.7 License=Creative Commons Attribution-Non-Commercial-Share Alike 3.0 ; ======================================== ; Measure diff --git a/Resources/Skins/WebView2/Clock/Clock.ini b/Resources/Skins/WebView2/Clock/Clock.ini index 830ef3d..2b61529 100644 --- a/Resources/Skins/WebView2/Clock/Clock.ini +++ b/Resources/Skins/WebView2/Clock/Clock.ini @@ -5,7 +5,7 @@ Update=1000 Name=Calendar Author=nstechbytes Information=Calendar Widget using WebView2 -Version=0.0.6 +Version=0.0.7 License=Creative Commons Attribution-Non-Commercial-Share Alike 3.0 ; ======================================== ; Measure diff --git a/Resources/Skins/WebView2/InformationProperty/InformationProperty.ini b/Resources/Skins/WebView2/InformationProperty/InformationProperty.ini index 021bcb2..dc7f22e 100644 --- a/Resources/Skins/WebView2/InformationProperty/InformationProperty.ini +++ b/Resources/Skins/WebView2/InformationProperty/InformationProperty.ini @@ -7,7 +7,7 @@ DynamicWindowSize=1 Name=InformationProperty Author=nstechbytes Information=Shows Rainmeter information properties via WebView2. -Version=0.0.6 +Version=0.0.7 License=Creative Commons Attribution-Non-Commercial-Share Alike 3.0 ; ======================================== ; Measure diff --git a/Resources/Skins/WebView2/IslamicDate/IslamicDate.ini b/Resources/Skins/WebView2/IslamicDate/IslamicDate.ini index fa8609d..3aa32d9 100644 --- a/Resources/Skins/WebView2/IslamicDate/IslamicDate.ini +++ b/Resources/Skins/WebView2/IslamicDate/IslamicDate.ini @@ -5,7 +5,7 @@ Update=1000 Name=Islamic Date Author=nstechbytes Information=Islamic (Hijri) Date Widget using WebView2 -Version=0.0.6 +Version=0.0.7 License=Creative Commons Attribution-Non-Commercial-Share Alike 3.0 ; ======================================== ; Measure diff --git a/Resources/Skins/WebView2/JSInteraction/JSInteraction.ini b/Resources/Skins/WebView2/JSInteraction/JSInteraction.ini index 71833fd..3e1ba0b 100644 --- a/Resources/Skins/WebView2/JSInteraction/JSInteraction.ini +++ b/Resources/Skins/WebView2/JSInteraction/JSInteraction.ini @@ -7,7 +7,7 @@ DynamicWindowSize=1 Name=JSInteraction Author=nstechbytes Information=Demonstrates JavaScript interaction (OnInitialize, OnUpdate, CallJS) with the WebView2 plugin. -Version=0.0.6 +Version=0.0.7 License=Creative Commons Attribution-Non-Commercial-Share Alike 3.0 ; ======================================== diff --git a/Resources/Skins/WebView2/ReadMeasureOption/ReadMeasureOption.ini b/Resources/Skins/WebView2/ReadMeasureOption/ReadMeasureOption.ini index cc2110b..6e2729c 100644 --- a/Resources/Skins/WebView2/ReadMeasureOption/ReadMeasureOption.ini +++ b/Resources/Skins/WebView2/ReadMeasureOption/ReadMeasureOption.ini @@ -7,7 +7,7 @@ DynamicWindowSize=1 Name=ReadMeasureOption Author=nstechbytes Information=Demonstrates reading options from the current measure using the WebView2 plugin. -Version=0.0.6 +Version=0.0.7 License=Creative Commons Attribution-Non-Commercial-Share Alike 3.0 ; ======================================== ; Measure diff --git a/Resources/Skins/WebView2/ReadSectionOption/ReadSectionOption.ini b/Resources/Skins/WebView2/ReadSectionOption/ReadSectionOption.ini index f19dc6a..62d4c9e 100644 --- a/Resources/Skins/WebView2/ReadSectionOption/ReadSectionOption.ini +++ b/Resources/Skins/WebView2/ReadSectionOption/ReadSectionOption.ini @@ -7,7 +7,7 @@ DynamicWindowSize=1 Name=ReadSectionOption Demo Author=nstechbytes Information=Demonstrates reading options from other sections using WebView2 -Version=0.0.6 +Version=0.0.7 License=Creative Commons Attribution-Non-Commercial-Share Alike 3.0 [Variables] diff --git a/Resources/Skins/WebView2/UtilityFunction/UtilityFunction.ini b/Resources/Skins/WebView2/UtilityFunction/UtilityFunction.ini index 90b87e3..79d64f3 100644 --- a/Resources/Skins/WebView2/UtilityFunction/UtilityFunction.ini +++ b/Resources/Skins/WebView2/UtilityFunction/UtilityFunction.ini @@ -7,7 +7,7 @@ DynamicWindowSize=1 Name=UtilityFunction Author=nstechbytes Information=Demonstrates RainmeterAPI utility functions using the WebView2 plugin. -Version=0.0.6 +Version=0.0.7 License=Creative Commons Attribution-Non-Commercial-Share Alike 3.0 [Variables] diff --git a/Resources/Skins/WebView2/Weather/Weather.ini b/Resources/Skins/WebView2/Weather/Weather.ini index 02b8340..66e0189 100644 --- a/Resources/Skins/WebView2/Weather/Weather.ini +++ b/Resources/Skins/WebView2/Weather/Weather.ini @@ -5,7 +5,7 @@ Update=1000 Name=Weather Author=nstechbytes Information=Weather Widget using WebView2 -Version=0.0.6 +Version=0.0.7 License=Creative Commons Attribution-Non-Commercial-Share Alike 3.0 ; ======================================== ; Measure diff --git a/Resources/skin_definition.json b/Resources/skin_definition.json index 8c25266..d8ceb85 100644 --- a/Resources/skin_definition.json +++ b/Resources/skin_definition.json @@ -1,6 +1,6 @@ { "skinDir": ".\\Resources\\Skins", - "version": "0.0.6", + "version": "0.0.7", "minimumVersion": "4.5", "author": "nstechbytes", "variableFiles": "", @@ -17,5 +17,5 @@ "load": "WebView2\\Clock\\Clock.ini", "headerImage": ".\\Resources\\banner.bmp", "configPrefix": "WebView2", - "output": ".\\dist\\WebView2_v0.0.6_Alpha6.rmskin" + "output": ".\\dist\\WebView2_v0.0.7_Alpha7.rmskin" } diff --git a/WebView2/Plugin.cpp b/WebView2/Plugin.cpp index cc41929..dbab4cd 100644 --- a/WebView2/Plugin.cpp +++ b/WebView2/Plugin.cpp @@ -51,8 +51,8 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) // Measure constructor Measure::Measure() : rm(nullptr), skin(nullptr), skinWindow(nullptr), measureName(nullptr), - width(800), height(600), x(0), y(0), - visible(true), initialized(false), clickthrough(false), webMessageToken{} + width(800), height(600), x(0), y(0), zoomFactor(1.0), + visible(true), initialized(false), clickthrough(false), allowDualControl(true), webMessageToken{} { // Initialize COM for this thread if not already done if (!g_comInitialized) @@ -114,11 +114,69 @@ void UpdateClickthrough(Measure* measure) // EnableWindow(FALSE) makes it ignore mouse input (Clickthrough=1) // EnableWindow(TRUE) makes it accept mouse input (Clickthrough=0) EnableWindow(child, !measure->clickthrough); + + // If enabling clickthrough (disabling input), ensure it loses focus + if (measure->clickthrough) + { + HWND focusedWindow = GetFocus(); + if (focusedWindow && (focusedWindow == child || IsChild(child, focusedWindow))) + { + SetFocus(nullptr); + } + } child = GetWindow(child, GW_HWNDNEXT); } } +// Inject AllowDualControl script into the WebView +void InjectAllowDualControl(Measure* measure) +{ + if (!measure->webView) return; + // Inject script to capture page load events for drag/move and context menu + measure->webView->ExecuteScript( + L"let rm_AllowDualControl=false,rm_AllowDualControlOn=false,rm_AllowDualControlClientX=0,rm_AllowDualControlClientY=0;function rm_SetAllowDualControl(v){rm_AllowDualControl=!!v;if(!rm_AllowDualControl)rm_AllowDualControlOn=false;}document.body.onpointerdown=e=>{if(!rm_AllowDualControl)return;if(e.button===0&&e.ctrlKey){e.preventDefault();e.stopImmediatePropagation();rm_AllowDualControlOn=true;rm_AllowDualControlClientX=e.clientX;rm_AllowDualControlClientY=e.clientY;try{document.body.setPointerCapture(e.pointerId);}catch{}}};document.body.onpointermove=e=>{if(!rm_AllowDualControl||!rm_AllowDualControlOn)return;e.preventDefault();RainmeterAPI.Bang('[!Move '+(e.screenX-RainmeterAPI.ReadFormula('X',0)-rm_AllowDualControlClientX)+' '+(e.screenY-RainmeterAPI.ReadFormula('Y',0)-rm_AllowDualControlClientY)+']');};document.body.onpointerup=e=>{if(!rm_AllowDualControl)return;if(e.button===0){e.preventDefault();rm_AllowDualControlOn=false;try{document.body.releasePointerCapture(e.pointerId);}catch{}}};document.body.oncontextmenu=e=>{if(!rm_AllowDualControl)return;if(e.button===2&&e.ctrlKey){e.preventDefault();RainmeterAPI.Bang('[!SkinMenu]');}};", + Callback( + [measure](HRESULT errorCode, LPCWSTR resultObjectAsJson) -> HRESULT + { + return S_OK; + } + ).Get() + ); + measure->isAllowDualControlInjected = true; + UpdateAllowDualControl(measure); +} + +// Update AllowDualControl state in the WebView +void UpdateAllowDualControl(Measure* measure) +{ + if (!measure->webView) return; + + if (measure->allowDualControl) + { + measure->webView->ExecuteScript( + L"rm_SetAllowDualControl(true);", + Callback( + [measure](HRESULT errorCode, LPCWSTR resultObjectAsJson) -> HRESULT + { + return S_OK; + } + ).Get() + ); + } + else { + measure->webView->ExecuteScript( + L"rm_SetAllowDualControl(false);", + Callback( + [measure](HRESULT errorCode, LPCWSTR resultObjectAsJson) -> HRESULT + { + return S_OK; + } + ).Get() + ); + } +} + PLUGIN_EXPORT void Reload(void* data, void* rm, double* maxValue) { Measure* measure = (Measure*)data; @@ -172,12 +230,72 @@ PLUGIN_EXPORT void Reload(void* data, void* rm, double* maxValue) int newHeight = RmReadInt(rm, L"H", 600); int newX = RmReadInt(rm, L"X", 0); int newY = RmReadInt(rm, L"Y", 0); - bool newVisible = RmReadInt(rm, L"Hidden", 0) == 0; - bool newClickthrough = RmReadInt(rm, L"Clickthrough", 0) != 0; - + double newZoomFactor = RmReadFormula(rm, L"ZoomFactor", 1.0); + bool newVisible = RmReadInt(rm, L"Hidden", 0) <= 0; + bool newClickthrough = RmReadInt(rm, L"Clickthrough", 0) >= 1; + + // Read AllowDualControl for Yincognito's script injection + bool newAllowDualControl = RmReadInt(rm, L"AllowDualControl", 1) >= 1; + + // Read OnWebViewLoadAction + std::wstring newOnWebViewLoadAction; + LPCWSTR onWebViewLoadOption = RmReadString(rm, L"OnWebViewLoadAction", L"", FALSE); + if (onWebViewLoadOption && wcslen(onWebViewLoadOption) > 0) + { + newOnWebViewLoadAction = onWebViewLoadOption; + } + + // Read OnWebViewFailAction + std::wstring newOnWebViewFailAction; + LPCWSTR onWebViewFailOption = RmReadString(rm, L"OnWebViewFailAction", L"", FALSE); + if (onWebViewFailOption && wcslen(onWebViewFailOption) > 0) + { + newOnWebViewFailAction = onWebViewFailOption; + } + + // Read OnPageLoadStartAction + std::wstring newOnPageLoadStartAction; + LPCWSTR onPageLoadStartOption = RmReadString(rm, L"OnPageLoadStartAction", L"", FALSE); + if (onPageLoadStartOption && wcslen(onPageLoadStartOption) > 0) + { + newOnPageLoadStartAction = onPageLoadStartOption; + } + + // Read OnPageLoadingAction + std::wstring newOnPageLoadingAction; + LPCWSTR onPageLoadingOption = RmReadString(rm, L"OnPageLoadingAction", L"", FALSE); + if (onPageLoadingOption && wcslen(onPageLoadingOption) > 0) + { + newOnPageLoadingAction = onPageLoadingOption; + } + + // Read OnPageLoadFinishAction + std::wstring newOnPageLoadFinishAction; + LPCWSTR onPageLoadFinishOption = RmReadString(rm, L"OnPageLoadFinishAction", L"", FALSE); + if (onPageLoadFinishOption && wcslen(onPageLoadFinishOption) > 0) + { + newOnPageLoadFinishAction = onPageLoadFinishOption; + } + + // Read OnPageFirstLoadAction + std::wstring newOnPageFirstLoadAction; + LPCWSTR onPageFirstLoadOption = RmReadString(rm, L"OnPageFirstLoadAction", L"", FALSE); + if (onPageFirstLoadOption && wcslen(onPageFirstLoadOption) > 0) + { + newOnPageFirstLoadAction = onPageFirstLoadOption; + } + + // Read OnPageReloadAction + std::wstring newOnPageReloadAction; + LPCWSTR onPageReloadOption = RmReadString(rm, L"OnPageReloadAction", L"", FALSE); + if (onPageReloadOption && wcslen(onPageReloadOption) > 0) + { + newOnPageReloadAction = onPageReloadOption; + } + // Check if URL has changed (requires recreation) bool urlChanged = (newUrl != measure->url); - + // Check if dimensions or position changed (can be updated dynamically) bool dimensionsChanged = (newWidth != measure->width || newHeight != measure->height || @@ -186,16 +304,27 @@ PLUGIN_EXPORT void Reload(void* data, void* rm, double* maxValue) bool visibilityChanged = (newVisible != measure->visible); bool clickthroughChanged = (newClickthrough != measure->clickthrough); - + bool allowDualControlChanged = (newAllowDualControl != measure->allowDualControl); + bool zoomFactorChanged = (newZoomFactor != measure->zoomFactor); + // Update stored values measure->url = newUrl; measure->width = newWidth; measure->height = newHeight; measure->x = newX; measure->y = newY; + measure->zoomFactor = newZoomFactor; measure->visible = newVisible; measure->clickthrough = newClickthrough; - + measure->allowDualControl = newAllowDualControl; + measure->onWebViewLoadAction = newOnWebViewLoadAction; + measure->onWebViewFailAction = newOnWebViewFailAction; + measure->onPageLoadStartAction = newOnPageLoadStartAction; + measure->onPageLoadingAction = newOnPageLoadingAction; + measure->onPageLoadFinishAction = newOnPageLoadFinishAction; + measure->onPageFirstLoadAction = newOnPageFirstLoadAction; + measure->onPageReloadAction = newOnPageReloadAction; + // Only create WebView2 if not initialized OR if URL changed if (!measure->initialized || urlChanged) { @@ -210,6 +339,11 @@ PLUGIN_EXPORT void Reload(void* data, void* rm, double* maxValue) else { // First initialization - create WebView2 + if (measure->isCreationInProgress) + { + // Avoid re-entrancy if creation is already in progress + return; + } CreateWebView2(measure); } } @@ -231,11 +365,26 @@ PLUGIN_EXPORT void Reload(void* data, void* rm, double* maxValue) { measure->webViewController->put_IsVisible(measure->visible ? TRUE : FALSE); } + + if (zoomFactorChanged && measure->webViewController) + { + measure->webViewController->put_ZoomFactor(measure->zoomFactor); + } if (clickthroughChanged) { UpdateClickthrough(measure); } + + if (allowDualControlChanged) + { + if (!measure->isAllowDualControlInjected) + { + InjectAllowDualControl(measure); + } + else + UpdateAllowDualControl(measure); + } } } @@ -265,13 +414,6 @@ PLUGIN_EXPORT double Update(void* data) { measure->callbackResult = result; } - - // Trigger Rainmeter redraw after callback completes - if (measure->skin) - { - RmExecute(measure->skin, L"!UpdateMeter *"); - RmExecute(measure->skin, L"!Redraw"); - } } return S_OK; } @@ -294,6 +436,7 @@ PLUGIN_EXPORT LPCWSTR GetString(void* data) return L"0"; } + PLUGIN_EXPORT void ExecuteBang(void* data, LPCWSTR args) { Measure* measure = (Measure*)data; diff --git a/WebView2/Plugin.h b/WebView2/Plugin.h index 0ea5939..c51e5d6 100644 --- a/WebView2/Plugin.h +++ b/WebView2/Plugin.h @@ -20,27 +20,41 @@ struct Measure void* skin; HWND skinWindow; LPCWSTR measureName; - + std::wstring url; + std::wstring currentUrl; int width; int height; int x; int y; + double zoomFactor; bool visible; bool initialized; bool clickthrough; - + bool isCreationInProgress = false; + bool isFirstLoad = true; + bool allowDualControl; + bool isAllowDualControlInjected = false; + + std::wstring onWebViewLoadAction; + std::wstring onWebViewFailAction; + std::wstring onPageFirstLoadAction; + std::wstring onPageLoadStartAction; + std::wstring onPageLoadingAction; + std::wstring onPageLoadFinishAction; + std::wstring onPageReloadAction; + wil::com_ptr webViewController; wil::com_ptr webView; EventRegistrationToken webMessageToken; - + std::wstring buffer; // Buffer for section variable return values std::wstring callbackResult; // Stores return value from OnInitialize/OnUpdate callbacks std::map jsResults; // Cache for CallJS results - + Measure(); ~Measure(); - + // Member callback functions for WebView2 creation HRESULT CreateEnvironmentHandler(HRESULT result, ICoreWebView2Environment* env); HRESULT CreateControllerHandler(HRESULT result, ICoreWebView2Controller* controller); @@ -49,4 +63,7 @@ struct Measure // WebView2 functions void CreateWebView2(Measure* measure); void UpdateClickthrough(Measure* measure); +void InjectAllowDualControl(Measure* measure); +void UpdateAllowDualControl(Measure* measure); + diff --git a/WebView2/WebView2.cpp b/WebView2/WebView2.cpp index a3db632..6eb16b6 100644 --- a/WebView2/WebView2.cpp +++ b/WebView2/WebView2.cpp @@ -12,6 +12,18 @@ void CreateWebView2(Measure* measure) RmLog(measure->rm, LOG_ERROR, L"WebView2: Invalid measure or skin window"); return; } + + if (measure->initialized) + { + return; + } + + if (measure->isCreationInProgress) + { + return; + } + + measure->isCreationInProgress = true; // Create user data folder in TEMP directory to avoid permission issues wchar_t tempPath[MAX_PATH]; @@ -28,6 +40,7 @@ void CreateWebView2(Measure* measure) measure, &Measure::CreateEnvironmentHandler ).Get() + ); if (FAILED(hr)) @@ -38,6 +51,11 @@ void CreateWebView2(Measure* measure) swprintf_s(errorMsg, L"WebView2: Failed to start creation process (HRESULT: 0x%08X). Make sure WebView2 Runtime is installed.", hr); RmLog(measure->rm, LOG_ERROR, errorMsg); } + if (measure->skin && wcslen(measure->onWebViewFailAction.c_str()) > 0) + { + RmExecute(measure->skin, measure->onWebViewFailAction.c_str()); + } + measure->isCreationInProgress = false; } } @@ -52,6 +70,11 @@ HRESULT Measure::CreateEnvironmentHandler(HRESULT result, ICoreWebView2Environme swprintf_s(errorMsg, L"WebView2: Failed to create environment (HRESULT: 0x%08X)", result); RmLog(rm, LOG_ERROR, errorMsg); } + if (skin && wcslen(onWebViewFailAction.c_str()) > 0) + { + RmExecute(skin, onWebViewFailAction.c_str()); + } + isCreationInProgress = false; return result; } @@ -70,6 +93,7 @@ HRESULT Measure::CreateEnvironmentHandler(HRESULT result, ICoreWebView2Environme // Controller creation callback HRESULT Measure::CreateControllerHandler(HRESULT result, ICoreWebView2Controller* controller) { + if (FAILED(result)) { if (rm) @@ -78,6 +102,11 @@ HRESULT Measure::CreateControllerHandler(HRESULT result, ICoreWebView2Controller swprintf_s(errorMsg, L"WebView2: Failed to create controller (HRESULT: 0x%08X)", result); RmLog(rm, LOG_ERROR, errorMsg); } + if (skin && wcslen(onWebViewFailAction.c_str()) > 0) + { + RmExecute(skin, onWebViewFailAction.c_str()); + } + isCreationInProgress = false; return result; } @@ -85,6 +114,11 @@ HRESULT Measure::CreateControllerHandler(HRESULT result, ICoreWebView2Controller { if (rm) RmLog(rm, LOG_ERROR, L"WebView2: Controller is null"); + if (skin && wcslen(onWebViewFailAction.c_str()) > 0) + { + RmExecute(skin, onWebViewFailAction.c_str()); + } + isCreationInProgress = false; return S_FALSE; } @@ -108,7 +142,10 @@ HRESULT Measure::CreateControllerHandler(HRESULT result, ICoreWebView2Controller // Set initial visibility webViewController->put_IsVisible(visible ? TRUE : FALSE); - + + // Set initial zoom factor + webViewController->put_ZoomFactor(zoomFactor); + // Transparent background auto controller2 = webViewController.query(); if (controller2) @@ -142,12 +179,81 @@ HRESULT Measure::CreateControllerHandler(HRESULT result, ICoreWebView2Controller L"window.RainmeterAPI = chrome.webview.hostObjects.sync.RainmeterAPI", nullptr ); - - // Add NavigationCompleted event to call OnInitialize after page loads + + // Add SourceChanged event to detect changes in URL + webView->add_SourceChanged( + Callback( + [this](ICoreWebView2* sender, ICoreWebView2SourceChangedEventArgs* args) -> HRESULT + { + wil::unique_cotaskmem_string updatedUri; + + if (SUCCEEDED(sender->get_Source(&updatedUri)) && updatedUri.get() != nullptr) + { + std::wstring newUrl = updatedUri.get(); + + if (currentUrl != newUrl) + { + // URL changed + isFirstLoad = true; + currentUrl = newUrl; + } + else + { + // URL did not change + isFirstLoad = false; + } + } + return S_OK; + } + ).Get(), + nullptr + ); + + // Add NavigationStarting event to call action when navigation starts + webView->add_NavigationStarting( + Callback( + [this](ICoreWebView2* sender, ICoreWebView2NavigationStartingEventArgs* args) -> HRESULT + { + if (wcslen(onPageLoadStartAction.c_str()) > 0) + { + if (skin) + RmExecute(skin, onPageLoadStartAction.c_str()); + } + return S_OK; + } + ).Get(), + nullptr + ); + + // Add ContentLoading event to call action when page starts loading + webView->add_ContentLoading( + Callback( + [this](ICoreWebView2* sender, ICoreWebView2ContentLoadingEventArgs* args) -> HRESULT + { + if (wcslen(onPageLoadingAction.c_str()) > 0) + { + if (skin) + RmExecute(skin, onPageLoadingAction.c_str()); + } + return S_OK; + } + ).Get(), + nullptr + ); + + // Add NavigationCompleted event to call OnInitialize after page loads and handle load actions webView->add_NavigationCompleted( Callback( [this](ICoreWebView2* sender, ICoreWebView2NavigationCompletedEventArgs* args) -> HRESULT { + isAllowDualControlInjected = false; + + // Inject script to capture page load events for drag/move and context menu + if (allowDualControl) + { + InjectAllowDualControl(this); + } + // Call JavaScript OnInitialize callback if it exists and capture return value webView->ExecuteScript( L"(function() { if (typeof window.OnInitialize === 'function') { var result = window.OnInitialize(); return result !== undefined ? String(result) : ''; } return ''; })();", @@ -162,30 +268,47 @@ HRESULT Measure::CreateControllerHandler(HRESULT result, ICoreWebView2Controller { result = result.substr(1, result.length() - 2); } - + // Store the callback result if (!result.empty() && result != L"null") { callbackResult = result; - - // Trigger Rainmeter redraw after callback completes - if (skin) - { - RmExecute(skin, L"!UpdateMeter *"); - RmExecute(skin, L"!Redraw"); - } } } return S_OK; } ).Get() ); + + if (isFirstLoad) // First load + { + if (wcslen(onPageFirstLoadAction.c_str()) > 0) + { + if (skin) + RmExecute(skin, onPageFirstLoadAction.c_str()); + } + isFirstLoad = false; + } + else // Page reload + { + if (wcslen(onPageReloadAction.c_str()) > 0) + { + if (skin) + RmExecute(skin, onPageReloadAction.c_str()); + } + } + // Common action after any page load + if (wcslen(onPageLoadFinishAction.c_str()) > 0) + { + if (skin) + RmExecute(skin, onPageLoadFinishAction.c_str()); + } return S_OK; } ).Get(), nullptr ); - + // Navigate to URL if (!url.empty()) { @@ -194,9 +317,16 @@ HRESULT Measure::CreateControllerHandler(HRESULT result, ICoreWebView2Controller initialized = true; + isCreationInProgress = false; + if (rm) RmLog(rm, LOG_NOTICE, L"WebView2: Initialized successfully with COM Host Objects"); + if (wcslen(onWebViewLoadAction.c_str()) > 0) + { + RmExecute(skin, onWebViewLoadAction.c_str()); + } + // Apply initial clickthrough state UpdateClickthrough(this); diff --git a/WebView2/WebView2.rc b/WebView2/WebView2.rc index 89c9450..9b0f1ed 100644 Binary files a/WebView2/WebView2.rc and b/WebView2/WebView2.rc differ