WYSIWYG HTML editor (Windows)

Share your advanced PureBasic knowledge/code with the community.
firace
Addict
Addict
Posts: 946
Joined: Wed Nov 09, 2011 8:58 am

WYSIWYG HTML editor (Windows)

Post by firace »

Experimental, for simple HTML content.
Use as a starting point - adapt to your needs!

Code: Select all


code$ + "<link href=https://cdn.quilljs.com/1.3.6/quill.snow.css rel=stylesheet>" + #CRLF$ 
code$ + "" + #CRLF$ 
code$ + "<div style=height:400px id=editor>" + #CRLF$ 
code$ + "  <p>Hello World!</p>" + #CRLF$ 
code$ + "  <p>Some initial <strong>bold</strong> text</p><p><em style=color:red;>Open</em>Window(0, 100, 100)</p>" + #CRLF$ 
code$ + "  <p><br></p>" + #CRLF$ 
code$ + "</div>" + #CRLF$ 
code$ + "" + #CRLF$ 
code$ + "<script src=https://cdn.quilljs.com/1.3.6/quill.js></script>" + #CRLF$ 
code$ + "" + #CRLF$ 
code$ + "<script>" + #CRLF$ 
code$ + "var toolbarOptions = [" + #CRLF$ 
code$ + "  ['bold', 'italic', 'underline', 'strike'],        // toggled buttons" + #CRLF$ 
code$ + "  ['blockquote', 'code-block']," + #CRLF$ 
code$ + "" + #CRLF$ 
code$ + "  [{ 'header': 1 }, { 'header': 2 }],               // custom button values" + #CRLF$ 
code$ + "  [{ 'list': 'ordered'}, { 'list': 'bullet' }]," + #CRLF$ 
code$ + "  [{ 'indent': '-1'}, { 'indent': '+1' }],          // outdent/indent" + #CRLF$ 
code$ + "" + #CRLF$ 
code$ + "  [{ 'size': ['small', false, 'large', 'huge'] }],  // custom dropdown" + #CRLF$ 
code$ + "  ['link', 'image']," + #CRLF$ 
code$ + "" + #CRLF$ 
code$ + "  [{ 'color': [] }, { 'background': [] }],          // dropdown with defaults from theme" + #CRLF$ 
code$ + "  [{ 'font': [] }]," + #CRLF$ 
code$ + "  [{ 'align': [] }]," + #CRLF$ 
code$ + "" + #CRLF$ 
code$ + "  ['clean']                                         // remove formatting button" + #CRLF$ 
code$ + "];" + #CRLF$ 
code$ + "" + #CRLF$ 
code$ + "var quill = new Quill('#editor', {" + #CRLF$ 
code$ + "  modules: {" + #CRLF$ 
code$ + "    toolbar: toolbarOptions" + #CRLF$ 
code$ + "  }," + #CRLF$ 
code$ + "  theme: 'snow'" + #CRLF$ 
code$ + "});" + #CRLF$ 
code$ + "  " + #CRLF$ 
code$ + "</script>"

Define.s regkeyName, dwLabel, statusMsg, keyResult.i


regkeyName    = "Software\Microsoft\Internet Explorer\Main\FeatureControl\Feature_Browser_Emulation\"
dwLabel = GetFilePart(ProgramFilename())

dwValue = 11001   
RegOpenKeyEx_(#HKEY_CURRENT_USER, regkeyName,  0, #KEY_ALL_ACCESS, @keyResult) 
RegSetValueEx_(keyResult, @dwLabel, 0, #REG_DWORD, @dwValue, SizeOf(Long))


DataSection 
  IID_IHTMLDocument2: ; {332C4425-26CB-11D0-B483-00C04FD90119} 
  Data.l $332C4425 
  Data.w $26CB, $11D0 
  Data.b $B4, $83, $00, $C0, $4F, $D9, $01, $19 
EndDataSection 


Procedure.i WebGadget_GetHTMLDocument2 (nGadget)
  Protected oBrowser.IWebBrowser2 = GetWindowLongPtr_(GadgetID(nGadget), #GWL_USERDATA)
  Protected oDocumentDispatch.IDispatch
  Protected oHTMLDocument.IHTMLDocument2
  Protected iBusy
  
  Repeat
    While WindowEvent(): Delay(0): Wend    
    oBrowser\get_Busy(@iBusy): Delay(10)        
  Until iBusy = #VARIANT_FALSE
  
  If oBrowser
    If oBrowser\get_document(@oDocumentDispatch) = #S_OK 
      If oDocumentDispatch\QueryInterface(?IID_IHTMLDocument2, @oHTMLDocument) = #S_OK
        oDocumentDispatch\Release()
      EndIf
    EndIf
  EndIf
  
  ProcedureReturn oHTMLDocument 
EndProcedure

Procedure.i WebGadget_GetHTMLDocumentParent (nGadget)
  Protected oHTMLDocument.IHTMLDocument2 = WebGadget_GetHTMLDocument2 (nGadget)
  Protected oWindow.IHTMLWindow2
  
  If oHTMLDocument
    oHTMLDocument\get_parentWindow(@oWindow)
  EndIf
  
  oHTMLDocument\Release()  
  
  ProcedureReturn oWindow
EndProcedure 

Procedure WebGadget_ExecScript (nGadget, sScriptCode.s, sScriptLanguage.s = "JavaScript")    
  Protected oWindow.IHTMLWindow2 = WebGadget_GetHTMLDocumentParent (nGadget)
  Protected tVariant.VARIANT
  
  If oWindow
    oWindow\execScript (sScriptCode, sScriptLanguage, @tVariant)
    oWindow\Release()
  EndIf    
EndProcedure 

DataSection
  IID_IHTMLDocument: ; {626FC520-A41E-11CF-A731-00A0C9082637}
  Data.l $626FC520
  Data.w $A41E, $11CF
  Data.b $A7, $31, $00, $A0, $C9, $08, $26, $37
  
  IID_NULL: ; {00000000-0000-0000-0000-000000000000}
  Data.l $00000000
  Data.w $0000, $0000
  Data.b $00, $00, $00, $00, $00, $00, $00, $00       
EndDataSection



; >>> MakeBSTR <<<

CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
  Import ""
    MakeBSTR(str.p-unicode) As "_SysAllocString"
  EndImport
CompilerElse
  Import ""
    MakeBSTR(str.p-unicode) As "SysAllocString"
  EndImport
CompilerEndIf

; >>> StringFromVARIANT <<<

Procedure.s StringFromVARIANT(*var.VARIANT)
  
  If VariantChangeType_(*var, *var, $2, #VT_BSTR) = #S_OK
    Result$ = PeekS(*var\bstrVal, -1, #PB_Unicode)
    SysFreeString_(*var\bstrVal)
  Else
    Result$ = "ERROR : Cannot convert VARIANT to String!"
  EndIf
  
  ProcedureReturn Result$
EndProcedure


Procedure.s GetJSVariable(Gadget, Name$)
  Result$ = "ERROR" 
  
  Browser.IWebBrowser2 = GetWindowLong_(GadgetID(Gadget), #GWL_USERDATA)
  If Browser\get_Document(@DocumentDispatch.IDispatch) = #S_OK
    If DocumentDispatch\QueryInterface(?IID_IHTMLDocument, @Document.IHTMLDocument) = #S_OK
      If Document\get_Script(@Script.IDispatch) = #S_OK
        
        bstr_name = MakeBSTR(Name$)
        result = Script\GetIDsOfNames(?IID_NULL, @bstr_name, 1, 0, @dispID.l)
        If result = #S_OK
          
          params.DISPPARAMS\cArgs = 0
          params\cNamedArgs = 0        
          
          result = Script\Invoke(dispID, ?IID_NULL, 0, #DISPATCH_PROPERTYGET, @params, @varResult.VARIANT, 0, 0)
          If result = #S_OK
            Result$ = StringFromVARIANT(@varResult)
          Else
            Message$ = Space(3000)
            FormatMessage_(#FORMAT_MESSAGE_IGNORE_INSERTS|#FORMAT_MESSAGE_FROM_SYSTEM, 0, result, 0, @Message$, 3000, 0)          
            Result$ = "ERROR: Invoke() "+Message$            
          EndIf
          
        Else
          Message$ = Space(3000)
          FormatMessage_(#FORMAT_MESSAGE_IGNORE_INSERTS|#FORMAT_MESSAGE_FROM_SYSTEM, 0, result, 0, @Message$, 3000, 0)          
          Result$ = "ERROR: GetIDsOfNames() "+Message$          
          
        EndIf
        SysFreeString_(bstr_name)
        
        Script\Release()
      EndIf
      Document\Release()
    EndIf
    DocumentDispatch\Release()
  EndIf
  
  ProcedureReturn Result$
EndProcedure


Enumeration
  #Gadget_Web
  #Gadget_Text
  #Gadget_Button
  #Gadget_Button2
EndEnumeration






OpenWindow(0, 0, 0, 1000, 600, "WYSIWYG", #PB_Window_ScreenCentered|#PB_Window_MinimizeGadget)

WebGadget(#Gadget_Web, 5, 5, 990, 530, "")    

SetGadgetItemText(#Gadget_Web, #PB_Web_HtmlCode, code$)

ButtonGadget(#Gadget_Button2, 440, 550, 210, 30, "Save as HTML")

Repeat
  Event = WaitWindowEvent()
  
  If Event = #PB_Event_Gadget And EventGadget() = #Gadget_Button2
    script.s = "var c=document.getElementsByClassName('ql-editor')[0].innerHTML;" 
    WebGadget_ExecScript (#Gadget_Web, script.s) 
    res$ = GetJSVariable(#Gadget_Web,"c")
    res$ = "<head><meta http-equiv='X-UA-Compatible' content='IE=edge' /><link href=https://cdn.quilljs.com/1.3.6/quill.snow.css rel=stylesheet></head><span class='ql-editor'>" + res$
    
    filename$ = SaveFileRequester("Save HTML", GetUserDirectory(#PB_Directory_Desktop), "HTML (*.html)", 0)
    if not FindString(filename$, ".") : filename$ + ".html": endif 
    OpenFile(1, filename$) : WriteString(1, res$) : CloseFile(1)
  EndIf
  
Until Event = #PB_Event_CloseWindow
Last edited by firace on Mon Oct 30, 2023 10:30 am, edited 6 times in total.
BarryG
Addict
Addict
Posts: 4173
Joined: Thu Apr 18, 2019 8:17 am

Re: WYSIWYG HTML editor (Windows)

Post by BarryG »

Wow, this is impressive! Thanks for sharing.
Oso
Enthusiast
Enthusiast
Posts: 595
Joined: Wed Jul 20, 2022 10:09 am

Re: WYSIWYG HTML editor (Windows)

Post by Oso »

It looks very good indeed. Do you plan to be able to open existing HTML content, firace?

Just bearing in mind that the default HTML code in fact contains the icon section, so that has to be preserved, regardless of what's being edited as new content.

What about the editing, are you relying on Quill to do that?
User avatar
jacdelad
Addict
Addict
Posts: 2010
Joined: Wed Feb 03, 2021 12:46 pm
Location: Riesa

Re: WYSIWYG HTML editor (Windows)

Post by jacdelad »

This is really cool. And so tiny!
Good morning, that's a nice tnetennba!

PureBasic 6.21/Windows 11 x64/Ryzen 7900X/32GB RAM/3TB SSD
Synology DS1821+/DX517, 130.9TB+50.8TB+2TB SSD
User avatar
ChrisR
Addict
Addict
Posts: 1466
Joined: Sun Jan 08, 2017 10:27 pm
Location: France

Re: WYSIWYG HTML editor (Windows)

Post by ChrisR »

Nicely done, a simple and effective little WYSIWYG HTML editor and so tiny indeed, thank you for sharing :)
Rinzwind
Enthusiast
Enthusiast
Posts: 690
Joined: Wed Mar 11, 2009 4:06 pm
Location: NL

Re: WYSIWYG HTML editor (Windows)

Post by Rinzwind »

Read as Quill js text editor
firace
Addict
Addict
Posts: 946
Joined: Wed Nov 09, 2011 8:58 am

Re: WYSIWYG HTML editor (Windows)

Post by firace »

Yes this is built with Quill which does the editing work (didn't feel like reinventing the wheel)
and PB captures the resulting HTML through the webgadget and some API tricks
firace
Addict
Addict
Posts: 946
Joined: Wed Nov 09, 2011 8:58 am

Re: WYSIWYG HTML editor (Windows)

Post by firace »

Fixed some bugs - updated code in the top post.
firace
Addict
Addict
Posts: 946
Joined: Wed Nov 09, 2011 8:58 am

Re: WYSIWYG HTML editor (Windows)

Post by firace »

[removed obsolete post]
Last edited by firace on Sun Nov 12, 2023 2:41 pm, edited 1 time in total.
firace
Addict
Addict
Posts: 946
Joined: Wed Nov 09, 2011 8:58 am

Re: WYSIWYG HTML editor (Windows)

Post by firace »

I just created a little appplication based on this approach - see here:

viewtopic.php?t=82852
Last edited by firace on Fri Nov 17, 2023 5:07 pm, edited 1 time in total.
ricardo
Addict
Addict
Posts: 2438
Joined: Fri Apr 25, 2003 7:06 pm
Location: Argentina

Re: WYSIWYG HTML editor (Windows)

Post by ricardo »

Hi,

Very nice !!

One question, can you add that we can trigger some purebasic from some event (click a button oper example)?
ARGENTINA WORLD CHAMPION
firace
Addict
Addict
Posts: 946
Joined: Wed Nov 09, 2011 8:58 am

Re: WYSIWYG HTML editor (Windows)

Post by firace »

ricardo wrote: Fri Nov 17, 2023 3:42 am Hi,

Very nice !!

One question, can you add that we can trigger some purebasic from some event (click a button oper example)?
Thanks ricardo. Can you clarify what you mean?
It's possible to trigger PB code from HTML using the CreateExternalDispatch / ScriptRaiseEventCallback method, but I think you already know this :)
ricardo
Addict
Addict
Posts: 2438
Joined: Fri Apr 25, 2003 7:06 pm
Location: Argentina

Re: WYSIWYG HTML editor (Windows)

Post by ricardo »

firace wrote: Fri Nov 17, 2023 5:06 pm
ricardo wrote: Fri Nov 17, 2023 3:42 am Hi,

Very nice !!

One question, can you add that we can trigger some purebasic from some event (click a button oper example)?
Thanks ricardo. Can you clarify what you mean?
It's possible to trigger PB code from HTML using the CreateExternalDispatch / ScriptRaiseEventCallback method, but I think you already know this :)
Yes, i know how to do it.

I was tired when i write this, please forget about it and don't tell anybody :)

Thank you for your nice code !!
ARGENTINA WORLD CHAMPION
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5494
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: WYSIWYG HTML editor (Windows)

Post by Kwai chang caine »

Really nice code, that I hadn't seen :oops:
Thanks for sharing 8)
ImageThe happiness is a road...
Not a destination
firace
Addict
Addict
Posts: 946
Joined: Wed Nov 09, 2011 8:58 am

Re: WYSIWYG HTML editor (Windows)

Post by firace »

Kwai chang caine wrote: Sun Dec 03, 2023 9:32 pm Really nice code, that I hadn't seen :oops:
Thanks for sharing 8)
Thanks!

You can also look at this version, which is (slightly) more advanced:
https://www.purebasic.fr/english/viewtopic.php?t=82852
Post Reply