Skip to content

Commit 30d7cc6

Browse files
authored
mdMsgBox
1 parent 5f34530 commit 30d7cc6

2 files changed

Lines changed: 514 additions & 0 deletions

File tree

examples/019-mdMsgBox.au3

Lines changed: 391 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,391 @@
1+
#NoTrayIcon
2+
#AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7
3+
#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
4+
#AutoIt3Wrapper_Icon=
5+
#AutoIt3Wrapper_Outfile=mdMsgBox.exe
6+
#AutoIt3Wrapper_Res_Description=Markdown Modern MsgBox (WebView2)
7+
#AutoIt3Wrapper_Res_Fileversion=0.0.0.3
8+
#AutoIt3Wrapper_Res_ProductName=MarkdownMsgBox
9+
#AutoIt3Wrapper_AU3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7
10+
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****
11+
12+
#include <GUIConstantsEx.au3>
13+
#include <WindowsConstants.au3>
14+
#include "..\NetWebView2Lib.au3"
15+
16+
;----------------------------------------------------------------------------------------
17+
; Title...........: 019-mdMsgBox.au3
18+
; Description.....: Markdown Modern MsgBox (WebView2)
19+
; AutoIt Version..: 3.3.18.0 Author: ioa747 Script Version: 0.1
20+
; Note............: Testet in Windows 11 Pro 25H2 Date:20/03/2026
21+
; ## The dependencies are in the folder @ScriptDir & "\JS_Lib"
22+
; 🏆 Thanks to https://github.com/markedjs/marked/
23+
; 🏆 Thanks to https://fontawesome.com/search?ic=free-collection
24+
;----------------------------------------------------------------------------------------
25+
26+
DllCall("User32.dll", "bool", "SetProcessDpiAwarenessContext", "int_ptr", -2)
27+
28+
$_g_bNetWebView2_DebugInfo = False
29+
30+
Global $g_hGUI, $g_hBkColor, $g_hTxtColor, $g_hFootColor, $g_iMaxWidth, $g_iMaxHeight, $g_iLeft, $g_iTop, $g_sTitle, $g_sText, _
31+
$g_sButtons, $g_sBtnDefColor, $g_sBtnColor, $g_sBtnResult, $g_aBtn, $g_iTimer, $g_iTopMost, $g_hParent
32+
33+
$g_hGUI = 0
34+
$g_hBkColor = Hex(0x2B2B2B, 6) ; Dark slate gray
35+
$g_hTxtColor = Hex(0xE0E0E0, 6) ; Gainsboro
36+
$g_hFootColor = Hex(0x1E1E1E, 6) ; Black
37+
$g_iMaxWidth = 400
38+
$g_iMaxHeight = 800
39+
$g_iLeft = -1
40+
$g_iTop = -1
41+
$g_sTitle = "Markdown MsgBox"
42+
$g_sText = ""
43+
$g_sButtons = "OK"
44+
$g_sBtnDefColor = Hex(0xD73443, 6) ; Crimson
45+
$g_sBtnColor = Hex(0x559FF2, 6) ; Cornflower blue
46+
$g_sBtnResult = ""
47+
$g_aBtn = StringSplit($g_sButtons, "|", 1)
48+
$g_iTimer = 0 ; Default: No timer
49+
$g_iTopMost = 0
50+
$g_hParent = 0
51+
52+
_CmdLine_Parsing()
53+
54+
; === Execution ===
55+
Global $oWebV2M
56+
Global $sResult = _MarkdownMsgBox()
57+
ConsoleWrite($sResult & @CRLF)
58+
Exit Int($sResult)
59+
60+
;---------------------------------------------------------------------------------------
61+
Func _CmdLine_Parsing()
62+
If $CmdLine[0] > 0 Then
63+
For $i = 1 To $CmdLine[0]
64+
Local $sCmd = $CmdLine[$i]
65+
Select
66+
Case StringInStr($sCmd, "/BkColor:")
67+
$g_hBkColor = Hex(StringReplace($sCmd, "/BkColor:", ""), 6)
68+
Case StringInStr($sCmd, "/TxtColor:")
69+
$g_hTxtColor = Hex(StringReplace($sCmd, "/TxtColor:", ""), 6)
70+
Case StringInStr($sCmd, "/FootColor:")
71+
$g_hFootColor = Hex(StringReplace($sCmd, "/FootColor:", ""), 6)
72+
Case StringInStr($sCmd, "/MaxWidth:")
73+
$g_iMaxWidth = Int(StringReplace($sCmd, "/MaxWidth:", ""))
74+
Case StringInStr($sCmd, "/MaxHeight:")
75+
$g_iMaxHeight = Int(StringReplace($sCmd, "/MaxHeight:", ""))
76+
Case StringInStr($sCmd, "/Left:")
77+
$g_iLeft = Int(StringReplace($sCmd, "/Left:", ""))
78+
Case StringInStr($sCmd, "/Top:")
79+
$g_iTop = Int(StringReplace($sCmd, "/Top:", ""))
80+
Case StringInStr($sCmd, "/Title:")
81+
$g_sTitle = StringReplace($sCmd, "/Title:", "")
82+
Case StringInStr($sCmd, "/Text:")
83+
Local $sHex = StringReplace($sCmd, "/Text:", "")
84+
$g_sText = BinaryToString($sHex, 4) ; 4 = UTF8
85+
Case StringInStr($sCmd, "/Buttons:")
86+
Local $sHexBtn = StringReplace($sCmd, "/Buttons:", "")
87+
; If the string starts with 0x, it is Hex, otherwise it is plain text
88+
If StringLeft($sHexBtn, 2) = "0x" Then
89+
$g_sButtons = BinaryToString($sHexBtn, 4)
90+
Else
91+
$g_sButtons = $sHexBtn
92+
EndIf
93+
$g_aBtn = StringSplit($g_sButtons, "|", 1)
94+
Case StringInStr($sCmd, "/BtnDefColor:")
95+
$g_sBtnDefColor = Hex(StringReplace($sCmd, "/BtnDefColor:", ""), 6)
96+
Case StringInStr($sCmd, "/BtnColor:")
97+
$g_sBtnColor = Hex(StringReplace($sCmd, "/BtnColor:", ""), 6)
98+
Case StringInStr($sCmd, "/Timer:")
99+
$g_iTimer = Int(StringReplace($sCmd, "/Timer:", "")) * 1000
100+
Case StringInStr($sCmd, "/TopMost:")
101+
$g_iTopMost = (Int(StringReplace($sCmd, "/TopMost:", "")) ? $WS_EX_TOPMOST : 0)
102+
Case StringInStr($sCmd, "/Parent:")
103+
$g_hParent = HWnd(StringReplace($sCmd, "/Parent:", ""))
104+
EndSelect
105+
Next
106+
EndIf
107+
EndFunc ;==>_CmdLine_Parsing
108+
;---------------------------------------------------------------------------------------
109+
Func _MarkdownMsgBox()
110+
; Create the GUI Window (Hidden)
111+
$g_hGUI = GUICreate($g_sTitle, $g_iMaxWidth, $g_iMaxHeight, -1, -1, BitOR($WS_CAPTION, $WS_POPUP, $WS_SYSMENU, $WS_CLIPCHILDREN), $g_iTopMost, $g_hParent)
112+
GUISetIcon(@ScriptFullPath)
113+
GUISetBkColor($g_hBkColor)
114+
115+
$oWebV2M = _NetWebView2_CreateManager("", "Web_Events_", "--allow-file-access-from-files --disable-web-security") ; 👈
116+
_NetWebView2_GetBridge($oWebV2M, "JS_Events_")
117+
118+
; Initialize WebView2 - Use formatted Hex string for background
119+
_NetWebView2_Initialize($oWebV2M, $g_hGUI, @ScriptDir & "\UserData", 0, 0, 0, 0, True, True, 1.0, "0x" & $g_hBkColor, False)
120+
121+
Local $sAssetsFolder = @ScriptDir & "\JS_Lib" ; The folder that has fontawesome\css\all.min.css
122+
_NetWebView2_SetVirtualHostNameToFolderMapping($oWebV2M, "local.lib", $sAssetsFolder, 0)
123+
124+
_RenderMarkdown()
125+
126+
; Register the background emergency timer
127+
AdlibRegister("_EmergencyShow", 1500)
128+
129+
; If set a Timer, start the TimeOut
130+
If $g_iTimer > 0 Then AdlibRegister("_TimeOut", $g_iTimer)
131+
132+
$g_sBtnResult = ""
133+
Local $nMsg
134+
While $g_sBtnResult = ""
135+
$nMsg = GUIGetMsg()
136+
Switch $nMsg
137+
Case $GUI_EVENT_CLOSE
138+
$g_sBtnResult = "0"
139+
EndSwitch
140+
WEnd
141+
142+
; Cleanup
143+
AdlibUnRegister("_EmergencyShow")
144+
AdlibUnRegister("_TimeOut")
145+
GUIDelete($g_hGUI)
146+
_NetWebView2_CleanUp($oWebV2M, $g_hGUI)
147+
148+
Return $g_sBtnResult
149+
150+
EndFunc ;==>_MarkdownMsgBox
151+
;---------------------------------------------------------------------------------------
152+
Volatile Func Web_Events_OnNavigationStarting($oWebV2M, $hGUI, $oArgs)
153+
#forceref $oWebV2M, $hGUI
154+
Local $sURL = $oArgs.Uri
155+
If StringLeft($sURL, 4) = "http" Then
156+
ShellExecute($sURL)
157+
$oArgs.Cancel = True ; Cancel navigation in WebView2
158+
EndIf
159+
EndFunc ;==>Web_Events_OnNavigationStarting
160+
;---------------------------------------------------------------------------------------
161+
Volatile Func JS_Events_OnMessageReceived($oWebV2M, $hGUI, $sMessage)
162+
#forceref $oWebV2M
163+
164+
If StringLeft($sMessage, 7) = "RESIZE|" Then
165+
; Cancel _EmergencyShow timer immediately since JS is responding
166+
AdlibUnRegister("_EmergencyShow")
167+
168+
Local $aData = StringSplit($sMessage, "|")
169+
If $aData[0] < 3 Then Return
170+
171+
Local $iW = Int($aData[2]), $iH = Int($aData[3])
172+
173+
; Constraints
174+
If $iW > $g_iMaxWidth Then $iW = $g_iMaxWidth
175+
If $iW < 300 Then $iW = 300
176+
If $iH > $g_iMaxHeight Then $iH = $g_iMaxHeight
177+
If $iH < 120 Then $iH = 120
178+
179+
; Final dimensions with window chrome offsets
180+
Local $iFinalW = $iW + 6
181+
Local $iFinalH = $iH + 30
182+
183+
; Use Global Left/Top if set, otherwise center
184+
Local $iTargetLeft = ($g_iLeft = -1) ? (@DesktopWidth - $iFinalW) / 2 : $g_iLeft
185+
Local $iTargetTop = ($g_iTop = -1) ? (@DesktopHeight - $iFinalH) / 2 : $g_iTop
186+
187+
; Resize and Show
188+
WinMove($hGUI, "", $iTargetLeft, $iTargetTop, $iFinalW, $iFinalH)
189+
GUISetState(@SW_SHOW, $hGUI)
190+
Return
191+
EndIf
192+
193+
$g_sBtnResult = $sMessage
194+
EndFunc ;==>JS_Events_OnMessageReceived
195+
;---------------------------------------------------------------------------------------
196+
Func _EmergencyShow()
197+
AdlibUnRegister("_EmergencyShow") ; Run only once
198+
199+
; If window is still hidden (State < 2), show it
200+
If $g_hGUI And Not BitAND(WinGetState($g_hGUI), 2) Then
201+
; ConsoleWrite("! Emergency Show Triggered" & @CRLF)
202+
GUISetState(@SW_SHOW, $g_hGUI)
203+
EndIf
204+
EndFunc ;==>_EmergencyShow
205+
;---------------------------------------------------------------------------------------
206+
Func _TimeOut()
207+
AdlibUnRegister("_TimeOut") ; Stop the timer
208+
$g_sBtnResult = "0" ; Force exit the loop with 'Cancel' status
209+
EndFunc ;==>_TimeOut
210+
;---------------------------------------------------------------------------------------
211+
Func _RenderMarkdown()
212+
Local $sStyle = _CSS()
213+
214+
; Define the local path for FontAwesome and Marked.js library using the virtual host mapping
215+
Local $sFA_Link = "<link rel='stylesheet' href='https://local.lib/fontawesome/css/all.min.css'>"
216+
Local $sMarked_Link = "<script src='https://local.lib/marked/marked.umd.min.js'></script>"
217+
218+
Local $Txt, $sButtons = "", $sDefaultBtn = ""
219+
220+
For $i = 1 To $g_aBtn[0]
221+
Local $isDefault = (StringLeft($g_aBtn[$i], 1) = "~")
222+
$Txt = ($isDefault ? StringTrimLeft($g_aBtn[$i], 1) : $g_aBtn[$i])
223+
224+
If $isDefault And $sDefaultBtn = "" Then $sDefaultBtn = $i
225+
226+
Local $sID = ($isDefault ? "id='default-btn'" : "")
227+
; Added a space before buttons for better horizontal separation
228+
$sButtons &= " <button " & $sID & " class='btn focusable-btn btn-" & $i & "' onclick='uiAction(" & $i & ")'>" & $Txt & "</button>"
229+
Next
230+
231+
Local $sHTML = "<html><head><meta charset='utf-8'>" & _
232+
"<base href='https://local.assets/'>" & _
233+
$sFA_Link & _
234+
$sMarked_Link & _
235+
"<style>" & $sStyle & "</style></head><body>" & _
236+
"<div id='main-container'>" & _
237+
" <div id='view'></div>" & _
238+
" <div class='footer'>" & $sButtons & "</div>" & _
239+
"</div>" & _
240+
"<script>" & @CRLF & _
241+
"function sendToAutoIt(data) {" & @CRLF & _
242+
" if (window.chrome && window.chrome.webview && window.chrome.webview.postMessage) {" & @CRLF & _
243+
" window.chrome.webview.postMessage(data.toString());" & @CRLF & _
244+
" } else {" & @CRLF & _
245+
" setTimeout(() => sendToAutoIt(data), 50);" & @CRLF & _
246+
" }" & @CRLF & _
247+
"}" & @CRLF & _
248+
@CRLF & _
249+
"function uiAction(val) { sendToAutoIt(val); }" & @CRLF & _
250+
@CRLF & _
251+
"function updateSize() {" & @CRLF & _
252+
" const container = document.getElementById('main-container');" & @CRLF & _
253+
" const rect = container.getBoundingClientRect();" & @CRLF & _
254+
" sendToAutoIt('RESIZE|' + Math.ceil(rect.width) + '|' + Math.ceil(rect.height));" & @CRLF & _
255+
" const defBtn = document.getElementById('default-btn');" & @CRLF & _
256+
" if(defBtn) defBtn.focus();" & @CRLF & _
257+
"}" & @CRLF & _
258+
@CRLF & _
259+
"window.addEventListener('load', function() {" & @CRLF & _
260+
" const view = document.getElementById('view');" & @CRLF & _
261+
" if (typeof marked !== 'undefined') {" & @CRLF & _
262+
" marked.setOptions({ gfm: true, breaks: true });" & @CRLF & _
263+
" view.innerHTML = marked.parse(" & _EscapeForJS($g_sText) & ".replace(/\\n|\\\\n/g, '\n'));" & @CRLF & _
264+
" } else {" & @CRLF & _
265+
" view.innerText = 'Error: Marked library not found.';" & @CRLF & _
266+
" }" & @CRLF & _
267+
" updateSize();" & @CRLF & _
268+
"});" & @CRLF & _
269+
@CRLF & _
270+
"window.addEventListener('keydown', function(e) {" & @CRLF & _
271+
" const buttons = document.querySelectorAll('.focusable-btn');" & @CRLF & _
272+
" if (e.key === 'Tab') {" & @CRLF & _
273+
" const firstBtn = buttons[0];" & @CRLF & _
274+
" const lastBtn = buttons[buttons.length - 1];" & @CRLF & _
275+
" if (e.shiftKey) {" & @CRLF & _
276+
" if (document.activeElement === firstBtn) { lastBtn.focus(); e.preventDefault(); }" & @CRLF & _
277+
" } else {" & @CRLF & _
278+
" if (document.activeElement === lastBtn) { firstBtn.focus(); e.preventDefault(); }" & @CRLF & _
279+
" }" & @CRLF & _
280+
" }" & @CRLF & _
281+
" if (e.key === 'Enter') {" & @CRLF & _
282+
" if (document.activeElement.tagName === 'BUTTON') {" & @CRLF & _
283+
" document.activeElement.click();" & @CRLF & _
284+
" } else {" & @CRLF & _
285+
" uiAction('" & $sDefaultBtn & "');" & @CRLF & _
286+
" }" & @CRLF & _
287+
" }" & @CRLF & _
288+
" if (e.key === 'Escape') uiAction('0');" & @CRLF & _
289+
"});" & @CRLF & _
290+
"</script></body></html>"
291+
292+
_NetWebView2_NavigateToString($oWebV2M, $sHTML)
293+
$oWebV2M.LockWebView()
294+
EndFunc ;==>_RenderMarkdown
295+
;---------------------------------------------------------------------------------------
296+
Func _CSS()
297+
Local $hBCol, $sButtons = ""
298+
For $i = 1 To $g_aBtn[0]
299+
$hBCol = (StringLeft($g_aBtn[$i], 1) = "~" ? $g_sBtnDefColor : $g_sBtnColor)
300+
$sButtons &= ".btn-" & $i & " { background: #" & $hBCol & "; color: white; border: none; " & _
301+
(StringLeft($g_aBtn[$i], 1) = "~" ? " font-weight: bold; " : "") & "}"
302+
Next
303+
304+
Local $sStyle = ""
305+
; ΠΡΟΣΘΗΚΗ MARGIN-BOTTOM ΓΙΑ ΝΑ ΜΗΝ ΕΙΝΑΙ ΣΤΡΙΜΩΓΜΕΝΑ
306+
$sStyle &= "h1, h2, h3, h4, h5, h6 { margin: 0 0 15px 0; border-bottom: 1px solid #333; padding-bottom: 5px; color: #ffffff; }" & _
307+
"ul, ol { margin: 10px 0; padding-left: 25px; }" & _
308+
"li { margin-bottom: 5px; padding: 0; }" & _
309+
"#view p { margin-bottom: 10px; padding: 0; }" & _
310+
"a { color: #4da6ff; text-decoration: none; }"
311+
312+
$sStyle &= "body { font-family: 'Segoe UI', Tahoma, sans-serif; background: #" & $g_hBkColor & "; " & _
313+
"color: #" & $g_hTxtColor & "; margin: 0; padding: 0; overflow: hidden; outline: none; }" & _
314+
"#main-container { width: 100%; display: block; text-align: left; word-wrap: break-word; outline: none; }" & _
315+
"#view { padding: 20px; min-height: 50px; }" ; white-space: pre-wrap;
316+
317+
$sStyle &= ".footer { background: #" & $g_hFootColor & "; padding: 12px; text-align: right; " & _
318+
"border-top: 1px solid #333; width: 100%; box-sizing: border-box; }" & _
319+
".btn { padding: 8px 16px; margin-left: 6px; border-radius: 3px; cursor: pointer; font-size: 13px; " & _
320+
"transition: filter 0.2s; outline: none; }" & $sButtons & _
321+
".btn:hover { filter: brightness(1.2); } .btn:focus { outline: 2px solid #ffffff; outline-offset: 2px; }"
322+
323+
Return $sStyle
324+
EndFunc
325+
;---------------------------------------------------------------------------------------
326+
Func _EscapeForJS($sText)
327+
$sText = StringReplace($sText, "\", "\\")
328+
$sText = StringReplace($sText, "'", "\'")
329+
330+
$sText = StringReplace($sText, @CRLF & @CRLF, "\\n&nbsp;\\n")
331+
$sText = StringReplace($sText, " ", "&nbsp;&nbsp;")
332+
333+
$sText = StringReplace($sText, @CRLF, "\\n")
334+
$sText = StringReplace($sText, @CR, "\\n")
335+
$sText = StringReplace($sText, @LF, "\\n")
336+
Return "'" & $sText & "'"
337+
EndFunc ;==>_EscapeForJS
338+
339+
#CS Helper function for client script
340+
;---------------------------------------------------------------------------------------
341+
Func _FA($sIconName, $iStyle = 0, $hColor = "", $sEffect = "", $iSize = 0, $sExtra = "")
342+
; $iStyle: 0="fa-solid", 1="fa-regular"
343+
; $iSize: 0=normal, 1=lg, 2=2x, etc.
344+
; $sEffect: "" (none), "spin", "pulse", "beat", "fade"
345+
; $sExtra: "fa-rotate-90", "fa-flip-horizontal", "fa-border"
346+
347+
Local $sPrefix = ($iStyle ? "fa-regular" : "fa-solid")
348+
Local $sClass = $sPrefix & " fa-fw fa-" & $sIconName
349+
350+
If $sEffect <> "" Then $sClass &= " fa-" & $sEffect
351+
If $sExtra <> "" Then $sClass &= " " & $sExtra
352+
353+
If $iSize > 0 Then
354+
Local $sSizeSuffix = ($iSize = 1 ? "lg" : $iSize & "x")
355+
$sClass &= " fa-" & $sSizeSuffix
356+
EndIf
357+
358+
Local $sStyleAttr = "margin-right:5px; vertical-align: middle;"
359+
If $hColor <> "" Then $sStyleAttr &= "color:#" & Hex($hColor, 6) & ";"
360+
361+
Return "<i class='" & $sClass & "' style='" & $sStyleAttr & "'></i>"
362+
EndFunc ;==>_FA
363+
;---------------------------------------------------------------------------------------
364+
Func _FA_Stack($sBackHTML, $sFrontHTML, $iStackSize = 0)
365+
Local $sSizeClass = ($iStackSize = 1 ? " fa-lg" : ($iStackSize > 1 ? " fa-" & $iStackSize & "x" : ""))
366+
Local $sFinalBack = StringReplace($sBackHTML, "fa-fw", "fa-stack-2x")
367+
Local $sFinalFront = StringReplace($sFrontHTML, "fa-fw", "fa-stack-1x fa-inverse")
368+
Return "<span class='fa-stack" & $sSizeClass & "' style='vertical-align: middle; margin-right: 5px;'>" & _
369+
$sFinalBack & $sFinalFront & _
370+
"</span>"
371+
EndFunc ;==>_FA_Stack
372+
;---------------------------------------------------------------------------------------
373+
374+
=== mdMsgBox parameters with default values ===
375+
/BkColor:0x2B2B2B (Dark slate gray)
376+
/TxtColor:0xE0E0E0 (Gainsboro)
377+
/FootColor:0x1E1E1E (Black)
378+
/MaxWidth:400
379+
/MaxHeight:800
380+
/Left:-1
381+
/Top:-1
382+
/Title:"Markdown MsgBox"
383+
/Text:""
384+
/Buttons:"OK"
385+
/BtnDefColor:0xD73443 (Crimson)
386+
/BtnColor:0x559FF2 (Cornflower blue)
387+
/Timer:0
388+
/TopMost:0
389+
/Parent:0
390+
391+
#CE Helper function for client script

0 commit comments

Comments
 (0)