├── LICENSE ├── Lib ├── 32bit │ └── WebView2Loader.dll ├── 64bit │ └── WebView2Loader.dll ├── ComVar.ahk ├── Promise.ahk ├── WebView2.ahk └── WebViewToo.ahk ├── Pages ├── Bootstrap │ ├── bootstrap.bundle.min.js │ ├── bootstrap.min.css │ ├── color-modes.js │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ ├── sidebars.css │ └── sidebars.js └── index.html ├── README.md ├── Simple Browser.ahk └── WebViewGui Example.ahk /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Ryan Dingman (known also as Panaku, The-CoDingman) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Lib/32bit/WebView2Loader.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/The-CoDingman/WebViewToo/53fc321984d1ad9665038950f5ef4cedd1face35/Lib/32bit/WebView2Loader.dll -------------------------------------------------------------------------------- /Lib/64bit/WebView2Loader.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/The-CoDingman/WebViewToo/53fc321984d1ad9665038950f5ef4cedd1face35/Lib/64bit/WebView2Loader.dll -------------------------------------------------------------------------------- /Lib/ComVar.ahk: -------------------------------------------------------------------------------- 1 | ;/////////////////////////////////////////////////////////////////////////////////////////// 2 | ; MIT License 3 | ; 4 | ; Copyright (c) 2023 thqby 5 | ; 6 | ; Permission is hereby granted, free of charge, to any person obtaining a copy 7 | ; of this software and associated documentation files (the "Software"), to deal 8 | ; in the Software without restriction, including without limitation the rights 9 | ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | ; copies of the Software, and to permit persons to whom the Software is 11 | ; furnished to do so, subject to the following conditions: 12 | ; 13 | ; The above copyright notice and this permission notice shall be included in all 14 | ; copies or substantial portions of the Software. 15 | ; 16 | ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | ; SOFTWARE. 23 | ;/////////////////////////////////////////////////////////////////////////////////////////// 24 | 25 | ; Construction and deconstruction VARIANT struct 26 | class ComVar extends Buffer { 27 | /** 28 | * Construction VARIANT struct, `ptr` property points to the address, `__Item` property returns var's Value 29 | * @param vVal Values that need to be wrapped, supports String, Integer, Double, Array, ComValue, ComObjArray 30 | * ### example 31 | * `var1 := ComVar('string'), MsgBox(var1[])` 32 | * 33 | * `var2 := ComVar([1,2,3,4], , true)` 34 | * 35 | * `var3 := ComVar(ComValue(0xb, -1))` 36 | * @param vType Variant's type, VT_VARIANT(default) 37 | * @param convert Convert AHK's array to ComObjArray 38 | */ 39 | static Call(vVal := 0, vType := 0xC, convert := false) { 40 | static size := 8 + 2 * A_PtrSize 41 | if vVal is ComVar 42 | return vVal 43 | var := super(size, 0), IsObject(vVal) && vType := 0xC 44 | var.ref := ref := ComValue(0x4000 | vType, var.Ptr + (vType = 0xC ? 0 : 8)) 45 | if convert && (vVal is Array) { 46 | switch Type(vVal[1]) { 47 | case "Integer": vType := 3 48 | case "String": vType := 8 49 | case "Float": vType := 5 50 | case "ComValue", "ComObject": vType := ComObjType(vVal[1]) 51 | default: vType := 0xC 52 | } 53 | ComObjFlags(ref[] := obj := ComObjArray(vType, vVal.Length), i := -1) 54 | for v in vVal 55 | obj[++i] := v 56 | } else ref[] := vVal 57 | if vType & 0xC 58 | var.IsVariant := 1 59 | return var 60 | } 61 | __Delete() => DllCall("oleaut32\VariantClear", "ptr", this) 62 | __Item { 63 | get => this.ref[] 64 | set => this.ref[] := Value 65 | } 66 | Type { 67 | get => NumGet(this, "ushort") 68 | set { 69 | if (!this.IsVariant) 70 | throw PropertyError("VarType is not VT_VARIANT, Type is read-only.", -2) 71 | NumPut("ushort", Value, this) 72 | } 73 | } 74 | static Prototype.IsVariant := 0 75 | static Prototype.ref := 0 76 | } -------------------------------------------------------------------------------- /Lib/Promise.ahk: -------------------------------------------------------------------------------- 1 | ;/////////////////////////////////////////////////////////////////////////////////////////// 2 | ; MIT License 3 | ; 4 | ; Copyright (c) 2023 thqby 5 | ; 6 | ; Permission is hereby granted, free of charge, to any person obtaining a copy 7 | ; of this software and associated documentation files (the "Software"), to deal 8 | ; in the Software without restriction, including without limitation the rights 9 | ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | ; copies of the Software, and to permit persons to whom the Software is 11 | ; furnished to do so, subject to the following conditions: 12 | ; 13 | ; The above copyright notice and this permission notice shall be included in all 14 | ; copies or substantial portions of the Software. 15 | ; 16 | ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | ; SOFTWARE. 23 | ;/////////////////////////////////////////////////////////////////////////////////////////// 24 | 25 | /************************************************************************ 26 | * @description Implements a javascript-like Promise 27 | * @author thqby 28 | * @date 2025/01/09 29 | * @version 1.0.10 30 | ***********************************************************************/ 31 | 32 | /** 33 | * Represents the completion of an asynchronous operation 34 | * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise MDN doc} 35 | * @alias Promise 36 | */ 37 | class Promise { 38 | static Prototype.status := 'pending' 39 | /** @type {T} */ 40 | static Prototype.result := '' 41 | static Prototype.thrown := false 42 | 43 | /** 44 | * @param {(resolve [,reject]) => void} executor A callback used to initialize the promise. This callback is passed two arguments: 45 | * a resolve callback used to resolve the promise with a value or the result of another promise, 46 | * and a reject callback used to reject the promise with a provided reason or error. 47 | * - resolve(data) => void 48 | * - reject(err) => void 49 | */ 50 | __New(executor) { 51 | this.callbacks := [] 52 | try 53 | (executor.MaxParams = 1) ? executor(resolve) : executor(resolve, reject) 54 | catch Any as e 55 | reject(e) 56 | resolve(value := '') { 57 | if value is Promise { 58 | if !ObjHasOwnProp(value, 'status') { 59 | if this !== value 60 | return value.onCompleted(resolve) 61 | this.status := 'rejected', this.result := ValueError('Chaining cycle detected for promise', -1) 62 | } else if this 63 | this.status := value.status, this.result := value.result 64 | else return 65 | } else if this 66 | this.status := 'fulfilled', this.result := value 67 | else return 68 | SetTimer(task.Bind(this), -1), this := 0 69 | } 70 | reject(reason?) { 71 | if !this 72 | return 73 | this.status := 'rejected', this.result := reason ?? Error(, -1) 74 | SetTimer(task.Bind(this), -1), this := 0 75 | } 76 | static task(this) { 77 | for cb in this.DeleteProp('callbacks') 78 | cb(this) 79 | else if !ObjHasOwnProp(this, 'thrown') && this.status == 'rejected' && this.thrown := true 80 | throw this.result 81 | } 82 | } 83 | ; __Delete() => OutputDebug('del: ' ObjPtr(this) '`n') 84 | 85 | /** 86 | * Attaches a callback that is invoked when the Promise is completed (fulfilled or rejected). 87 | * @param {(value: Promise) => void} callback The callback to execute when the Promise is completed. 88 | * @returns {void} 89 | */ 90 | onCompleted(callback) { 91 | ObjHasOwnProp(this, 'callbacks') ? this.callbacks.Push(callback) : nextTick(this, callback) 92 | static nextTick(this, callback) => SetTimer(() => callback(this), -1) 93 | } 94 | /** 95 | * Attaches callbacks for the resolution and/or rejection of the Promise. 96 | * @param {(value) => void} onfulfilled The callback to execute when the Promise is resolved. 97 | * @param {(reason) => void} onrejected The callback to execute when the Promise is rejected. 98 | * @returns {void} 99 | */ 100 | onSettled(onfulfilled, onrejected := Promise.throw) { 101 | this.onCompleted(val => (val.status == 'fulfilled' ? onfulfilled : onrejected)(val.result)) 102 | } 103 | /** 104 | * Attaches callbacks for the resolution and/or rejection of the Promise. 105 | * @param {(value) => Any} onfulfilled The callback to execute when the Promise is resolved. 106 | * @param {(reason) => Any} onrejected The callback to execute when the Promise is rejected. 107 | * @returns {Promise} A Promise for the completion of which ever callback is executed. 108 | */ 109 | then(onfulfilled, onrejected := Promise.throw) { 110 | return Promise(executor) 111 | executor(resolve, reject) { 112 | this.onCompleted(task) 113 | task(p1) { 114 | try 115 | resolve((p1.status == 'fulfilled' ? onfulfilled : onrejected)(p1.result)) 116 | catch Any as e 117 | reject(e) 118 | } 119 | } 120 | } 121 | /** 122 | * Attaches a callback for only the rejection of the Promise. 123 | * @param {(reason) => Any} onrejected The callback to execute when the Promise is rejected. 124 | * @returns {Promise} A Promise for the completion of the callback. 125 | */ 126 | catch(onrejected) => this.then(val => val, onrejected) 127 | /** 128 | * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). 129 | * The resolved value cannot be modified from the callback. 130 | * @param {() => void} onfinally The callback to execute when the Promise is settled (fulfilled or rejected). 131 | * @returns {Promise} A Promise for the completion of the callback. 132 | */ 133 | finally(onfinally) => this.then( 134 | val => (onfinally(), val), 135 | err => (onfinally(), (Promise.throw)(err)) 136 | ) 137 | /** 138 | * Waits for a promise to be completed. 139 | * @returns {T} 140 | */ 141 | await2(timeout := -1) { 142 | end := A_TickCount + timeout, old := Critical(0) 143 | while (pending := !ObjHasOwnProp(this, 'status')) && (timeout < 0 || A_TickCount < end) 144 | Sleep(1) 145 | Critical(old) 146 | if !pending && this.status == 'fulfilled' 147 | return this.result 148 | throw pending ? TimeoutError() : (this.thrown := true) && this.result 149 | } 150 | /** 151 | * Waits for a promise to be completed. 152 | * Wake up only when a system event or timeout occurs, which takes up less cpu time. 153 | * @returns {T} 154 | */ 155 | await(timeout := -1) { 156 | static hEvent := DllCall('CreateEvent', 'ptr', 0, 'int', 1, 'int', 0, 'ptr', 0, 'ptr') 157 | static __del := { Ptr: hEvent, __Delete: this => DllCall('CloseHandle', 'ptr', this) } 158 | static msg := Buffer(4 * A_PtrSize + 16) 159 | t := A_TickCount, r := 258, old := Critical(0) 160 | while (pending := !ObjHasOwnProp(this, 'status')) && timeout && 161 | (DllCall('PeekMessage', 'ptr', msg, 'ptr', 0, 'uint', 0, 'uint', 0, 'uint', 0) || 162 | 1 == r := DllCall('MsgWaitForMultipleObjects', 'uint', 1, 'ptr*', hEvent, 163 | 'int', 0, 'uint', timeout, 'uint', 7423, 'uint')) 164 | Sleep(-1), (timeout < 0) || timeout := Max(timeout - A_TickCount + t, 0) 165 | Critical(old) 166 | if !pending && this.status == 'fulfilled' 167 | return this.result 168 | throw pending ? r == 0xffffffff ? OSError() : TimeoutError() : (this.thrown := true) && this.result 169 | } 170 | static throw() { 171 | throw this 172 | } 173 | /** 174 | * Creates a new resolved promise for the provided value. 175 | * @param value The value the promise was resolved. 176 | * @returns {Promise} A new resolved Promise. 177 | */ 178 | static resolve(value) => { base: this.Prototype, result: value, status: 'fulfilled' } 179 | /** 180 | * Creates a new rejected promise for the provided reason. 181 | * @param reason The reason the promise was rejected. 182 | * @returns {Promise} A new rejected Promise. 183 | */ 184 | static reject(reason) => Promise((_, reject) => reject(reason)) 185 | /** 186 | * Creates a Promise that is resolved with an array of results when all of the provided Promises 187 | * resolve, or rejected when any Promise is rejected. 188 | * @param {Array} promises An array of Promises. 189 | * @returns {Promise} A new Promise. 190 | */ 191 | static all(promises) { 192 | return Promise(executor) 193 | executor(resolve, reject) { 194 | res := [], count := res.Length := promises.Length 195 | resolve2 := (index, val) => (res[index] := val, !--count && resolve(res)) 196 | for val in promises { 197 | if val is Promise 198 | val.onSettled(resolve2.Bind(A_Index), reject) 199 | else resolve2(A_Index, val) 200 | } else resolve(res) 201 | } 202 | } 203 | /** 204 | * Creates a Promise that is resolved with an array of results when all 205 | * of the provided Promises resolve or reject. 206 | * @param {Array} promises An array of Promises. 207 | * @returns {Promise>} A new Promise. 208 | */ 209 | static allSettled(promises) { 210 | return Promise(executor) 211 | executor(resolve, reject) { 212 | res := [], count := res.Length := promises.Length 213 | callback := (index, val) => (res[index] := { result: val.result, status: val.status }, !--count && resolve(res)) 214 | for val in promises { 215 | if val is Promise 216 | val.onCompleted(callback.Bind(A_Index)) 217 | else res[A_Index] := { result: val, status: 'fulfilled' }, !--count && resolve(res) 218 | } else resolve(res) 219 | } 220 | } 221 | /** 222 | * The any function returns a promise that is fulfilled by the first given promise to be fulfilled, or rejected with an AggregateError containing an array of rejection reasons if all of the given promises are rejected. It resolves all elements of the passed iterable to promises as it runs this algorithm. 223 | * @param {Array} promises An array of Promises. 224 | * @returns {Promise} A new Promise. 225 | */ 226 | static any(promises) { 227 | return Promise(executor) 228 | executor(resolve, reject) { 229 | errs := [], count := errs.Length := promises.Length 230 | reject2 := (index, err) => (errs[index] := err, !--count && ( 231 | err := Error('All promises were rejected'), err.errors := errs, reject(err))) 232 | for val in promises 233 | val.onSettled(resolve, reject2.Bind(A_Index)) 234 | } 235 | } 236 | /** 237 | * Creates a Promise that is resolved or rejected when any of the provided Promises are resolved or rejected. 238 | * @param {Array} promises An array of Promises. 239 | * @returns {Promise} A new Promise. 240 | */ 241 | static race(promises) { 242 | return Promise(executor) 243 | executor(resolve, reject) { 244 | for val in promises 245 | if val is Promise 246 | val.onSettled(resolve, reject) 247 | else return resolve(val) 248 | } 249 | } 250 | static try(fn) { 251 | try { 252 | val := fn() 253 | return Promise.resolve(val) 254 | } catch Any as e 255 | return Promise.reject(e) 256 | } 257 | /** 258 | * Creates a new Promise and returns it in an object, along with its resolve and reject functions. 259 | * @returns {{ promise: Promise, resolve: (data) => void, reject: (err) => void }} 260 | */ 261 | static withResolvers() { 262 | local resolvers := 0 263 | resolvers.promise := Promise((resolve, reject) => resolvers := { resolve: resolve, reject: reject }) 264 | return resolvers 265 | } 266 | } -------------------------------------------------------------------------------- /Lib/WebViewToo.ahk: -------------------------------------------------------------------------------- 1 | ;/////////////////////////////////////////////////////////////////////////////////////////// 2 | ; WebViewToo.ahk v1.0.1-git 3 | ; Copyright (c) 2025 Ryan Dingman (known also as Panaku, The-CoDingman) 4 | ; https://github.com/The-CoDingman/WebViewToo 5 | ; 6 | ; MIT License 7 | ; 8 | ; Permission is hereby granted, free of charge, to any person obtaining a copy 9 | ; of this software and associated documentation files (the "Software"), to deal 10 | ; in the Software without restriction, including without limitation the rights 11 | ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | ; copies of the Software, and to permit persons to whom the Software is 13 | ; furnished to do so, subject to the following conditions: 14 | ; 15 | ; The above copyright notice and this permission notice shall be included in all 16 | ; copies or substantial portions of the Software. 17 | ; 18 | ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | ; SOFTWARE. 25 | ;/////////////////////////////////////////////////////////////////////////////////////////// 26 | 27 | #Requires AutoHotkey v2 28 | #Include WebView2.ahk 29 | 30 | class WebViewGui extends Gui { 31 | /** 32 | * Creates a new Gui with a WebViewCtrl and necessary custom handling attached. 33 | * @param Options AlwaysOnTop Border Caption Disabled -DPIScale LastFound 34 | * MaximizeBox MinimizeBox MinSize600x600 MaxSize800x800 Resize 35 | * OwnDialogs '+Owner' OtherGui.hwnd +Parent 36 | * SysMenu Theme ToolWindow 37 | * @param Title The window title. If omitted, it defaults to the current value of A_ScriptName. 38 | * @param EventObj OnEvent, OnNotify and OnCommand can be used to register methods of EventObj to be called when an event is raised 39 | * @param {Object} WebViewSettings May contain a CreatedEnvironment, DataDir, EdgeRuntime, Options, or DllPath 40 | * @returns {WebViewGui} 41 | */ 42 | __New(Options?, Title?, EventObj?, WebViewSettings := {}) { 43 | super.__New(Options?, Title?, EventObj?) 44 | DefaultWidth := WebViewSettings.HasProp("DefaultWidth") ? WebViewSettings.DefaultWidth : 640 45 | DefaultHeight := WebViewSettings.HasProp("DefaultHeight") ? WebViewSettings.DefaultHeight : 480 46 | /** @type {WebViewCtrl} */ 47 | this.Control := WebViewCtrl(this, "w" DefaultWidth " h" DefaultHeight " vWebViewCtrl", WebViewSettings?) 48 | this.Control.IsNonClientRegionSupportEnabled := True 49 | this.Control.wv.AddHostObjectToScript("gui", { 50 | __Call: ((Hwnd, Th, Name, Q) => GuiFromHwnd(Hwnd).%Name%(Q*)).Bind(this.Hwnd) 51 | }) 52 | this.Sizers := WebViewSizer("-Caption +Resize +Parent" this.Hwnd) 53 | this.OnEvent("Size", this.Size) 54 | for Prop in this.Control.OwnProps() { 55 | if (!this.HasProp(Prop)) { 56 | this.DefineProp(Prop, this.Control.GetOwnPropDesc(Prop)) 57 | } 58 | } 59 | DllCall("Dwmapi.dll\DwmSetWindowAttribute", "Ptr", this.Hwnd, "UInt", DWMWA_WINDOW_CORNER_PREFERENCE := 33, "Ptr*", pvAttribute := 2, "UInt", 4) 60 | this.Move(,, DefaultWidth, DefaultHeight) ;Sets an initial size that is somewhat reasonable 61 | this.Control.wvc.Fill() ;Fill the window after setting initial size 62 | WebViewSizer.ToggleSizer(this) ;Toggle Sizers 63 | return this 64 | } 65 | 66 | LastMinMax := "" 67 | Size(MinMax, Width, Height) { 68 | ; Resize the WebView2 to fit the GUI 69 | this.Control.Move(0, 0, Width, Height) 70 | 71 | ;Resize the sizing handles to fit the GUI 72 | this.Sizers.Move(0, 0, Width, Height) 73 | 74 | if (MinMax == this.LastMinMax) { 75 | return 76 | } 77 | this.LastMinMax := MinMax 78 | 79 | ; When not visible, WebView2 stops rendering reducing its CPU load. When 80 | ; added to a hidden window, like we do in this class, the WebView2 is 81 | ; created non-visible by default and must be made visible before it will 82 | ; appear. This handler satisfies both situations. 83 | this.control.wvc.IsVisible := MinMax != -1 84 | 85 | if (MinMax == 1) { ; -1, 0, 1 86 | try this.Control.ExecuteScriptAsync("document.body.classList.add('ahk-maximized')") 87 | this.Sizers.Hide() ;Always hide the Sizers if the window is maximized 88 | } else { 89 | try this.Control.ExecuteScriptAsync("document.body.classList.remove('ahk-maximized')") 90 | WebViewSizer.ToggleSizer(this) ;Check if Sizers should be displayed or not 91 | } 92 | } 93 | 94 | __Delete() { 95 | ; Placeholder 96 | } 97 | 98 | ;------------------------------------------------------------------------------------------- 99 | ;Default GUI Overrides 100 | /** @throws {Error} Not applicable for a WebViewGui. */ 101 | NotApplicableError(Msg := "") { 102 | throw Error("Not applicable for a WebViewGui. " Msg, -2) 103 | } 104 | 105 | /** @throws {Error} Not applicable for a WebViewGui. */ 106 | Add(ControlType := "", Options := "", Value := "") => this.NotApplicableError("Did you mean AddRoute()?") 107 | 108 | /** @throws {Error} Not applicable for a WebViewGui. */ 109 | AddActiveX(Options := "", Value := "") => this.NotApplicableError() 110 | 111 | /** @throws {Error} Not applicable for a WebViewGui. */ 112 | AddButton(Options := "", Value := "") => this.NotApplicableError() 113 | 114 | /** @throws {Error} Not applicable for a WebViewGui. */ 115 | AddCheckbox(Options := "", Value := "") => this.NotApplicableError() 116 | 117 | /** @throws {Error} Not applicable for a WebViewGui. */ 118 | AddComboBox(Options := "", Value := "") => this.NotApplicableError() 119 | 120 | /** @throws {Error} Not applicable for a WebViewGui. */ 121 | AddCustom(Options := "", Value := "") => this.NotApplicableError() 122 | 123 | /** @throws {Error} Not applicable for a WebViewGui. */ 124 | AddDateTime(Options := "", Value := "") => this.NotApplicableError() 125 | 126 | /** @throws {Error} Not applicable for a WebViewGui. */ 127 | AddDropDownList(Options := "", Value := "") => this.NotApplicableError() 128 | 129 | /** @throws {Error} Not applicable for a WebViewGui. */ 130 | AddDDL(Options := "", Value := "") => this.NotApplicableError() 131 | 132 | /** @throws {Error} Not applicable for a WebViewGui. */ 133 | AddEdit(Options := "", Value := "") => this.NotApplicableError() 134 | 135 | /** @throws {Error} Not applicable for a WebViewGui. */ 136 | AddGroupBox(Options := "", Value := "") => this.NotApplicableError() 137 | 138 | /** @throws {Error} Not applicable for a WebViewGui. */ 139 | AddHotkey(Options := "", Value := "") => this.NotApplicableError() 140 | 141 | /** @throws {Error} Not applicable for a WebViewGui. */ 142 | AddLink(Options := "", Value := "") => this.NotApplicableError() 143 | 144 | /** @throws {Error} Not applicable for a WebViewGui. */ 145 | AddListBox(Options := "", Value := "") => this.NotApplicableError() 146 | 147 | /** @throws {Error} Not applicable for a WebViewGui. */ 148 | AddListView(Options := "", Value := "") => this.NotApplicableError() 149 | 150 | /** @throws {Error} Not applicable for a WebViewGui. */ 151 | AddMonthCal(Options := "", Value := "") => this.NotApplicableError() 152 | 153 | /** @throws {Error} Not applicable for a WebViewGui. */ 154 | AddPicture(Options := "", Value := "") => this.NotApplicableError() 155 | 156 | /** @throws {Error} Not applicable for a WebViewGui. */ 157 | AddPic(Options := "", Value := "") => this.NotApplicableError() 158 | 159 | /** @throws {Error} Not applicable for a WebViewGui. */ 160 | AddProgress(Options := "", Value := "") => this.NotApplicableError() 161 | 162 | /** @throws {Error} Not applicable for a WebViewGui. */ 163 | AddRadio(Options := "", Value := "") => this.NotApplicableError() 164 | 165 | /** @throws {Error} Not applicable for a WebViewGui. */ 166 | AddSlider(Options := "", Value := "") => this.NotApplicableError() 167 | 168 | /** @throws {Error} Not applicable for a WebViewGui. */ 169 | AddStatusBar(Options := "", Value := "") => this.NotApplicableError() 170 | 171 | /** @throws {Error} Not applicable for a WebViewGui. */ 172 | AddTab(Options := "", Value := "") => this.NotApplicableError() 173 | 174 | /** @throws {Error} Not applicable for a WebViewGui. */ 175 | AddTab2(Options := "", Value := "") => this.NotApplicableError() 176 | 177 | /** @throws {Error} Not applicable for a WebViewGui. */ 178 | AddTab3(Options := "", Value := "") => this.NotApplicableError() 179 | 180 | /** @throws {Error} Not applicable for a WebViewGui. */ 181 | AddText(Options := "", Value := "") => this.NotApplicableError("Did you mean AddTextRoute()?") 182 | 183 | /** @throws {Error} Not applicable for a WebViewGui. */ 184 | AddTreeView(Options := "", Value := "") => this.NotApplicableError() 185 | 186 | /** @throws {Error} Not applicable for a WebViewGui. */ 187 | AddUpDown(Options := "", Value := "") => this.NotApplicableError() 188 | 189 | /** Close WebView2 instance and delete the window. */ 190 | Destroy() { 191 | this.Sizers.Destroy() 192 | this.Sizers := 0 193 | Super.Destroy() 194 | } 195 | 196 | /** @throws {Error} Not applicable for a WebViewGui. */ 197 | SetFont(Options := "", FontName := "") => this.NotApplicableError() 198 | 199 | /** 200 | * Display window. It can also minimize, maximize or move the window. 201 | * @param Options (Optional Parameter) Positioning: Xn Yn Wn Hn Center xCenter yCenter AutoSize 202 | * Minimize Maximize Restore NoActivate NA Hide 203 | */ 204 | Show(Options := "") { 205 | if (!((Style := WinGetStyle(this.Hwnd)) & 0x00800000)) { 206 | this.GetClientPos(&gX, &gY, &gWidth, &gHeight) 207 | Width := RegExMatch(Options, "w\s*\K\d+", &Match) ? Match[] : gWidth 208 | Height := RegExMatch(Options, "h\s*\K\d+", &Match) ? Match[] : gHeight 209 | 210 | Rect := Buffer(16, 0) 211 | DllCall("AdjustWindowRectEx", 212 | "Ptr", Rect, ; LPRECT lpRect 213 | "UInt", Style, ; DWORD dwStyle 214 | "UInt", 0, ; BOOL bMenu 215 | "UInt", 0, ; DWORD dwExStyle 216 | "UInt" ; BOOL 217 | ) 218 | Options .= " w" Width += (NumGet(Rect, 0, "Int") - NumGet(Rect, 8, "Int")) 219 | Options .= " h" Height += (NumGet(Rect, 4, "Int") - NumGet(Rect, 12, "Int")) 220 | } 221 | 222 | Super.Show(Options) 223 | } 224 | 225 | /** @throws {Error} Not applicable for a WebViewGui. */ 226 | Submit(Hide := true) => this.NotApplicableError() 227 | 228 | /** @throws {Error} Not applicable for a WebViewGui. */ 229 | FocusedCtrl { 230 | get => this.NotApplicableError() 231 | } 232 | 233 | /** @throws {Error} Not applicable for a WebViewGui. */ 234 | MarginX { 235 | get => this.NotApplicableError() 236 | set => this.NotApplicableError() 237 | } 238 | 239 | /** @throws {Error} Not applicable for a WebViewGui. */ 240 | MarginY { 241 | get => this.NotApplicableError() 242 | set => this.NotApplicableError() 243 | } 244 | } 245 | 246 | class WebViewSizer extends Gui { 247 | /** 248 | * Helper class for adding sizing handles to a caption-free WebViewGui 249 | */ 250 | static __New() { 251 | OnMessage(0x0024, (Params*) => WebViewSizer.WM_GETMINMAXINFO(Params*)) 252 | OnMessage(0x0083, (Params*) => WebViewSizer.WM_NCCALCSIZE(Params*)) 253 | OnMessage(0x00A1, (Params*) => WebViewSizer.WM_NCLBUTTONDOWN(Params*)) 254 | OnMessage(0x007D, (Params*) => WebViewSizer.WM_STYLECHANGED(Params*)) 255 | } 256 | 257 | /** Tests if the cursor intersects with the sizing handles */ 258 | static HitTest(lParam, Hwnd, &X?, &Y?) { 259 | static BorderSize := 29 260 | X := lParam << 48 >> 48, Y := lParam << 32 >> 48 261 | WinGetPos &gX, &gY, &gW, &gH, Hwnd 262 | Hit := (X < gX + BorderSize && 1) + (X >= gX + gW - BorderSize && 2) 263 | + (Y < gY + BorderSize && 3) + (Y >= gy + gH - BorderSize && 6) 264 | return Hit ? Hit + 9 : "" 265 | } 266 | 267 | /** 268 | * Ensures the borderless window does not turn into a borderless 269 | * fullscreen window 270 | */ 271 | static WM_GETMINMAXINFO(wParam, lParam, Msg, Hwnd) { 272 | if (!((CurrGui := GuiFromHwnd(Hwnd)) is WebViewGui) || (WinGetStyle(Hwnd) & 0x00800000)) { 273 | return 274 | } 275 | 276 | if (ParentHwnd := DllCall("GetParent", "Int", CurrGui.Hwnd)) { 277 | ;If window has a parent, use it's parent's size 278 | WinGetPos(,, &ParentWidth, &ParentHeight, ParentHwnd) 279 | MaximizedXPos := 0, MaximizedYPos := 0, MaximizedWidth := ParentWidth, MaximizedHeight := ParentHeight 280 | } else { 281 | ;If window does not have a parent, use it's monitor's size 282 | MonitorInfo := Buffer(40), NumPut("UInt", MonitorInfo.Size, MonitorInfo) 283 | hMonitor := DllCall("MonitorFromWindow", "UInt", Hwnd, "UInt", Mode := 2) 284 | DllCall("GetMonitorInfo", "Ptr", hMonitor, "Ptr", MonitorInfo) 285 | MonitorLeft := NumGet(MonitorInfo, 4, "Int"), MonitorTop := NumGet(MonitorInfo, 8, "Int") 286 | MonitorRight := NumGet(MonitorInfo, 12, "Int"), MonitorBottom := NumGet(MonitorInfo, 16, "Int") 287 | MonitorWorkLeft := NumGet(MonitorInfo, 20, "Int"), MonitorWorkTop := NumGet(MonitorInfo, 24, "Int") 288 | MonitorWorkRight := NumGet(MonitorInfo, 28, "Int"), MonitorWorkBottom := NumGet(MonitorInfo, 32, "Int") 289 | MaximizedWidth := MonitorWorkRight - MonitorLeft, MaximizedHeight := MonitorWorkBottom - MonitorTop 290 | MaximizedXPos := MonitorWorkLeft - MonitorLeft, MaximizedYPos := MonitorWorkTop - MonitorTop 291 | } 292 | 293 | NumPut( 294 | "Int", MaximizedWidth, ; Maximized Width 295 | "Int", MaximizedHeight, ; Maximized Height 296 | "Int", MaximizedXPos, ; Maximized xPos 297 | "Int", MaximizedYPos, ; Maximized yPos 298 | lParam, 8 299 | ) 300 | return 301 | } 302 | 303 | /** Redirects sizing area clicks to sizer's associated parent GUI */ 304 | static WM_NCLBUTTONDOWN(wParam, lParam, Msg, Hwnd) { 305 | if (!(GuiFromHwnd(Hwnd) is WebViewSizer)) { 306 | return 307 | } 308 | 309 | if (Hit := this.HitTest(lParam, Parent := DllCall("GetParent", "Ptr", Hwnd, "Ptr"), &X, &Y)) { 310 | Buf := Buffer(4), NumPut("Short", X, "Short", Y, Buf) 311 | PostMessage(0x00A1, Hit, Buf, Parent) 312 | return 0 313 | } 314 | } 315 | 316 | /** Hides or shows sizers in sync with parent GUI style */ 317 | static WM_STYLECHANGED(wParam, lParam, Msg, Hwnd) { 318 | if (!((CurrGui := GuiFromHwnd(Hwnd)) is WebViewGui)) { 319 | return 320 | } 321 | 322 | WebViewSizer.ToggleSizer(CurrGui) 323 | } 324 | 325 | /** 326 | * Checks the Parent GUI for WM_SIZEBOX and WM_BORDER styles 327 | * and toggles the Parent's Sizers' visibility as needed. 328 | * 329 | * @param Parent Parent GUI of a intialized Sizer 330 | */ 331 | static ToggleSizer(Parent) { 332 | if (!(Parent is WebViewGui)) { 333 | return 334 | } 335 | 336 | if (((Style := WinGetStyle(Parent)) & 0x00040000) && !(Style & 0x00800000)) { 337 | Parent.Sizers.Show() 338 | } else { 339 | Parent.Sizers.Hide() 340 | } 341 | } 342 | 343 | /** 344 | * When a GUI has -Caption and +Resize, it normally shows a wonky looking 345 | * default sizing border. This handler recalculates the window size to 346 | * render the client area over top of where that sizing border would 347 | * normally be, so that it is hidden. 348 | */ 349 | static WM_NCCALCSIZE(wParam, lParam, Msg, Hwnd) { 350 | if (!((CurrGui := GuiFromHwnd(Hwnd)) is WebViewGui) || (WinGetStyle(Hwnd) & 0x00800000)) { 351 | return 352 | } 353 | 354 | return 0 355 | } 356 | 357 | __New(p*) { 358 | super.__New(p*) 359 | } 360 | 361 | __Delete() { 362 | ; Placeholder 363 | } 364 | 365 | Move(X, Y, Width, Height) { 366 | ; Adjust the sizing handles to fit the GUI, first punching a big hole 367 | ; in the center for click-through, then resizing it to fit the GUI. 368 | hRgn1 := DllCall("CreateRectRgn", "Int", 0, "Int", 0, "Int", Width, "Int", Height, "Ptr") 369 | hRgn2 := DllCall("CreateRectRgn", "Int", 6, "Int", 6, "Int", Width - 6, "Int", Height - 6, "Ptr") 370 | DllCall("CombineRgn", "Ptr", hRgn1, "Ptr", hRgn1, "Ptr", hRgn2, "Int", RGN_DIFF := 4) 371 | DllCall("SetWindowRgn", "Ptr", this.Hwnd, "Ptr", hRgn1, "Int", true) 372 | 373 | DllCall("SetWindowPos", 374 | "Ptr", this.Hwnd, "Ptr", 0, 375 | "Int", 0, "Int", 0, "Int", Width, "Int", Height, 376 | "UInt", 0x4210 ; SWP_ASYNCWINDOWPOS | SWP_NOACTIVATE | SWP_NOOWNERZORDER 377 | ) 378 | } 379 | } 380 | 381 | class WebViewCtrl extends Gui.Custom { 382 | /** 383 | * Creates a WebControl instance around a Gui.Custom control 384 | * @param Target The Gui you want to attach the control to 385 | * @param {String} Options Control options such as width, height, vName 386 | * @param {Object} WebViewSettings May contain a CreatedEnvironment, DataDir, EdgeRuntime, Options, or DllPath 387 | * @returns {WebViewCtrl} 388 | */ 389 | static Call(Target, Options := "", WebViewSettings := {}) { 390 | Container := Gui.Prototype.AddCustom.Call(Target, "ClassStatic " Options) 391 | for Prop in this.Prototype.OwnProps() { 392 | Container.DefineProp(Prop, this.Prototype.GetOwnPropDesc(Prop)) 393 | } 394 | Container.__Init(), Container.__New(WebViewSettings?) 395 | return Container 396 | } 397 | 398 | static __New() { 399 | OnExit((*) => WebViewCtrl.CloseAllWebViewCtrls()) 400 | } 401 | 402 | static Template := {} 403 | static Template.Framework := " 404 | ( 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 |
{1}
414 | 415 | 416 | 417 | )" 418 | 419 | static Template.Css := "html, body {width: 100%; height: 100%;margin: 0; padding: 0;font-family: sans-serif;} body {display: flex;flex-direction: column;} .main {flex-grow: 1;overflow: hidden;}" 420 | static Template.Name := "Template.html" 421 | static Template.Html := "
The documentation for WebViewToo is currently being reworked. Sorry for the inconvenience.
" 422 | static Template.JavaScript := "" 423 | 424 | static UniqueId => WebViewCtrl.CreateUniqueID() 425 | static CreateUniqueId() { 426 | SplitPath(A_ScriptName,,,, &OutNameNoExt) 427 | Loop Parse, OutNameNoExt { 428 | Id .= Mod(A_Index, 3) ? Format("{:X}", Ord(A_LoopField)) : "-" Format("{:X}", Ord(A_LoopField)) 429 | } 430 | return RTrim(StrLower(Id), "-") 431 | } 432 | static TempDir := A_Temp "\" WebViewCtrl.UniqueId 433 | 434 | static ActiveHwnds := Map() 435 | 436 | __New(WebViewSettings?) { 437 | DllPath := WebViewSettings.HasProp("DllPath") ? WebViewSettings.DllPath : "WebView2Loader.dll" 438 | DataDir := WebViewSettings.HasProp("DataDir") ? WebViewSettings.DataDir : "" 439 | Options := WebViewSettings.HasProp("Options") ? WebViewSettings.Options : 0 440 | EdgeRuntime := WebViewSettings.HasProp("EdgeRuntime") ? WebViewSettings.EdgeRuntime : "" 441 | CreatedEnvironment := WebViewSettings.HasProp("CreatedEnvironment") ? WebViewSettings.CreatedEnvironment : 0 442 | Html := WebViewSettings.HasProp("Html") ? WebViewSettings.Html : WebViewCtrl.Template.Html 443 | Css := WebViewSettings.HasProp("Css") ? WebViewSettings.Css : WebViewCtrl.Template.Css 444 | JavaScript := WebViewSettings.HasProp("JavaScript") ? WebViewSettings.JavaScript : WebViewCtrl.Template.JavaScript 445 | Url := WebViewSettings.HasProp("Url") ? WebViewSettings.Url : "" 446 | 447 | this.wvc := WebView2.Create(this.Hwnd,, CreatedEnvironment, DataDir, EdgeRuntime, Options, DllPath) 448 | this.wv := this.wvc.CoreWebView2 449 | WebViewCtrl.ActiveHwnds[this.Hwnd] := this.wvc 450 | this.wv.InjectAhkComponent().await() 451 | this.wvc.IsVisible := 1 452 | if (A_IsCompiled) { 453 | this.BrowseExe() 454 | } else { 455 | this.BrowseFolder(A_WorkingDir) 456 | } 457 | 458 | this.wv.add_NavigationStarting(InstallGlobal) 459 | InstallGlobal(ICoreWebView2, Args) { 460 | static Proxy := { __Get: (this, Name, *) => %Name% } 461 | Host := WebViewCtrl.ParseUri(Args.Uri).Host 462 | if (Host ~= "i)\.localhost$" || this._AllowGlobalHosts.Has(Host)) { 463 | try ICoreWebView2.AddHostObjectToScript("global", Proxy) 464 | } else { 465 | try ICoreWebView2.RemoveHostObjectFromScript("global") 466 | } 467 | } 468 | 469 | ; Add the request router 470 | this.wv.add_WebResourceRequested (p*) => this._Router(p*) 471 | if (Url) { 472 | this.Navigate(Url) 473 | } else { 474 | this.NavigateToString(Format(WebViewCtrl.Template.Framework, Html, Css, JavaScript)) 475 | } 476 | return this 477 | } 478 | 479 | ;This never seems to be called 480 | __Delete() { 481 | ; Placeholder 482 | } 483 | 484 | ;------------------------------------------------------------------------------------------- 485 | ;Custom Uri Routing 486 | 487 | _DefaultHost := "ahk.localhost" 488 | 489 | /** 490 | * Adds folder access into the WebView2 environment under the given host 491 | * name. Host names provided here should end with `.localhost` for best 492 | * performance. 493 | * 494 | * Folders added by this method cannot easily be used in compiled scripts. 495 | * 496 | * @param Path The path to the folder to add 497 | * @param Host The host name to add the folder under, e.g. `ahk.localhost` 498 | * 499 | */ 500 | BrowseFolder(Path, Host := this._DefaultHost) { 501 | this.wv.SetVirtualHostNameToFolderMapping(Host, NormalizePath(Path), WebView2.HOST_RESOURCE_ACCESS_KIND.ALLOW) 502 | 503 | NormalizePath(Path) { 504 | cc := DllCall("GetFullPathName", "str", Path, "uint", 0, "ptr", 0, "ptr", 0, "uint") 505 | buf := Buffer(cc * 2) 506 | DllCall("GetFullPathName", "str", path, "uint", cc, "ptr", buf, "ptr", 0) 507 | return StrGet(buf) 508 | } 509 | } 510 | 511 | /** 512 | * Adds exe resource access into the WebView2 environment under the given 513 | * host name. 514 | * 515 | * @param {String} Path Path to the exe to load resources from 516 | * @param {String} Host Host to make the resources available on 517 | */ 518 | BrowseExe(Path?, Host := this._DefaultHost) { 519 | if (IsSet(Path)) { 520 | throw Error("Not yet supported") 521 | } 522 | 523 | this._CompileRoutesForHost(Host, [['**', (Uri) => WebViewCtrl.ExeRead(Uri.Path)]]) 524 | } 525 | 526 | /** 527 | * Adds access to an individual file. 528 | * 529 | * @param {String} FilePath The path to load the file from 530 | * @param {String} Route The route to make the file available under, if 531 | * different than the name of filePath. 532 | * @param {String} Host The host name to add the file under 533 | */ 534 | AddFileRoute(FilePath, Route?, Host := this._DefaultHost) { 535 | SplitPath(FilePath, &Name) 536 | if (A_IsCompiled) { 537 | this.AddRoute(Route ?? Name, (Uri) => WebViewCtrl.ExeRead(FilePath)) 538 | } else { 539 | this.AddRoute(Route ?? Name, (Uri) => FileRead(FilePath, "RAW")) 540 | } 541 | } 542 | 543 | /** 544 | * Adds a text resource at the specified route 545 | * 546 | * @param {String} Route The route to make the resource available under 547 | * @param {String} Text The text content for the resource 548 | * @param {String} Host The host name to add the file under 549 | */ 550 | AddTextRoute(Route, Text, Host := this._DefaultHost) { 551 | this.AddRoute(Route, Text, Host) 552 | } 553 | 554 | /** 555 | * Adds a resource at the specified route 556 | * 557 | * @param {String} Route The route to make the resource available under 558 | * @param Resource The resource to make available 559 | * @param {String} Host The host name to add the resource under 560 | */ 561 | AddRoute(Route, Resource, Host := this._DefaultHost) { 562 | this._Routes[Host].Dirty := true 563 | this._Routes[Host].InsertAt(1, [Route, Resource]) 564 | if (!this._Routes.Dirty) { 565 | this._Routes.Dirty := true 566 | SetTimer(() => this._SaveUnsavedRoutes(), -1) 567 | } 568 | } 569 | 570 | /** 571 | * Allow pages at the given host to access the `ahk.global` object. 572 | * @param Host The host name to allow access under 573 | */ 574 | AllowGlobalAccessFor(Host := this._DefaultHost) { 575 | this._AllowGlobalHosts[Host] := true 576 | ; TODO: Make change on any active page 577 | } 578 | 579 | /** 580 | * Shows a specified resource in the web view 581 | * 582 | * @param Path The path to the resource, not including any leading slash 583 | */ 584 | Navigate(Path) { 585 | this._SaveUnsavedRoutes() 586 | if (!(Path ~= "i)^[^\/\\:]+:")) { 587 | Path := "https://" this._DefaultHost "/" LTrim(Path, "/\") 588 | } 589 | this.wv.Navigate(Path) 590 | } 591 | 592 | /** List of hosts allowed to access names in AHK's global scope */ 593 | _AllowGlobalHosts := Map() 594 | 595 | /** Map of hosts to route lists */ 596 | _Routes := WebViewCtrl._RouteMap() 597 | class _RouteMap extends Map { 598 | Dirty := false ; Has not been compiled since last change 599 | __Item[Name] => ( 600 | this.Has(Name) || this.Set(Name, WebViewCtrl._RouteList()), 601 | this.Get(Name) 602 | ) 603 | } 604 | 605 | /** 606 | * Contains a list of route objects. Route objects are a two-element array 607 | * pairing a route string and a resource. 608 | */ 609 | class _RouteList extends Array { 610 | Dirty := false ; Has not been compiled since last change 611 | } 612 | 613 | /** Map of hosts to compiled regular expressions */ 614 | _CompiledRoutes := Map() 615 | 616 | /** 617 | * Compiles any routes that have been changed since the last time they 618 | * were compiled 619 | */ 620 | _SaveUnsavedRoutes() { 621 | if (!this._Routes.Dirty) { 622 | return 623 | } 624 | 625 | this._Routes.Dirty := false 626 | for Host, RouteList in this._Routes { 627 | if (!RouteList.Dirty) { 628 | continue 629 | } 630 | RouteList.Dirty := false 631 | this._CompileRoutesForHost(Host, RouteList) 632 | } 633 | } 634 | 635 | /** Compiles the routes for a specified host */ 636 | _CompileRoutesForHost(Host, Routes) { 637 | ; Clear any overriding folder mappings that would prevent custom routing 638 | try this.wv.ClearVirtualHostNameToFolderMapping(Host) 639 | 640 | FullReg := "" 641 | for Route in Routes { 642 | Pattern := "^[\/\\]{0,}(\Q" StrReplace(Route[1], "\E", "\E\\E\Q") "\E)$(?C" A_Index ":Callout)" 643 | Pattern := StrReplace(Pattern, "**", "\E.{0,}?\Q") 644 | Pattern := StrReplace(Pattern, "*", "\E[^\/\\]{0,}?\Q") 645 | FullReg .= "|" Pattern 646 | } 647 | 648 | this._CompiledRoutes[Host] := {Pattern: "S)" SubStr(fullReg, 2), Routes: Routes.Clone()} 649 | 650 | ; Register the router to handle requests made against this domain 651 | this.wv.AddWebResourceRequestedFilter("http://" Host "/*", 0) 652 | this.wv.AddWebResourceRequestedFilter("https://" Host "/*", 0) 653 | } 654 | 655 | /** Connects requests to target resources */ 656 | _Router(ICoreWebView2, Args) { 657 | Parsed := WebViewCtrl.ParseUri(Args.Request.Uri) 658 | Path := Parsed.Path, Host := Parsed.host 659 | 660 | Target := unset 661 | CompiledRoutes := this._CompiledRoutes[Host] 662 | RegExMatch(Path, CompiledRoutes.Pattern) 663 | if (!IsSet(Target)) { 664 | return 665 | } 666 | 667 | if (Target is Object && !(Target is Buffer)) { 668 | try Target := Target(Parsed) 669 | } 670 | 671 | if (Target is Buffer) { 672 | Stream := WebView2.CreateMemStream(Target) 673 | Args.Response := ICoreWebView2.Environment.CreateWebResourceResponse(Stream, 200, "OK", "") 674 | return 675 | } 676 | 677 | if (Target is String) { 678 | Headers := "" 679 | if (Path ~= "i)\.js$") { 680 | Headers .= "Content-Type: text/javascript;" 681 | } 682 | Stream := WebView2.CreateTextStream(Target) 683 | Args.Response := ICoreWebView2.Environment.CreateWebResourceResponse(Stream, 200, "OK", Headers) 684 | return 685 | } 686 | 687 | if (Target is WebView2.Stream) { 688 | Args.Response := ICoreWebView2.Environment.CreateWebResourceResponse(Target, 200, "OK", "") 689 | return 690 | } 691 | 692 | Callout(Match, Num, Pos, Haystack, Needle) { 693 | Target := CompiledRoutes.Routes[Num][2] 694 | return -1 695 | } 696 | } 697 | 698 | ;------------------------------------------------------------------------------------------- 699 | ;Static WebViewCtrl Methods 700 | static CloseAllWebViewCtrls() { 701 | for Hwnd, WebView in this.ActiveHwnds { 702 | try WebView.Close() 703 | } 704 | } 705 | 706 | static ConvertColor(RGB) => (RGB := RGB ~= "^0x" ? RGB : "0x" RGB, (((RGB & 0xFF) << 16) | (RGB & 0xFF00) | (RGB >> 16 & 0xFF)) << 8 | 0xFF) ;Must be a string 707 | 708 | static CreateFileFromResource(ResourceName, DestinationDir := WebViewCtrl.TempDir) { ;Create a file from an installed resource -- works like a dynamic `FileInstall()` 709 | if (!A_IsCompiled) { 710 | return 711 | } 712 | 713 | ResourceName := StrReplace(ResourceName, "/", "\") 714 | SplitPath(ResourceName, &OutFileName, &OutDir, &OutExt) 715 | ResourceType := OutExt = "bmp" || OutExt = "dib" ? 2 : OutExt = "ico" ? 14 : OutExt = "htm" || OutExt = "html" || OutExt = "mht" ? 23 : OutExt = "manifest" ? 24 : 10 716 | Module := DllCall("GetModuleHandle", "Ptr", 0, "Ptr") 717 | Resource := DllCall("FindResource", "Ptr", Module, "Str", ResourceName, "UInt", ResourceType, "Ptr") 718 | ResourceSize := DllCall("SizeofResource", "Ptr", Module, "Ptr", Resource) 719 | ResourceData := DllCall("LoadResource", "Ptr", Module, "Ptr", Resource, "Ptr") 720 | ConvertedData := DllCall("LockResource", "Ptr", ResourceData, "Ptr") 721 | TextData := StrGet(ConvertedData, ResourceSize, "UTF-8") 722 | 723 | if (!DirExist(DestinationDir "\" OutDir)) { 724 | DirCreate(DestinationDir "\" OutDir) 725 | } 726 | 727 | if (FileExist(DestinationDir "\" ResourceName)) { 728 | ExistingFile := FileOpen(DestinationDir "\" ResourceName, "r") 729 | ExistingFile.RawRead(TempBuffer := Buffer(ResourceSize)) 730 | ExistingFile.Close() 731 | if (DllCall("ntdll\memcmp", "Ptr", TempBuffer, "Ptr", ConvertedData, "Ptr", ResourceSize)) { 732 | FileSetAttrib("-R", DestinationDir "\" ResourceName) 733 | FileDelete(DestinationDir "\" ResourceName) 734 | } 735 | } 736 | 737 | if (!FileExist(DestinationDir "\" ResourceName)) { 738 | TempFile := FileOpen(DestinationDir "\" ResourceName, "w") 739 | TempFile.RawWrite(ConvertedData, ResourceSize) 740 | TempFile.Close() 741 | FileSetAttrib("+HR", DestinationDir "\" OutDir) 742 | FileSetAttrib("+HR", DestinationDir "\" ResourceName) 743 | } 744 | } 745 | 746 | static EscapeHtml(Text) => StrReplace(StrReplace(StrReplace(StrReplace(StrReplace(Text, "&", "&"), "<", "<"), ">", ">"), "`"", """), "'", "'") 747 | 748 | static EscapeJavaScript(Text) => StrReplace(StrReplace(StrReplace(Text, '\', '\\'), '"', '\"'), '`n', '\n') 749 | 750 | static ExeRead(ResourcePath) { 751 | ResourcePath := StrReplace(StrUpper(LTrim(StrReplace(ResourcePath, "/", "\"), "\")), "%20", " ") 752 | SplitPath(ResourcePath,,, &OutExt) 753 | ResourceType := (OutExt = "bmp" || OutExt = "dib") ? 2 : (OutExt = "ico") ? 14 : (OutExt = "htm" || OutExt = "html" || OutExt = "mht") ? 23 : (OutExt = "manifest") ? 24 : 10 754 | Module := DllCall("GetModuleHandle", "Ptr", 0, "Ptr") 755 | Resource := DllCall("FindResource", "Ptr", Module, "Str", ResourcePath, "UInt", ResourceType, "Ptr") 756 | if (!Resource) { 757 | return 758 | } 759 | ResourceSize := DllCall("SizeofResource", "Ptr", Module, "Ptr", Resource) 760 | ResourceData := DllCall("LoadResource", "Ptr", Module, "Ptr", Resource, "Ptr") 761 | ConvertedData := DllCall("LockResource", "Ptr", ResourceData, "Ptr") 762 | return WebView2.CreateMemStream(ConvertedData, ResourceSize) 763 | } 764 | 765 | static ForEach(Obj, Parent := "Default", Depth := 0) { 766 | if(!IsObject(Obj) || (Type(Obj) = "ComObject")) { 767 | return 768 | } 769 | 770 | Output := "" 771 | for Key, Value, in Obj.OwnProps() { 772 | try Output .= "`n" Parent " >> " Key 773 | try Output .= ": " Value 774 | try Output .= WebViewCtrl.ForEach(Value, Parent " >> " Key, Depth + 1) 775 | } 776 | for Key, Value in base_props(Obj) { 777 | try Output .= "`n" Parent " >> " Key 778 | try Output .= ": " Value 779 | try Output .= WebViewCtrl.ForEach(Value, Parent " >> " Key, Depth + 1) 780 | } 781 | return Depth ? Output : Trim(Output, "`n") 782 | 783 | base_props(Obj) { 784 | iter := Obj.Base.OwnProps(), iter() ;skip `__Class` 785 | return next 786 | 787 | next(&Key, &Value, *) { 788 | while (iter(&Key)) 789 | ; try if !((Value := Obj.%Key%) is Func) 790 | return true 791 | return false 792 | } 793 | } 794 | } 795 | 796 | static FormatHtml(FormatStr, Values*) { 797 | for Index, Value, in Values { 798 | Values[Index] := WebViewCtrl.EscapeHtml(Value) 799 | } 800 | return Format(FormatStr, Values*) 801 | } 802 | 803 | static ParseUri(Uri) { 804 | static Pattern := "^(?:(?\w+):)?(?://(?:(?[^@]+)@)?(?[^:/?#]+)(?::(?\d+))?)?(?[^?#]*)?(?:\?(?[^#]*))?(?:#(?.*))?$" 805 | if (!RegExMatch(String(Uri), Pattern, &Match)) { 806 | return 807 | } 808 | Parsed := {} 809 | Parsed.Scheme := Match["Scheme"], Parsed.UserInfo := Match["UserInfo"], Parsed.Host := Match["Host"] 810 | Parsed.Port := Match["Port"], Parsed.Path := Match["Path"], Parsed.Query := Match["Query"] 811 | Parsed.Fragment := Match["Fragment"], Parsed.Authority := (Parsed.UserInfo != "" ? Parsed.UserInfo "@" : "") . Parsed.Host . (Parsed.Port != "" ? ":" Parsed.Port : "") 812 | return Parsed 813 | } 814 | 815 | ;------------------------------------------------------------------------------------------- 816 | ;WebViewCtrl class assignments 817 | AddCallbackToScript(CallbackName, Callback) => this.AddHostObjectToScript(CallbackName, Callback.Bind(this)) ;Similar to `AddHostObjectToScript()`, but only registers a callback 818 | RemoveCallbackFromScript(CallbackName) => this.RemoveHostObjectFromScript(CallbackName) ;Removes a registered callback 819 | Debug() { 820 | this.OpenDevToolsWindow() 821 | } 822 | 823 | Move(Params*) => (Super.Move(Params*), this.wvc.Fill()) 824 | 825 | SimplePrintToPdf(FileName := "", Orientation := "Portrait", Timeout := 5000) { 826 | Loop { 827 | FileName := FileSelect("S", tFileName := IsSet(FileName) ? FileName : "",, "*.pdf") 828 | if (FileName = "") { 829 | return CancelMsg() 830 | } 831 | 832 | SplitPath(FileName, &OutFileName, &OutDir, &OutExt) 833 | FileName := OutExt = "" ? FileName ".pdf" : Filename 834 | if (FileExist(FileName)) { 835 | Overwrite := OverwriteMsg() 836 | if (Overwrite = "No") { 837 | continue 838 | } else if (Overwrite = "Cancel") { 839 | return CancelMsg() 840 | } 841 | } 842 | break 843 | } 844 | 845 | Settings := this.Environment.CreatePrintSettings() 846 | Settings.Orientation := Orientation = "Portrait" ? WebView2.PRINT_ORIENTATION.PORTRAIT : WebView2.PRINT_ORIENTATION.LANDSCAPE 847 | PrintPromise := this.PrintToPdfAsync(FileName, Settings) 848 | try PrintPromise.await(Timeout) 849 | if (!PrintPromise.Result) { 850 | ErrorMsg() 851 | } else { 852 | if (MsgBox("Would you like to open this PDF?", "Print to PDF", "262148") = "Yes") { 853 | Run(FileName) 854 | } 855 | } 856 | 857 | ErrorMsg() => MsgBox("An error occurred while attempting to save the file.`n" FileName, "Print to PDF", "262144") 858 | CancelMsg() => MsgBox("Print Canceled", "Print to PDF", "262144") 859 | OverwriteMsg() => MsgBox(OutFileName " already exist.`nWould you like to overwrite it?", "Confirm Save As", "262195") 860 | } 861 | 862 | ;------------------------------------------------------------------------------------------- 863 | ;Controller class assignments 864 | Fill() => this.wvc.Fill() 865 | CoreWebView2 => this.wvc.CoreWebView2 ;Gets the CoreWebView2 associated with this CoreWebView2Controller 866 | 867 | /** 868 | * Returns a boolean representing if the WebView2 instance is visible 869 | */ 870 | IsVisible { ;Boolean => Determines whether to show or hide the WebView 871 | get => this.wvc.IsVisible 872 | set => this.wvc.IsVisible := Value 873 | } 874 | Bounds { ;Rectangle => Gets or sets the WebView bounds 875 | /** 876 | * Returns a Buffer() 877 | * You can extract the X, Y, Width, and Height using NumGet() 878 | * X is at offset 0, Y at offset 4, Width at offset 8, Height at offset 12. 879 | **/ 880 | get => this.wvc.Bounds 881 | 882 | /** 883 | * Value must be a Buffer(16) that you've inserted values into 884 | * using NumPut(). See the above notes regarding the appropriate offsets. 885 | **/ 886 | set => this.wvc.Bounds := Value 887 | } 888 | 889 | Bounds(X?, Y?, Width?, Height?) { ;Get: Object with X, Y, Width, Height properties; Set: 890 | tBounds := this.wvc.Bounds 891 | if (IsSet(X) || IsSet(Y) || IsSet(Width) || IsSet(Height)) { 892 | IsSet(X) ? NumPut("Int", X, tBounds, 0) : 0 893 | IsSet(Y) ? NumPut("Int", Y, tBounds, 4) : 0 894 | IsSet(Width) ? NumPut("Int", Width, tBounds, 8) : 0 895 | IsSet(Height) ? NumPut("Int", Height, tBounds, 12) : 0 896 | this.Bounds := tBounds 897 | } else { 898 | return Bounds := { 899 | X: NumGet(tBounds, 0, "Int"), 900 | Y: NumGet(tBounds, 4, "Int"), 901 | Width: NumGet(tBounds, 8, "Int"), 902 | Height: NumGet(tBounds, 12, "Int") 903 | } 904 | } 905 | } 906 | ZoomFactor { ;Double => Gets or sets the zoom factor for the WebView 907 | get => this.wvc.ZoomFactor 908 | set => this.wvc.ZoomFactor := Value 909 | } 910 | ParentWindow { ;Integer => Gets the parent window provided by the app or sets the parent window that this WebView is using to render content 911 | get => this.wvc.ParentWindow ;Returns the `Hwnd` of the Ctrl this instance is attached to 912 | set => this.wvc.ParentWindow := Value ;Not recommened to use set => because it dettaches the WebView2 window and can break the software 913 | } 914 | DefaultBackgroundColor { ;HexColorCode => Gets or sets the WebView default background color. 915 | get { 916 | BGRA := Format("{:X}", this.wvc.DefaultBackgroundColor) 917 | return SubStr(BGRA, 5, 2) SubStr(BGRA, 3, 2) SubStr(BGRA, 1, 2) 918 | } 919 | set => this.wvc.DefaultBackgroundColor := WebViewCtrl.ConvertColor(Value) 920 | } 921 | 922 | /** 923 | * RasterizationScale, ShouldDetectMonitorScaleChanges, and BoundsMode all work together 924 | * If you want to use set => (RasterizationScale||ShouldDetectMonitorScaleChanges||BoundsMode) 925 | * you will need to turn on DPI Awareness for your script by using the following DllCall 926 | * DllCall("SetThreadDpiAwarenessContext", "ptr", -3, "ptr") ;**NOTE: DpiAwareness Now causes fatal error, good luck** 927 | **/ 928 | RasterizationScale { ;Double => Gets or sets the WebView rasterization scale 929 | get => this.wvc.RasterizationScale 930 | set => this.wvc.RasterizationScale := Value 931 | } 932 | ShouldDetectMonitorScaleChanges { ;Boolean => Determines whether the WebView will detect monitor scale changes 933 | get => this.wvc.ShouldDetectMonitorScaleChanges 934 | set => this.wvc.ShouldDetectMonitorScaleChanges := Value 935 | } 936 | BoundsMode { ;Boolean => Gets or sets the WebView bounds mode 937 | /** 938 | * 0: UseRawPixels; Bounds property represents raw pixels. Physical size of Webview is not impacted by RasterizationScale 939 | * 1: UseRasterizationScale; Bounds property represents logical pixels and the RasterizationScale property is used to get the physical size of the WebView. 940 | **/ 941 | get => this.wvc.BoundsMode 942 | set => this.wvc.BoundsMode := Value 943 | } 944 | AllowExternalDrop { ;Boolean => Gets or sets the WebView allow external drop property 945 | get => this.wvc.AllowExternalDrop 946 | set => this.wvc.AllowExternalDrop := Value 947 | } 948 | SetBoundsAndZoomFactor(Bounds, ZoomFactor) => this.wvc.SetBoundsAndZoomFactor(Bounds, ZoomFactor) ;Updates Bounds and ZoomFactor properties at the same time 949 | 950 | /** 951 | * MoveFocus() 952 | * 1: Next; Specifies that the focus is moved due to Tab traversal forward 953 | * 2: Previous; Specifies that the focus is moved due to Tab traversal backward 954 | * 0: Programmatic; Specifies that the code is setting focus into WebView 955 | **/ 956 | MoveFocus(Reason) => this.wvc.MoveFocus(Reason) ;Moves focus into WebView 957 | 958 | /** 959 | * NotifyParentWindowPositionChanged() 960 | * Notifies the WebView that the parent (or any ancestor) HWND moved 961 | * Example: Calling this method updates dialog windows such as the DownloadDialog 962 | **/ 963 | NotifyParentWindowPositionChanged() => this.wvc.NotifyParentWindowPositionChanged() 964 | 965 | ;------------------------------------------------------------------------------------------- 966 | ;WebView2Core class assignments 967 | Settings => this.wv.Settings ;Returns Map() of Settings 968 | AreBrowserAcceleratorKeysEnabled { ;Boolean => Determines whether browser-specific accelerator keys are enabled 969 | get => this.Settings.AreBrowserAcceleratorKeysEnabled 970 | set => this.Settings.AreBrowserAcceleratorKeysEnabled := Value 971 | } 972 | AreDefaultContextMenusEnabled { ;Boolean => Determines whether the default context menus are shown to the user in WebView 973 | get => this.Settings.AreDefaultContextMenusEnabled 974 | set => this.Settings.AreDefaultContextMenusEnabled := Value 975 | } 976 | AreDefaultScriptDialogsEnabled { ;Boolean => Determines whether WebView renders the default JavaScript dialog box 977 | get => this.Settings.AreDefaultScriptDialogsEnabled 978 | set => this.Settings.AreDefaultScriptDialogsEnabled := Value 979 | } 980 | AreDevToolsEnabled { ;Boolean => Determines whether the user is able to use the context menu or keyboard shortcuts to open the DevTools window 981 | get => this.Settings.AreDevToolsEnabled 982 | set => this.Settings.AreDevToolsEnabled := Value 983 | } 984 | AreHostObjectsAllowed { ;Boolean => Determines whether host objects are accessible from the page in WebView 985 | get => this.Settings.AreHostObjectsAllowed 986 | set => this.Settings.AreHostObjectsAllowed := Value 987 | } 988 | HiddenPdfToolbarItems { ;Integer => Used to customize the PDF toolbar items 989 | /** 990 | * None: 0 991 | * Save: 1 992 | * Print: 2 993 | * SaveAs: 4 994 | * ZoomIn: 8 995 | * ZoomOut: 16 996 | * Rotate: 32 997 | * FitPage: 64 998 | * PageLayout: 128 999 | * Bookmarks: 256 ;This option is broken in the current runtime. See: https://github.com/MicrosoftEdge/WebView2Feedback/issues/2866 1000 | * PageSelector 512 1001 | * Search: 1024 1002 | * FullScreen: 2048 1003 | * MoreSettings: 4096 1004 | * Add up numbers if you want to hide multiple items, Ex: 257 to hide Bookmarks and Save 1005 | **/ 1006 | get => this.Settings.HiddenPdfToolbarItems 1007 | set => this.Settings.HiddenPdfToolbarItems := Value 1008 | } 1009 | IsBuiltInErrorPageEnabled { ;Boolean => Determines whether to disable built in error page for navigation failure and render process failure 1010 | get => this.Settings.IsBuiltInErrorPageEnabled 1011 | set => this.Settings.IsBuiltInErrorPageEnabled := Value 1012 | } 1013 | IsGeneralAutofillEnabled { ;Boolean => Determines whether general form information will be saved and autofilled 1014 | get => this.Settings.IsGeneralAutofillEnabled 1015 | set => this.Settings.IsGeneralAutofillEnabled := Value 1016 | } 1017 | IsNonClientRegionSupportEnabled { ;Boolean => The IsNonClientRegionSupportEnabled property enables web pages to use the app-region CSS style 1018 | get => this.wv.Settings.IsNonClientRegionSupportEnabled 1019 | set => this.wv.Settings.IsNonClientRegionSupportEnabled := Value 1020 | } 1021 | IsPasswordAutosaveEnabled { ;Boolean => Determines whether password information will be autosaved 1022 | get => this.Settings.IsPasswordAutosaveEnabled 1023 | set => this.Settings.IsPasswordAutosaveEnabled := Value 1024 | } 1025 | IsPinchZoomEnabled { ;Boolean => Determines the ability of the end users to use pinching motions on touch input enabled devices to scale the web content in the WebView2 1026 | get => this.Settings.IsPinchZoomEnabled 1027 | set => this.Settings.IsPinchZoomEnabled := Value 1028 | } 1029 | IsReputationCheckingRequired { ;Boolean => Determines whether SmartScreen is enabled when visiting web pages 1030 | get => this.Settings.IsReputationCheckingRequired 1031 | set => this.Settings.IsReputationCheckingRequired := Value 1032 | } 1033 | IsScriptEnabled { ;Boolean => Determines whether running JavaScript is enabled in all future navigations in the WebView 1034 | get => this.Settings.IsScriptEnabled 1035 | set => this.Settings.IsScriptEnabled := Value 1036 | } 1037 | IsStatusBarEnabled { ;Boolean => Determines whether the status bar is displayed 1038 | get => this.Settings.IsStatusBarEnabled 1039 | set => this.Settings.IsStatusBarEnabled := Value 1040 | } 1041 | IsSwipeNavigationEnabled { ;Boolean => Determines whether the end user to use swiping gesture on touch input enabled devices to navigate in WebView2 1042 | get => this.Settings.IsSwipeNavigationEnabled 1043 | set => this.Settings.IsSwipeNavigationEnabled := Value 1044 | } 1045 | IsWebMessageEnabled { ;Boolean => Determines whether communication from the host to the top-level HTML document of the WebView is allowed 1046 | get => this.Settings.IsWebMessageEnabled 1047 | set => this.Settings.IsWebMessageEnabled := Value 1048 | } 1049 | IsZoomControlEnabled { ;Boolean => Determines whether the user is able to impact the zoom of the WebView 1050 | get => this.Settings.IsZoomControlEnabled 1051 | set => this.Settings.IsZoomControlEnabled := Value 1052 | } 1053 | UserAgent { ;String => Determines WebView2's User Agent 1054 | get => this.Settings.UserAgent 1055 | set => this.Settings.UserAgent := Value 1056 | } 1057 | 1058 | Source => this.wv.Source ;Returns Uri of current page 1059 | NavigateToString(HtmlContent) => this.wv.NavigateToString(HtmlContent) ;Navigate to text (essentially create a webpage from a string) 1060 | AddScriptToExecuteOnDocumentCreatedAsync(JavaScript) => this.wv.AddScriptToExecuteOnDocumentCreatedAsync(JavaScript) ;Adds JavaScript to run when the DOM is created 1061 | AddScriptToExecuteOnDocumentCreated(JavaScript) { 1062 | AddScriptToExecuteOnDocumentCreatedPromise := this.wv.AddScriptToExecuteOnDocumentCreatedAsync(JavaScript) 1063 | AddScriptToExecuteOnDocumentCreatedPromise.await() 1064 | return Trim(AddScriptToExecuteOnDocumentCreatedPromise.Result, "`"") 1065 | } 1066 | RemoveScriptToExecuteOnDocumentCreated(Id) => this.wv.RemoveScriptToExecuteOnDocumentCreated(Id) 1067 | ExecuteScriptAsync(JavaScript) => this.wv.ExecuteScriptAsync(JavaScript) ;Execute code on the current Webpage 1068 | ExecuteScript(JavaScript, Timeout := -1) { 1069 | ExecuteScriptPromise := this.wv.ExecuteScriptAsync(JavaScript) 1070 | try { 1071 | ExecuteScriptPromise.await(Timeout) 1072 | } catch { 1073 | ExecuteScriptPromise.Result := "Timeout Error" 1074 | } 1075 | return Trim(ExecuteScriptPromise.Result, "`"") 1076 | } 1077 | CapturePreviewAsync(ImageFormat, ImageStream) => this.wv.CapturePreviewAsync(ImageFormat, ImageStream) ;Take a "screenshot" of the current WebView2 content 1078 | CapturePreview(ImageFormat, ImageStream) { 1079 | CapturePreviewPromise := this.wv.CapturePreviewAsync(ImageFormat, ImageStream) 1080 | CapturePreviewPromise.await() 1081 | return CapturePreviewPromise.Result 1082 | } 1083 | Reload() => this.wv.Reload() ;Reloads the current page 1084 | 1085 | /** 1086 | * In order to use PostWebMessageAsJson() or PostWebMessageAsString(), you'll need to setup your webpage to listen to messages 1087 | * First, MyWindow.Settings.IsWebMessageEnabled must be set to true 1088 | * On your webpage itself, you'll need to setup an EventListner and Handler for the WebMessages 1089 | * window.chrome.webview.addEventListener('message', ahkWebMessage); 1090 | * function ahkWebMessage(Msg) { 1091 | * console.log(Msg); 1092 | * } 1093 | **/ 1094 | PostWebMessageAsJson(WebMessageAsJson) => this.wv.PostWebMessageAsJson(WebMessageAsJson) ;Posts the specified JSON message to the top level document in this WebView 1095 | PostWebMessageAsString(WebMessageAsString) => this.wv.PostWebMessageAsString(WebMessageAsString) ;Posts the specified STRING message to the top level document in this WebView 1096 | CallDevToolsProtocolMethodAsync(MethodName, ParametersAsJson) => this.wv.CallDevToolsProtocolMethodAsync(MethodName, ParametersAsJson) ;Runs an DevToolsProtocol method 1097 | 1098 | /** 1099 | * @returns {Boolean} The process ID of the browser process that hosts the WebView2. 1100 | * @see {@link https://learn.microsoft.com/en-us/dotnet/api/microsoft.web.webview2.core.corewebview2.browserprocessid|BrowserProcessId} 1101 | */ 1102 | BrowserProcessId => this.wv.BrowserProcessId ;Returns the process ID of the browser process that hosts the WebView2 1103 | 1104 | CanGoBack => this.wv.CanGoBack ;Returns true if the WebView is able to navigate to a previous page in the navigation history 1105 | CanGoForward => this.wv.CanGoForward ;Returns true if the WebView is able to navigate to a next page in the navigation history 1106 | 1107 | /** 1108 | * Navigates the WebView to the previous page in the navigation history. 1109 | * @see {@link https://learn.microsoft.com/en-us/dotnet/api/microsoft.web.webview2.core.corewebview2.goback|GoBack} 1110 | */ 1111 | GoBack() => this.wv.GoBack() ;GoBack to the previous page in the navigation history 1112 | GoForward() => this.wv.GoForward() ;GoForward to the next page in the navigation history 1113 | GetDevToolsProtocolEventReceiver(EventName) => this.wv.GetDevToolsProtocolEventReceiver(EventName) ;Gets a DevTools Protocol event receiver that allows you to subscribe to a DevToolsProtocol event 1114 | Stop() => this.wv.Stop() ;Stops all navigations and pending resource fetches 1115 | DocumentTitle => this.wv.DocumentTitle ;Returns the DocumentTitle of the current webpage 1116 | AddHostObjectToScript(ObjName, Obj) => this.wv.AddHostObjectToScript(ObjName, Obj) ;Create object link between the WebView2 and the AHK Script 1117 | RemoveHostObjectFromScript(ObjName) => this.wv.RemoveHostObjectFromScript(ObjName) ;Delete object link from the WebView2 1118 | OpenDevToolsWindow() => this.wv.OpenDevToolsWindow() ;Opens DevTools for the current WebView2 1119 | ContainsFullScreenElement => this.wv.ContainsFullScreenElement ;Returns true if the WebView contains a fullscreen HTML element 1120 | AddWebResourceRequestedFilter(Uri, ResourceContext) => this.wv.AddWebResourceRequestedFilter(Uri, ResourceContext) ;Adds a URI and resource context filter for the WebResourceRequested event 1121 | RemoveWebResourceRequestedFilter(Uri, ResourceContext) => this.wv.RemoveWebResourceRequestedFilter(Uri, ResourceContext) ;Removes a matching WebResource filter that was previously added for the WebResourceRequested event 1122 | NavigateWithWebResourceRequest(Request) => this.wv.NavigateWithWebResourceRequest(Request) ;Navigates using a constructed CoreWebView2WebResourceRequest object 1123 | CookieManager => this.wv.CookieManager ;Gets the CoreWebView2CookieManager object associated with this CoreWebView2 1124 | GetCookiesAsync(Uri) => this.CookieManager.GetCookies(Uri) ;Gets a list of cookies matching the specific URI 1125 | 1126 | Environment => this.wv.Environment ;Returns Map() of Environment settings 1127 | CreateCoreWebView2ControllerAsync(ParentWindow) => this.Environment.CreateWebView2ControllerAsync(ParentWindow) 1128 | CreateWebResourceResponse(Content, StatusCode, ReasonPhrase, Headers) => this.Environment.CreateWebResourceResponse(Content, StatusCode, ReasonPhrase, Headers) 1129 | BrowserVersionString => this.Environment.BrowserVersionString ;Returns the browser version info of the current CoreWebView2Environment, including channel name if it is not the stable channel 1130 | FailureReportFolderPath => this.Environment.FailureReportFolderPath ;Returns the failure report folder that all CoreWebView2s created from this environment are using 1131 | UserDataFolder => this.Environment.UserDataFolder ;Returns the user data folder that all CoreWebView2s created from this environment are using 1132 | CreateWebResourceRequest(Uri, Method, PostData, Headers) => this.Environment.CreateWebResourceRequest(Uri, Method, PostData, Headers) ;Creates a new CoreWebView2WebResourceRequest object 1133 | CreateCoreWebView2CompositionControllerAsync(ParentWindow) => this.Environment.CreateCoreWebView2CompositionControllerAsync(ParentWindow) ;Creates a new WebView for use with visual hosting 1134 | CreateCoreWebView2PointerInfo() => this.Environment.CreateCoreWebView2PointerInfo() ;Returns Map() of a combined win32 POINTER_INFO, POINTER_TOUCH_INFO, and POINTER_PEN_INFO object 1135 | GetAutomationProviderForWindow(Hwnd) => this.Environment.GetAutomationProviderForWindow(Hwnd) ;PRODUCES ERROR, REACH OUT TO THQBY 1136 | CreatePrintSettings() => this.Environment.CreatePrintSettings() ;Creates the CoreWebView2PrintSettings used by the PrintToPdfAsync(String, CoreWebView2PrintSettings) method 1137 | GetProcessInfos() => this.Environment.GetProcessInfos() ;Returns the list of all CoreWebView2ProcessInfo using same user data folder except for crashpad process 1138 | CreateContextMenuItem(Label, IconStream, Kind) => this.Environment.CreateContextMenuItem(Label, IconStream, Kind) ;PRODUCES ERROR, REACH OUT TO THQBY 1139 | CreateCoreWebView2ControllerOptions() => this.Environment.CreateCoreWebView2ControllerOptions() ;PRODUCES ERROR, REACH OUT TO THQBY 1140 | CreateCoreWebView2ControllerWithOptionsAsync(ParentWindow, Options) => this.Environment.CreateCoreWebView2ControllerWithOptionsAsync(ParentWindow, Options) ;PRODUCES ERROR, REACH OUT TO THQBY -- I think the issue is part of the `CreateCoreWEbView2ControllerOptions()` method 1141 | CreateCoreWebView2CompositionControllerWithOptionsAsync(ParentWindow, Options) => this.Environment.CreateCoreWebView2CompositionControllerWithOptionsAsync(ParentWindow, Options) ;PRODUCES ERROR, REACH OUT TO THQBY -- I think the issue is part of the `CreateCoreWEbView2ControllerOptions()` method 1142 | CreateSharedBuffer(Size) => this.Environment.CreateSharedBuffer(Size) ;Create a shared memory based buffer with the specified size in bytes -- PRODUCES ERROR, REACH OUT TO THQBY 1143 | 1144 | TrySuspendAsync() => this.wv.TrySuspendAsync() ;Must set `IsVisible := 0` before trying to call 1145 | Resume() => this.wv.Resume() ;Resumes the WebView so that it resumes activities on the web page. Will fail unless you set `IsVisible := 1` 1146 | IsSuspended => this.wv.IsSuspended ;Returns true if the WebView is suspended 1147 | SetVirtualHostNameToFolderMapping(HostName, FolderPath, AccessKind) => this.wv.SetVirtualHostNameToFolderMapping(HostName, FolderPath, AccessKind) ;Sets a mapping between a virtual host name and a folder path to make available to web sites via that host name 1148 | ClearVirtualHostNameToFolderMapping(HostName) => this.wv.ClearVirtualHostNameToFolderMapping(HostName) ;Clears a host name mapping for local folder that was added by SetVirtualHostNameToFolderMapping() 1149 | OpenTaskManagerWindow() => this.wv.OpenTaskManagerWindow() ;Opens the Browser Task Manager view as a new window in the foreground 1150 | IsMuted { ;Indicates whether all audio output from this CoreWebView2 is muted or not. Set to true will mute this CoreWebView2, and set to false will unmute this CoreWebView2. true if audio is muted 1151 | get => this.wv.IsMuted 1152 | set => this.wv.IsMuted := Value 1153 | } 1154 | IsDocumentPlayingAudio => this.wv.IsDocumentPlayingAudio ;Returns true if audio is playing even if IsMuted is true 1155 | IsDefaultDownloadDialogOpen => this.wv.IsDefaultDownloadDialogOpen ;Returns true if the default download dialog is currently open 1156 | OpenDefaultDownloadDialog() => this.wv.OpenDefaultDownloadDialog() ;Opens the DownloadDialog Popup Window 1157 | CloseDefaultDownloadDialog() => this.wv.CloseDefaultDownloadDialog() ;Closes the DownloadDialog Popup Window 1158 | DefaultDownloadDialogCornerAlignment { ;Position of DownloadDialog does not update until after the WebView2 position or size has changed 1159 | get => this.wv.DefaultDownloadDialogCornerAlignment ;Return the current corner the DownloadDialog will show up in (0 := TopLeft, 1 := TopRight, 2 := BottomLeft, 3 := BottomRight) 1160 | set => this.wv.DefaultDownloadDialogCornerAlignment := Value ;Set the corner of the WebView2 that the DownloadDialog will show up in (0 := TopLeft, 1 := TopRight, 2 := BottomLeft, 3 := BottomRight) 1161 | } 1162 | DefaultDownloadDialogMargin { ;Working, but I don't know how to accurately assign a new Margin yet. We can assign one via an Integer, but it's hit and miss to get the position correct 1163 | get => this.wv.DefaultDownloadDialogMargin 1164 | set => this.wv.DefaultDownloadDialogMargin := Value 1165 | } 1166 | CallDevToolsProtocolMethodForSessionAsync(SessionId, MethodName, ParametersAsJson) => this.wv.CallDevToolsProtocolMethodForSessionAsync(SessionId, MethodName, ParametersAsJson) ;Runs a DevToolsProtocol method for a specific session of an attached target 1167 | StatusBarText => this.wv.StatusBarText ;Returns the current text of the WebView2 StatusBar 1168 | Profile => this.wv.Profile ;Returns the associated CoreWebView2Profile object of CoreWebView2 1169 | ClearServerCertificateErrorActionsAsync() => this.wv.ClearServerCertificateErrorActionsAsync() 1170 | FaviconUri => this.wv.FaviconUri ;Returns the Uri as a string of the current Favicon. This will be an empty string if the page does not have a Favicon 1171 | GetFaviconAsync(Format) => this.wv.GetFaviconAsync(Format) ;Get the downloaded Favicon image for the current page and copy it to the image stream 1172 | PrintAsync(PrintSettings) => this.wv.PrintAsync(PrintSettings) ;Print the current web page asynchronously to the specified printer with the provided settings 1173 | PrintToPdfAsync(ResultFilePath, PrintSettings) => this.wv.PrintToPdfAsync(ResultFilePath, PrintSettings) ;Print the current page to PDF with the provided settings 1174 | ShowPrintUI(PrintDialogKind) => this.wv.ShowPrintUI(PrintDialogKind) ;Opens the print dialog to print the current web page. Browser printDialogKind := 0, System printDialogKind := 1 1175 | PrintToPdfStreamAsync(PrintSettings) => this.wv.PrintToPdfStreamAsync(PrintSettings) ;Provides the PDF data of current web page for the provided settings to a Stream 1176 | PostSharedBufferToScript(SharedBuffer, Access, AdditionalDataAsJson) => this.wv.PostSharedBufferToScript(SharedBuffer, Access, AdditionalDataAsJson) ;Share a shared buffer object with script of the main frame in the WebView 1177 | MemoryUsageTargetLevel { ;0 = Normal, 1 = Low; Low can be used for apps that are inactive to conserve memory usage 1178 | get => this.wv.MemoryUsageTargetLevel 1179 | set => this.wv.MemoryUsageTargetLevel := Value 1180 | } 1181 | 1182 | ;------------------------------------------------------------------------------------------- 1183 | ;Handler Assignments 1184 | static PlaceholderHandler(Handler, ICoreWebView2, Args) { 1185 | ;MsgBox(handler, "WebviewWindow.PlaceholderHandler()", "262144") 1186 | } 1187 | 1188 | ;Controller 1189 | ZoomFactorChanged(Handler) => this.wvc.add_ZoomFactorChanged(Handler) 1190 | MoveFocusRequested(Handler) => this.wvc.add_MoveFocusRequested(Handler) 1191 | GotFocus(Handler) => this.wvc.add_GotFocus(Handler) 1192 | LostFocus(Handler) => this.wvc.add_LostFocus(Handler) 1193 | AcceleratorKeyPressed(Handler) => this.wvc.add_AcceleratorKeyPressed(Handler) 1194 | RasterizationScaleChanged(Handler) => this.wvc.add_RasterizationScaleChanged(Handler) 1195 | 1196 | ;Core 1197 | NavigationStarting(Handler) => this.wv.add_NavigationStarting(Handler) 1198 | ContentLoading(Handler) => this.wv.add_ContentLoading(Handler) 1199 | SourceChanged(Handler) => this.wv.add_SourceChanged(Handler) 1200 | HistoryChanged(Handler) => this.wv.add_HistoryChanged(Handler) 1201 | NavigationCompleted(Handler) => this.wv.add_NavigationCompleted(Handler) 1202 | ScriptDialogOpening(Handler) => this.wv.add_ScriptDialogOpening(Handler) 1203 | PermissionRequested(Handler) => this.wv.add_PermissionRequested(Handler) 1204 | ProcessFailed(Handler) => this.wv.add_ProcessFailed(Handler) 1205 | WebMessageReceived(Handler) => this.wv.add_WebMessageReceived(Handler) 1206 | NewWindowRequested(Handler) => this.wv.add_NewWindowRequested(Handler) 1207 | DocumentTitleChanged(Handler) => this.wv.add_DocumentTitleChanged(Handler) 1208 | ContainsFullScreenElementChanged(Handler) => this.wv.add_ContainsFullScreenElementChanged(Handler) 1209 | WebResourceRequested(Handler) => this.wv.add_WebResourceRequested(Handler) 1210 | WindowCloseRequested(Handler) => this.wv.add_WindowCloseRequested(Handler) 1211 | WebResourceResponseReceived(Handler) => this.wv.add_WebResourceResponseReceived(Handler) 1212 | DOMContentLoaded(Handler) => this.wv.add_DOMContentLoaded(Handler) 1213 | FrameCreated(Handler) => this.wv.add_FrameCreated(Handler) 1214 | DownloadStarting(Handler) => this.wv.add_ownloadStarting(Handler) 1215 | ClientCertificateRequested(Handler) => this.wv.add_ClientCertificateRequested(Handler) 1216 | IsMutedChanged(Handler) => this.wv.add_IsMutedChanged(Handler) 1217 | IsDocumentPlayingAudioChanged(Handler) => this.wv.add_IsDocumentPlayingAudioChanged(Handler) 1218 | IsDefaultDownloadDialogOpenChanged(Handler) => this.wv.add_IsDefaultDownloadDialogOpenChanged(Handler) 1219 | BasicAuthenticationRequested(Handler) => this.wv.add_BasicAuthenticationRequested(Handler) 1220 | ContextMenuRequested(Handler) => this.wv.add_ContextMenuRequested(Handler) 1221 | StatusBarTextChanged(Handler) => this.wv.add_StatusBarTextChanged(Handler) 1222 | ServerCertificateErrorDetected(Handler) => this.wv.add_ServerCertificateErrorDetected(Handler) 1223 | FaviconChanged(Handler) => this.wv.add_FaviconChanged(Handler) 1224 | LaunchingExternalUriScheme(Handler) => this.wv.add_LaunchingExternalUriScheme(Handler) 1225 | } -------------------------------------------------------------------------------- /Pages/Bootstrap/color-modes.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Color mode toggler for Bootstrap's docs (https://getbootstrap.com/) 3 | * Copyright 2011-2023 The Bootstrap Authors 4 | * Licensed under the Creative Commons Attribution 3.0 Unported License. 5 | */ 6 | 7 | (() => { 8 | 'use strict' 9 | 10 | const getStoredTheme = () => localStorage.getItem('theme') 11 | const setStoredTheme = theme => localStorage.setItem('theme', theme) 12 | 13 | const getPreferredTheme = () => { 14 | const storedTheme = getStoredTheme() 15 | if (storedTheme) { 16 | return storedTheme 17 | } 18 | 19 | return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light' 20 | } 21 | 22 | const setTheme = theme => { 23 | if (theme === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches) { 24 | document.documentElement.setAttribute('data-bs-theme', 'dark') 25 | } else { 26 | document.documentElement.setAttribute('data-bs-theme', theme) 27 | } 28 | } 29 | 30 | setTheme(getPreferredTheme()) 31 | 32 | const showActiveTheme = (theme, focus = false) => { 33 | const themeSwitcher = document.querySelector('#bd-theme') 34 | 35 | if (!themeSwitcher) { 36 | return 37 | } 38 | 39 | const themeSwitcherText = document.querySelector('#bd-theme-text') 40 | const activeThemeIcon = document.querySelector('.theme-icon-active use') 41 | const btnToActive = document.querySelector(`[data-bs-theme-value="${theme}"]`) 42 | const svgOfActiveBtn = btnToActive.querySelector('svg use').getAttribute('href') 43 | 44 | document.querySelectorAll('[data-bs-theme-value]').forEach(element => { 45 | element.classList.remove('active') 46 | element.setAttribute('aria-pressed', 'false') 47 | }) 48 | 49 | btnToActive.classList.add('active') 50 | btnToActive.setAttribute('aria-pressed', 'true') 51 | activeThemeIcon.setAttribute('href', svgOfActiveBtn) 52 | const themeSwitcherLabel = `${themeSwitcherText.textContent} (${btnToActive.dataset.bsThemeValue})` 53 | themeSwitcher.setAttribute('aria-label', themeSwitcherLabel) 54 | 55 | if (focus) { 56 | themeSwitcher.focus() 57 | } 58 | } 59 | 60 | window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => { 61 | const storedTheme = getStoredTheme() 62 | if (storedTheme !== 'light' && storedTheme !== 'dark') { 63 | setTheme(getPreferredTheme()) 64 | } 65 | }) 66 | 67 | window.addEventListener('DOMContentLoaded', () => { 68 | showActiveTheme(getPreferredTheme()) 69 | 70 | document.querySelectorAll('[data-bs-theme-value]') 71 | .forEach(toggle => { 72 | toggle.addEventListener('click', () => { 73 | const theme = toggle.getAttribute('data-bs-theme-value') 74 | setStoredTheme(theme) 75 | setTheme(theme) 76 | showActiveTheme(theme, true) 77 | }) 78 | }) 79 | }) 80 | })() 81 | -------------------------------------------------------------------------------- /Pages/Bootstrap/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/The-CoDingman/WebViewToo/53fc321984d1ad9665038950f5ef4cedd1face35/Pages/Bootstrap/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /Pages/Bootstrap/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/The-CoDingman/WebViewToo/53fc321984d1ad9665038950f5ef4cedd1face35/Pages/Bootstrap/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /Pages/Bootstrap/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/The-CoDingman/WebViewToo/53fc321984d1ad9665038950f5ef4cedd1face35/Pages/Bootstrap/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /Pages/Bootstrap/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/The-CoDingman/WebViewToo/53fc321984d1ad9665038950f5ef4cedd1face35/Pages/Bootstrap/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /Pages/Bootstrap/sidebars.css: -------------------------------------------------------------------------------- 1 | body { 2 | min-height: 100vh; 3 | min-height: -webkit-fill-available; 4 | } 5 | 6 | html { 7 | height: -webkit-fill-available; 8 | } 9 | 10 | main { 11 | height: 100vh; 12 | height: -webkit-fill-available; 13 | max-height: 100vh; 14 | overflow-x: auto; 15 | overflow-y: hidden; 16 | } 17 | 18 | .dropdown-toggle { outline: 0; } 19 | 20 | .btn-toggle { 21 | padding: .25rem .5rem; 22 | font-weight: 600; 23 | color: var(--bs-emphasis-color); 24 | background-color: transparent; 25 | } 26 | .btn-toggle:hover, 27 | .btn-toggle:focus { 28 | color: rgba(var(--bs-emphasis-color-rgb), .85); 29 | background-color: var(--bs-tertiary-bg); 30 | } 31 | 32 | .btn-toggle::before { 33 | width: 1.25em; 34 | line-height: 0; 35 | content: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='rgba%280,0,0,.5%29' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M5 14l6-6-6-6'/%3e%3c/svg%3e"); 36 | transition: transform .35s ease; 37 | transform-origin: .5em 50%; 38 | } 39 | 40 | [data-bs-theme="dark"] .btn-toggle::before { 41 | content: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='rgba%28255,255,255,.5%29' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M5 14l6-6-6-6'/%3e%3c/svg%3e"); 42 | } 43 | 44 | .btn-toggle[aria-expanded="true"] { 45 | color: rgba(var(--bs-emphasis-color-rgb), .85); 46 | } 47 | .btn-toggle[aria-expanded="true"]::before { 48 | transform: rotate(90deg); 49 | } 50 | 51 | .btn-toggle-nav a { 52 | padding: .1875rem .5rem; 53 | margin-top: .125rem; 54 | margin-left: 1.25rem; 55 | } 56 | .btn-toggle-nav a:hover, 57 | .btn-toggle-nav a:focus { 58 | background-color: var(--bs-tertiary-bg); 59 | } 60 | 61 | .scrollarea { 62 | overflow-y: auto; 63 | } 64 | -------------------------------------------------------------------------------- /Pages/Bootstrap/sidebars.js: -------------------------------------------------------------------------------- 1 | /* global bootstrap: false */ 2 | (() => { 3 | 'use strict' 4 | const tooltipTriggerList = Array.from(document.querySelectorAll('[data-bs-toggle="tooltip"]')) 5 | tooltipTriggerList.forEach(tooltipTriggerEl => { 6 | new bootstrap.Tooltip(tooltipTriggerEl) 7 | }) 8 | })() -------------------------------------------------------------------------------- /Pages/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Sidebars · Bootstrap v5.3 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 176 | 177 | 192 | 193 | 194 |
195 | WebViewToo Example 196 | 0 197 | 1 198 | 2 199 | r 200 |
201 |
202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 253 | 254 | 255 | 256 | Bootstrap 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 |
279 |
280 | 282 | 283 | Sidebar 284 | 285 |
286 | 303 |
304 | 318 |
319 | 320 |
321 | 322 | 323 |
324 |
325 | 354 |
355 |
356 | 357 |
358 | 359 |
360 |
361 |

Welcome to WebViewToo

362 |

363 | The WebViewToo class provides an easy to use method for building Web-based GUIs with AutoHotkey. It 364 | uses the same WebView2 framework from Microsoft present in Microsoft Edge. 365 |

366 |

367 | This example is designed to show how you can use third party web frameworks like Bootstrap to build advanced 368 | user interfaces, while still keeping all the code local. Only minimal resources will need to be extracted 369 | from the compiled file. 370 |

371 |

372 | As this example is more advanced, it assumes a stronger familiarity with the technology and may gloss over 373 | some parts more than other examples. If you're just getting started it may be helpful to work with some of 374 | the other example scripts first. 375 |

376 |
377 |
378 | 379 | 380 | 381 |
382 |
383 |

Buttons!

384 | 385 |

Colored Buttons:

386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 |

Outline Buttons:

397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 |

Block Buttons:

408 | 409 | 410 |
411 |
412 | 413 | 414 | 415 |
416 |
417 |

Forms!

418 |
419 |
420 |
421 | 422 | 423 |
424 | 425 |
426 | 427 | 428 |
429 |
430 | 431 |
432 | 433 | 434 |
435 | 436 |
437 | 438 | 439 |
440 | 441 |
442 |
443 | 444 | 445 |
446 | 447 |
448 | 449 | 453 |
454 | 455 |
456 | 457 | 458 |
459 |
460 | 461 |
462 |
463 | 464 | 467 |
468 |
469 |
470 | 471 |
472 |
473 |
474 | 475 | 476 | 477 |
478 |
479 |

Glyphicons


480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 |
738 |
739 | 740 | 741 | 742 |
743 |
744 |
The documentation for WebViewToo is currently being reworked. Sorry for the inconvenience.
745 |
746 |
747 | 748 |
749 |
750 | 751 |
752 | 753 | 775 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WebViewToo 2 | Allows for use of the WebView2 Framework within AHK to create Web Controls and Web-based GUIs 3 | 4 | ## NOTE: THIS UPDATE IS NOT BACKWARDS COMPATIBLE. 5 | - If you have current scripts using this library, you will need to update your scripts. 6 | 7 | ### Major Changes 8 | - The class has been split into three parts. 9 | - The `WebViewCtrl` class can be used to add a new WebView2 Control to a standard AHK GUI. 10 | - The `WebViewGui` class can be used to create fully functional Web-based windows. 11 | - This class replaces the `WebViewToo` class. This name change was made to make a more clear distinction between a `WebViewCtrl` and `WebViewGui`. 12 | - The `WebViewSizer` class should not be explicitly used, this is used by the `WebViewGui` class. 13 | - This class replaces the `Border` class that was added in the last update. 14 | - Navigation routing has been overhauled to provider a smoother end-user process. 15 | 16 | ### Minor Changes 17 | - The `Close()` method has been removed, you should now use the `Hide()` method or `WinClose()` function directly. 18 | - The `EnableGlobal()` method is now automatically intialized for your default host (`.localhost` by default). 19 | - To enable it for other hosts you can now use the `AllowGlobalAccessFor()` method. 20 | - The `Load()` method has been removed, you should now use `Navigate()` 21 | - Several GUI related methods and properties for `WebViewGui` have been removed as the class now properly extends native GUIs. 22 | - These include `Minimize()`, `Maximize()`, `Opt()`, `BackColor`, `Name`, etc. 23 | - NOTE: These still work, they just no longer needed to be defined. 24 | ### Bug Fixes 25 | - Fixed window displaying as a small grey box during creation on single-monitor setups. -------------------------------------------------------------------------------- /Simple Browser.ahk: -------------------------------------------------------------------------------- 1 | ;Environment Controls 2 | ;/////////////////////////////////////////////////////////////////////////////////////////// 3 | #Requires AutoHotkey v2.0 4 | #SingleInstance Force 5 | #Include Lib\WebViewToo.ahk 6 | Gui.ProtoType.AddWebViewCtrl := WebViewCtrl 7 | ;/////////////////////////////////////////////////////////////////////////////////////////// 8 | 9 | ;Main Script 10 | ;/////////////////////////////////////////////////////////////////////////////////////////// 11 | ;Create Dll if script is compiled 12 | if (A_IsCompiled) { 13 | WebViewCtrl.CreateFileFromResource(DllPath := ((A_PtrSize * 8) "bit\WebView2Loader.dll"), WebViewCtrl.TempDir) 14 | } 15 | 16 | ;Define our custom settings 17 | WebViewSettings := { 18 | DataDir: WebViewCtrl.TempDir, 19 | DllPath: A_IsCompiled ? WebViewCtrl.TempDir "\" (A_PtrSize * 8) "bit\WebView2Loader.dll" : "WebView2Loader.dll" 20 | } 21 | 22 | MyGui := Gui() 23 | MyGui.OnEvent("Close", (*) => ExitApp()) 24 | MyGui.AddText("xm ym+3", "Url:") 25 | MyGui.AddEdit("x+5 yp-3 w745 vUrlEdit", "") 26 | MyGui.AddButton("x+5 vNavigateButton", "Go").OnEvent("Click", NavToUrl) 27 | MyGui.AddWebViewCtrl("xm w800 h600 vWVToo", WebViewSettings) 28 | MyGui.Show() 29 | 30 | NavToUrl(GuiCtrlObj, *) { 31 | CurrGui := GuiCtrlObj.Gui 32 | CurrGui["WVToo"].Navigate(CurrGui["UrlEdit"].Value) 33 | CurrGui["UrlEdit"].Value := "" 34 | } 35 | ;/////////////////////////////////////////////////////////////////////////////////////////// 36 | 37 | ;Hotkeys 38 | ;/////////////////////////////////////////////////////////////////////////////////////////// 39 | #HotIf WinActive(MyGui.Hwnd) && (MyGui.FocusedCtrl = MyGui["UrlEdit"]) 40 | Enter::NavToUrl(MyGui["UrlEdit"]) 41 | #HotIf 42 | ;/////////////////////////////////////////////////////////////////////////////////////////// 43 | 44 | ;Resources for Compiled Scripts 45 | ;/////////////////////////////////////////////////////////////////////////////////////////// 46 | ;@Ahk2Exe-AddResource Lib\32bit\WebView2Loader.dll, 32bit\WebView2Loader.dll 47 | ;@Ahk2Exe-AddResource Lib\64bit\WebView2Loader.dll, 64bit\WebView2Loader.dll 48 | ;/////////////////////////////////////////////////////////////////////////////////////////// 49 | -------------------------------------------------------------------------------- /WebViewGui Example.ahk: -------------------------------------------------------------------------------- 1 | ;Environment Controls 2 | ;/////////////////////////////////////////////////////////////////////////////////////////// 3 | #Requires AutoHotkey v2 4 | #SingleInstance Force 5 | #Include Lib\WebViewToo.ahk 6 | GroupAdd("ScriptGroup", "ahk_pid" DllCall("GetCurrentProcessId")) 7 | ;/////////////////////////////////////////////////////////////////////////////////////////// 8 | 9 | ;Create the WebViewGui 10 | ;/////////////////////////////////////////////////////////////////////////////////////////// 11 | if (A_IsCompiled) { 12 | WebViewCtrl.CreateFileFromResource((A_PtrSize * 8) "bit\WebView2Loader.dll", WebViewCtrl.TempDir) 13 | WebViewSettings := {DllPath: WebViewCtrl.TempDir "\" (A_PtrSize * 8) "bit\WebView2Loader.dll"} 14 | } else { 15 | WebViewSettings := {} 16 | } 17 | 18 | MyWindow := WebViewGui("+Resize -Caption",,, WebViewSettings) 19 | MyWindow.OnEvent("Close", (*) => ExitApp()) 20 | MyWindow.Navigate("Pages/index.html") 21 | MyWindow.Debug() 22 | MyWindow.AddCallbackToScript("Tooltip", WebTooltipEvent) 23 | MyWindow.AddCallbackToScript("SubmitForm", SubmitFormHandler) 24 | MyWindow.AddCallbackToScript("CopyGlyphCode", CopyGlyphCodeEvent) 25 | MyWindow.AddHostObjectToScript("ButtonClick", {func: WebButtonClickEvent}) 26 | MyWindow.Show("w800 h600") 27 | ;/////////////////////////////////////////////////////////////////////////////////////////// 28 | 29 | ;Hotkeys 30 | ;/////////////////////////////////////////////////////////////////////////////////////////// 31 | #HotIf WinActive("ahk_group ScriptGroup") 32 | F1:: { 33 | MsgBox(MyWindow.Title) 34 | MyWindow.Title := "New Title!" 35 | MyWindow.ExecuteScriptAsync("document.querySelector('#ahkTitleBar').textContent = '" MyWindow.Title "'") 36 | MsgBox(MyWindow.Title) 37 | } 38 | 39 | F2:: { 40 | static Toggle := 0 41 | Toggle := !Toggle 42 | if (Toggle) { 43 | MyWindow.PostWebMessageAsString("Hello World") 44 | } else { 45 | MyWindow.PostWebMessageAsJson('{"key1": "value1"}') 46 | } 47 | } 48 | 49 | F3:: { 50 | MyWindow.SimplePrintToPdf() 51 | } 52 | #HotIf 53 | ;/////////////////////////////////////////////////////////////////////////////////////////// 54 | 55 | ;Web Callback Functions 56 | ;/////////////////////////////////////////////////////////////////////////////////////////// 57 | WebButtonClickEvent(button) { 58 | MsgBox(button) 59 | } 60 | 61 | CopyGlyphCodeEvent(WebView, Title) { 62 | GlyphCode := "" 63 | MsgBox(A_Clipboard := GlyphCode, "OuterHTML Copied to Clipboard") 64 | } 65 | 66 | WebTooltipEvent(WebView, Msg) { 67 | ToolTip(Msg) 68 | SetTimer((*) => ToolTip(), -1000) 69 | } 70 | 71 | SubmitFormHandler(WebView, FormData) { 72 | Output := "" 73 | Output .= "Email: " FormData.Email "`n" 74 | Output .= "Password: " FormData.Password "`n" 75 | Output .= "Address: " FormData.Address "`n" 76 | Output .= "Address2: " FormData.Address2 "`n" 77 | Output .= "City: " FormData.City "`n" 78 | Output .= "State: " FormData.State "`n" 79 | Output .= "Zip: " FormData.Zip "`n" 80 | try Output .= "Check: " FormData.Check "`n" ;Only works when checked 81 | MsgBox(Output) 82 | } 83 | ;/////////////////////////////////////////////////////////////////////////////////////////// 84 | 85 | ;Resources for Compiled Scripts 86 | ;/////////////////////////////////////////////////////////////////////////////////////////// 87 | ;@Ahk2Exe-AddResource Lib\32bit\WebView2Loader.dll, 32bit\WebView2Loader.dll 88 | ;@Ahk2Exe-AddResource Lib\64bit\WebView2Loader.dll, 64bit\WebView2Loader.dll 89 | ;@Ahk2Exe-AddResource Pages\index.html, Pages\index.html 90 | ;@Ahk2Exe-AddResource Pages\Bootstrap\bootstrap.bundle.min.js, Pages\Bootstrap\bootstrap.bundle.min.js 91 | ;@Ahk2Exe-AddResource Pages\Bootstrap\bootstrap.min.css, Pages\Bootstrap\bootstrap.min.css 92 | ;@Ahk2Exe-AddResource Pages\Bootstrap\color-modes.js, Pages\Bootstrap\color-modes.js 93 | ;@Ahk2Exe-AddResource Pages\Bootstrap\sidebars.css, Pages\Bootstrap\sidebars.css 94 | ;@Ahk2Exe-AddResource Pages\Bootstrap\sidebars.js, Pages\Bootstrap\sidebars.js 95 | ;@Ahk2Exe-AddResource Pages\Bootstrap\fonts\glyphicons-halflings-regular.ttf, Pages\Bootstrap\fonts\glyphicons-halflings-regular.ttf 96 | ;@Ahk2Exe-AddResource Pages\Bootstrap\fonts\glyphicons-halflings-regular.woff, Pages\Bootstrap\fonts\glyphicons-halflings-regular.woff 97 | ;@Ahk2Exe-AddResource Pages\Bootstrap\fonts\glyphicons-halflings-regular.woff2, Pages\Bootstrap\fonts\glyphicons-halflings-regular.woff2 98 | ;/////////////////////////////////////////////////////////////////////////////////////////// 99 | --------------------------------------------------------------------------------