Gox語言——支持跨平臺原生GUI開發(fā)的輕量級全功能腳本語言 - GX1

Gox語言(官網(wǎng))是以Go語言(Golang)為基礎(chǔ)的解釋型/腳本語言,它除了具有一般腳本語言所具有的編寫快捷、語言簡潔、易于理解等特點外,還支持其他語言所不具備的跨平臺原生圖形界面(GUI)開發(fā),并且代碼寫起來非常舒暢。文檔參看這里。循序漸進學(xué)習(xí)Gox語言編程請參看這里Gox語言入門。

用Gox語言編程

Gox語言的主要特點包括:

  • 跨平臺,目前支持Windows、Mac和Linux等主流平臺;
  • 完全免費和開源,使用MIT授權(quán)協(xié)議;
  • 代碼基于Go語言(Golang),但做了一些優(yōu)化,因此Go語言、C語言、C++、C#、Java及類似語言的開發(fā)者編寫起來幾乎沒有任何壓力,稍加了解就可以開始編寫代碼;
  • 相較于Go語言嚴(yán)格的語法書寫要求和靜態(tài)數(shù)據(jù)類型限制,Gox語言實現(xiàn)了動態(tài)類型,并做了許多更接近主流高級語言的改進,去除了一些語法限制,使得代碼書寫上方便了很多;
  • 基本支持所有Go語言主要的標(biāo)準(zhǔn)庫,并加以擴充,并且理論上可以支持任意多的第三方擴展庫,依托Go語言社區(qū)已有的海量代碼庫,Gox語言具備成為全棧語言的潛力;
  • 提供大量內(nèi)置高效的網(wǎng)絡(luò)服務(wù)、數(shù)據(jù)庫訪問等常用編程能力,可以直接提供完整的Web服務(wù)器、API服務(wù)器,免驅(qū)動訪問SQLite、MySQL、SQL Server等數(shù)據(jù)庫,也可以操作Oracle數(shù)據(jù)庫;
  • 具備嵌套執(zhí)行腳本的能力,支持模塊化編程,支持比Go語言更方便的面向?qū)ο缶幊蹋?/li>
  • 與一般的解釋性/腳本語言不同,Gox語言自帶代碼加密功能,支持對發(fā)布的代碼進行加密,以及對加密代碼的解密執(zhí)行功能,可以有效地保護開發(fā)人員的工作;
  • 支持各操作系統(tǒng)下的編譯,可以單文件發(fā)布最終應(yīng)用程序,同時起到代碼保護作用;
  • 語法基于Qlang這種與Go語言能夠緊密互操作的語言,天然繼承了很多Go語言的優(yōu)秀特性,例如Go語言特色的并發(fā)線程goroutine和chan等;
  • 多腳本之間可以共享全局?jǐn)?shù)據(jù);
  • 支持跨平臺的原生圖形界面(GUI)的開發(fā),并且界面布局代碼書寫簡單易懂,使用通用的HTML/CSS/Script的形式制作界面;
  • 綠色,無安裝文件或安裝包,無需安裝和任何環(huán)境配置過程,沒有任何依賴,只需下載一個可執(zhí)行文件,即可實現(xiàn)所有Gox代碼開發(fā)和程序執(zhí)行的任務(wù)(注:如果使用Sciter編寫界面也只需要附帶一個動態(tài)鏈接庫文件即可,Windows版的壓縮包中已經(jīng)自帶);
  • 經(jīng)過實際項目檢驗,已經(jīng)應(yīng)用在很多中小型項目中,可用性與穩(wěn)定性經(jīng)受了足夠的考驗;
  • 更為神奇的是,這一個主程序文件竟然能夠同時支持命令行模式的開發(fā)和圖形界面(GUI)的開發(fā),并且還自帶交互式編程環(huán)境(REPL),甚至還內(nèi)置了一個圖形化的、支持代碼高亮和語法檢查的代碼編輯器!
Gox語言內(nèi)置的代碼編輯器

下面用一個例子來介紹一下用Gox編寫代碼的方便程度,這是一個實現(xiàn)了簡易計算器功能的代碼:

expression = getInput("Please enter the expression: ")

result = eval(expression)

println("result:", result)


僅僅4行代碼而已,實現(xiàn)的效果如下:

Gox語言實現(xiàn)的計算器

可以看到,4行語言就實現(xiàn)了一個命令行版本的簡易表達(dá)式計算器。

也可以直接在內(nèi)置的代碼編輯器中編輯執(zhí)行,直接運行帶參數(shù)的gox -edit命令即可,效果如下:

image.png

如果用GUI圖形界面來實現(xiàn)怎么樣呢?同樣的,只需要相對很簡單的代碼就能實現(xiàn):

sciter = github_scitersdk_gosciter
window = github_scitersdk_gosciter_window

initGUI()

htmlT := `
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>Calculator</title>
</head>
<body>
    <div style="margin-top: 10px; margin-bottom: 10px;">
        <span>Please enter the expression:</span>
    </div>
    <div style="margin-top: 10px; margin-bottom: 10px;">
        <input id="mainInputID" type=text />
    </div>
    <div>
        <button id="btnCal">Calculate!</button>
        <button id="btnClose">Close</button>
    </div>

    <script type="text/tiscript">
        $(#btnCal).on("click", function() {
        var result = eval($(#mainInputID).value);

        view.prints(String.printf("%v", result));

        $(#mainInputID).value = result;
        });
 
        $(#btnClose).on("click", function() {
            view.close();
        });
 
    </script>
</body>
</html>

`

runtime.LockOSThread()

w, err := window.New(sciter.DefaultWindowCreateFlag, sciter.DefaultRect)

checkError(err)

w.SetOption(sciter.SCITER_SET_SCRIPT_RUNTIME_FEATURES, sciter.ALLOW_EVAL|sciter.ALLOW_SYSINFO)

w.LoadHtml(htmlT, "")

w.SetTitle("Calculator")

w.DefineFunction("prints", func(args) {
    tk.Pl("%v", args[0].String())
    return sciter.NewValue("")
})

w.Show()

w.Run()

除去注釋和用于制作GUI界面的HTML代碼,有效代碼不超過20行(其中還包括用于輸出調(diào)試信息的代碼),就能夠?qū)崿F(xiàn)一個界面相當(dāng)不錯的圖形化計算器了,而且可以實現(xiàn)跨平臺!實際效果如下圖所示:

Gox實現(xiàn)的圖形化計算器

那么,更為令人嘆為觀止地,一個支持中文、跨平臺、功能齊全而又簡潔的代碼編輯器也不需要多少Gox代碼就可以實現(xiàn),運行效果如下圖所示,有效代碼僅需不到200行(請參見本文末尾所附代碼)。

簡易全功能代碼編輯器

由于僅是示例,篇幅所限,沒有實現(xiàn)代碼高亮和自動完成功能,但除此以外,一個代碼編輯器所需的所有主要功能都已經(jīng)實現(xiàn)了,甚至還包括代碼加密、解密和運行功能。


下面看看Gox語言的主要使用方式。

  • 首先,直接去Gox語言官網(wǎng)(gox.topget.org)或其Github/Gitee的release頁面(topxeq/gox)下載Gox語言的單一可執(zhí)行文件,將其放在一個目錄下(如果是下載的zip壓縮包,需要進行解壓縮);該目錄最好在操作系統(tǒng)可找到的目錄下(即加入PATH環(huán)境變量中),以便執(zhí)行;


    Gox語言官網(wǎng)
  • 直接在命令行模式下(例如Windows的命令提示符)運行g(shù)ox命令(或gox -shell)即可進入交互式編程環(huán)境,如下圖所示:


    Gox的交互式編程環(huán)境
  • 使用類似 gox -edit basic.gox 的命令可以啟動Gox的內(nèi)置編輯器編輯名為basic.gox的Gox代碼文件,而如果直接用 gox -edit 不帶文件名,則將打開一個新文件進行編輯;

  • 也可以在Gox交互式編程環(huán)境中直接用edit函數(shù)來啟動圖形化代碼編輯器:


    REPL中啟動Gox代碼編輯器
  • 運行Gox代碼,只需要執(zhí)行類似 gox basic.gox 的命令即可。

  • 使用類似 gox -encrypt=mycode basic.gox 的命令來對源代碼進行加密(mycode是密碼,將會生成一個名為basic.goxe的加密代碼文件),然后可以使用類似 gox -decrypt=mycode basic.goxe 的命令來對代碼進行解密,或者直接用 gox -decrun=mycode basic.goxe 命令來解密執(zhí)行源代碼,也可以直接 gox basic.goxe 執(zhí)行源代碼,此時將會要求先輸入密碼后才能執(zhí)行;

  • 除使用內(nèi)置的代碼編輯器外,也可以使用功能更為豐富的其他編輯器,例如Visual Studio Code就是一個很好的選擇,將.gox類型文件的文件的代碼高亮方案設(shè)置成與Go語言或C語言相同即可;

  • 可以用類似gox -compile basic.gox -output=basic.exe這樣的命令來將Gox代碼文件編譯,可以起到單文件發(fā)布和代碼加密的效果;

  • 進行圖形化編程時,需要使用Sciter庫,這也是經(jīng)過業(yè)界檢驗的一個第三方圖形庫,采用HTML/CSS/Script的方式進行頁面開發(fā),運行時只需要附帶一個動態(tài)鏈接庫文件即可,非常方便;如果不希望出現(xiàn)命令行窗口(例如Windows下的CMD),可以用Gox的窗口版本goxw.exe即可;

  • 更多的說明和代碼示例可以參考Github上Gox語言的代碼庫,github.com/topxeq/gox。


當(dāng)然,Gox語言目前也存在不足,我們要心里有數(shù),以便在使用Gox語言時清楚它更適合做什么和不太適合做什么。

目前,Gox語言最主要的不足就是:作為解釋型/腳本語言,Gox也具有這類語言的通病,就是執(zhí)行代碼的速度相對比較慢,比一些具有多年優(yōu)化積累的解釋型語言如Python、Java等也要更慢,但在大多數(shù)的應(yīng)用環(huán)境中已經(jīng)足夠使用,包括一些中型規(guī)模的網(wǎng)絡(luò)應(yīng)用和微服務(wù),這也是經(jīng)過實踐檢驗的。

另外,Gox語言雖然只有一個主程序,但相對來說比較大(有幾十M之多)。但這也比較好理解,畢竟還需要支持圖形化編程并自帶代碼編輯器??紤]到這一點,Gox語言也提供一個去除了圖形化編程功能的純命令行版本,文件大小會小一些,效率也會高一點。

第三,Gox語言如果要支持更多的第三方類庫,需要手動編譯源碼,但這點對于程序員來說應(yīng)該不是大問題。


總的來說,Gox語言雖然具備開發(fā)各種各種應(yīng)用場景的全棧開發(fā)語言的潛力,相對來說不太適合開發(fā)高密度計算類型和需要極高速響應(yīng)的程序和系統(tǒng);但它非常適合作為“膠水語言”來作為大型系統(tǒng)之間的粘合劑,也非常適合快速開發(fā)一些無需太高速度要求的功能系統(tǒng)(例如可以取代shell腳本的開發(fā)、進行各種復(fù)雜的文本處理、實現(xiàn)各種網(wǎng)絡(luò)編程、進行數(shù)據(jù)庫訪問、編寫一些圖形化操控界面)以及做一些快速原型演示等。


附——全功能簡易代碼編輯器源碼(該版本使用Sciter GUI圖形庫,使用時需要Sciter的動態(tài)鏈接庫,Windows下為sciter.dll,在Gox官網(wǎng)上的主程序壓縮包中已經(jīng)提供,使用gox -initgui命令也可以直接下載):

sciter = github_scitersdk_gosciter
window = github_scitersdk_gosciter_window

initGUI()

htmlT := `
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>Gox Editor</title>
    <style>
    
    plaintext {
      padding:0;
      flow:vertical;
      behavior:plaintext;
      background:#333; border:1px solid #333;
      color:white;
      overflow:scroll-indicator;
      font-rendering-mode:snap-pixel;
      size:*; 
      tab-size: 4;
    }
    plaintext > text {
      font-family:monospace;
      white-space: pre-wrap;
      background:white;
      color:black;
      margin-left: 3em;
      padding-left: 4dip;
      cursor:text;
      display:list-item;
      list-style-type: index;
      list-marker-color:#aaa;
    }
    plaintext > text:last-child {
      padding-bottom:*;
    }    
    
    plaintext > text:nth-child(10n) {
      list-marker-color:#fff;
    }
    
    
    </style>


    <script type="text/tiscript">
        function colorize() 
        {
            const apply = Selection.applyMark; 
            const isEditor = this.tag == "plaintext";
            
            // forward declarations:
            var doStyle;
            var doScript;

            // markup colorizer  
            function doMarkup(tz) 
            {
                    var bnTagStart = null;
                    var tagScript = false;
                    var tagScriptType = false;
                    var tagStyle = false;
                    var textElement;
                
                while(var tt = tz.token()) {
                if( isEditor && tz.element != textElement )       
                {
                    textElement = tz.element;
                    textElement.attributes["type"] = "markup";
                }
                switch(tt) {
                    case #TAG-START: {    
                        bnTagStart = tz.tokenStart; 
                        const tag = tz.tag;
                        tagScript = tag == "script";
                        tagStyle  = tag == "style";
                    } break;
                    case #TAG-HEAD-END: {
                        apply(bnTagStart,tz.tokenEnd,"tag"); 
                        if( tagScript ) { tz.push(#source,"</sc"+"ript>"); doScript(tz, tagScriptType, true); }
                        else if( tagStyle ) { tz.push(#source,"</style>"); doStyle(tz, true); }
                    } break;
                    case #TAG-END:      apply(tz.tokenStart,tz.tokenEnd,"tag"); break;  
                    case #TAG-ATTR:     if( tagScript && tz.attr == "type") tagScriptType = tz.value; 
                                        if( tz.attr == "id" ) apply(tz.tokenStart,tz.tokenEnd,"tag-id"); 
                                        break;
                }
                }
            }
            
            // script colorizer
            doScript = function(tz, typ, embedded = false) 
            {
                const KEYWORDS = 
                {
                "type"    :true, "function":true, "var"       :true,"if"       :true,
                "else"    :true, "while"   :true, "return"    :true,"for"      :true,
                "break"   :true, "continue":true, "do"        :true,"switch"   :true,
                "case"    :true, "default" :true, "null"      :true,"super"    :true,
                "new"     :true, "try"     :true, "catch"     :true,"finally"  :true,
                "throw"   :true, "typeof"  :true, "instanceof":true,"in"       :true,
                "property":true, "const"   :true, "get"       :true,"set"      :true,
                "include" :true, "like"    :true, "class"     :true,"namespace":true,
                "this"    :true, "assert"  :true, "delete"    :true,"otherwise":true,
                "with"    :true, "__FILE__":true, "__LINE__"  :true,"__TRACE__":true,
                "debug"   :true, "await"   :true 
                };
                
                const LITERALS = { "true": true, "false": true, "null": true, "undefined": true };
                
                var firstElement;
                var lastElement;
            
                while:loop(var tt = tz.token()) {
                var el = tz.element;
                if( !firstElement ) firstElement = el;
                lastElement = el;
                switch(tt) 
                {
                    case #NUMBER:       apply(tz.tokenStart,tz.tokenEnd,"number"); break; 
                    case #NUMBER-UNIT:  apply(tz.tokenStart,tz.tokenEnd,"number-unit"); break; 
                    case #STRING:       apply(tz.tokenStart,tz.tokenEnd,"string"); break;
                    case #NAME:         
                    {
                    var val = tz.value;
                    if( val[0] == '#' )
                        apply(tz.tokenStart,tz.tokenEnd, "symbol"); 
                    else if(KEYWORDS[val]) 
                        apply(tz.tokenStart,tz.tokenEnd, "keyword"); 
                    else if(LITERALS[val]) 
                        apply(tz.tokenStart,tz.tokenEnd, "literal"); 
                    break;
                    }
                    case #COMMENT:      apply(tz.tokenStart,tz.tokenEnd,"comment"); break;
                    case #END-OF-ISLAND:  
                    tz.pop(); //pop tokenizer layer
                    break loop;
                }
                }
                if(isEditor && embedded) {
                for( var el = firstElement; el; el = el.next ) {
                    el.attributes["type"] = "script";
                    if( el == lastElement )
                    break;
                }
                }
            };
            
            doStyle = function(tz, embedded = false) 
            {
                const KEYWORDS = 
                {
                "rgb":true, "rgba":true, "url":true, 
                "@import":true, "@media":true, "@set":true, "@const":true
                };
                
                const LITERALS = { "inherit": true };
                
                var firstElement;
                var lastElement;
                
                while:loop(var tt = tz.token()) {
                var el = tz.element;
                if( !firstElement ) firstElement = el;
                lastElement = el;
                switch(tt) 
                {
                    case #NUMBER:       apply(tz.tokenStart,tz.tokenEnd,"number"); break; 
                    case #NUMBER-UNIT:  apply(tz.tokenStart,tz.tokenEnd,"number-unit"); break; 
                    case #STRING:       apply(tz.tokenStart,tz.tokenEnd,"string"); break;
                    case #NAME:         
                    {
                    var val = tz.value;
                    if( val[0] == '#' )
                        apply(tz.tokenStart,tz.tokenEnd, "symbol"); 
                    else if(KEYWORDS[val]) 
                        apply(tz.tokenStart,tz.tokenEnd, "keyword"); 
                    else if(LITERALS[val]) 
                        apply(tz.tokenStart,tz.tokenEnd, "literal"); 
                    break;
                    }
                    case #COMMENT:      apply(tz.tokenStart,tz.tokenEnd,"comment"); break;
                    case #END-OF-ISLAND:  
                    // got </sc ript>
                    tz.pop(); //pop tokenizer layer
                    break loop;
                }
                }
                if(isEditor && embedded) {
                for( var el = firstElement; el; el = el.next ) {
                    el.attributes["type"] = "style";
                    if( el == lastElement )
                    break;
                }
                }
            };
            
            var me = this;
            
            function doIt() { 
            
                var typ = me.attributes["type"];

                var syntaxKind = typ like "*html" || typ like "*xml" ? #markup : #source;
                var syntax = typ like "*css"? #style : #script;
                
                var tz = new Tokenizer( me, syntaxKind );
            
                if( syntaxKind == #markup )
                doMarkup(tz);
                else if( syntax == #style )
                doStyle(tz);
                else 
                doScript(tz,typ);
            }
            
            doIt();
            
            // redefine value property
            this[#value] = property(v) {
                get { return this.state.value; }
                set { this.state.value = v; doIt(); }
            };
            
            this.load = function(text,sourceType) 
            {
                this.attributes["type"] = sourceType;
                if( !isEditor )
                text = text.replace(/\r\n/g,"\n"); 
                this.state.value = text; 
                doIt();
            };
            
            this.sourceType = property(v) {
                get { return this.attributes["type"]; }
                set { this.attributes["type"] = v; doIt(); }
            };
            if (isEditor)
                    this.on("change", function() {
                        this.timer(40ms,doIt);
                    });
            

        }
    </script>
    <style>

        @set colorizer < std-plaintext 
        {
            :root { aspect: colorize; }
            
            text { white-space:pre;  display:list-item; list-style-type: index; list-marker-color:#aaa; }
            /*markup*/  
            text::mark(tag) { color: olive; } /*background-color: #f0f0fa;*/
            text::mark(tag-id) { color: red; } /*background-color: #f0f0fa;*/

            /*source*/  
            text::mark(number) { color: brown; }
            text::mark(number-unit) { color: brown; }
            text::mark(string) { color: teal; }
            text::mark(keyword) { color: blue; }
            text::mark(symbol) { color: brown; }
            text::mark(literal) { color: brown; }
            text::mark(comment) { color: green; }
            
            text[type=script] {  background-color: #FFFAF0; }
            text[type=markup] {  background-color: #FFF;  }
            text[type=style]  {  background-color: #FAFFF0; }
        }

        plaintext[type] {
            style-set: colorizer;
        }

        @set element-colorizer 
        {
            :root { 
                aspect: colorize; 
                background-color: #fafaff;
                    padding:4dip;
                    border:1dip dashed #bbb;
                }
            
            /*markup*/  
            :root::mark(tag) { color: olive; } 
            :root::mark(tag-id) { color: red; }

            /*source*/  
            :root::mark(number) { color: brown; }
            :root::mark(number-unit) { color: brown; }
            :root::mark(string) { color: teal; }
            :root::mark(keyword) { color: blue; }
            :root::mark(symbol) { color: brown; }
            :root::mark(literal) { color: brown; }
            :root::mark(comment) { color: green; }
            }

            pre[type] {
            style-set: element-colorizer;
        }

    </style>
    <script type="text/tiscript">
        // if (view.connectToInspector) {
        //  view.connectToInspector(rootElement, inspectorIpAddress);
        // }

        function isErrStr(strA) {
            if (strA.substr(0, 6) == "TXERROR:") {
                return true;
            }

            return false;
        }

        function getErrStr(strA) {
            if (strA.substr(0, 6) == "TXERROR:") {
                return strA.substr(6);
            }

            return strA;
        }

        function getConfirm(titelA, msgA) {
            var result = view.msgbox { 
                type:#question,
                title: titelA,
                content: msgA, 
                buttons: [
                    {id:#yes,text:"Ok",role:"default-button"},
                    {id:#cancel,text:"Cancel",role:"cancel-button"}]                               
                };

            return result;
        }

        function showInfo(titelA, msgA) {
            var result = view.msgbox { 
                type:#information,
                title: titelA,
                content: msgA, 
                buttons: [
                    {id:#cancel,text:"Close",role:"cancel-button"}]                               
                };

            return result;
        }

        function showError(titelA, msgA) {
            var result = view.msgbox { 
                type:#alert,
                title: titelA,
                content: msgA, 
                buttons: [
                    {id:#cancel,text:"Close",role:"cancel-button"}]                               
                };

            return result;
        }

        function getScreenWH() {
            var (w, h) = view.screenBox(#frame, #dimension)

            view.move((w-800)/2, (h-600)/2, 800, 600);

            return String.printf("%v|%v", w, h);
        }

        var editFileNameG = "";
        var editFileCleanFlagG = "";

        function updateFileName() {
            $(#fileNameLabelID).html = (editFileNameG + editFileCleanFlagG);
        }

        function selectFileJS() {
            //var fn = view.selectFile(#open, "Gotx Files (*.gt,*.go)|*.gt;*.go|All Files (*.*)|*.*" , "gotx" );
            var fn = view.selectFile(#open);
            view.prints(String.printf("fn: %v", fn));

            if (fn == undefined) {
                return;
            }

            var fileNameT = URL.toPath(fn);

            var rs = view.loadStringFromFile(fileNameT);

            if (isErrStr(rs)) {
                showError("Error", String.printf("Failed to load file content: %v", getErrStr(rs)));
                return;
            }

            $(plaintext).attributes["type"] = "text/script";

            $(plaintext).value = rs;

            editFileNameG = fileNameT;

            editFileCleanFlagG = "";

            updateFileName();
        }

        function editFileLoadClick() {
            if (editFileCleanFlagG != "") {
            
                var rs = getConfirm("Please confirm", "File modified, load another file anyway?");

                if (rs != #yes) {
                    return;
                }

            }

            selectFileJS();
        }

        function editFileSaveAsClick() {
            var fn = view.selectFile(#save);
            view.prints(String.printf("fn: %v", fn));

            if (fn == undefined) {
                return;
            }

            var fileNameT = URL.toPath(fn);

            var textT = $(plaintext).value;

            var rs = view.saveStringToFile(textT, fileNameT);

            if (isErrStr(rs)) {
                showError("Error", String.printf("Failed to save file content: %v", getErrStr(rs)));
                return;
            }

            editFileNameG = fileNameT;
            editFileCleanFlagG = "";
            updateFileName();

            showInfo("Info", "Saved.");

        }

        function editFileSaveClick() {
            if (editFileNameG == "") {
                editFileSaveAsClick();

                return;
            }

            var textT = $(plaintext).value;

            var rs = view.saveStringToFile(textT, editFileNameG);

            if (isErrStr(rs)) {
                showError("Error", String.printf("Failed to save file content: %v", getErrStr(rs)));
                return;
            }

            editFileCleanFlagG = "";
            updateFileName();

            showInfo("Info", "Saved.");
        }

        function editRunClick() {
            view.close();
        }

        function getInput(msgA) {
            var res = view.dialog({ 
                html: `+"`"+`
                <html>
                <body>
                  <center>
                      <div style="margin-top: 10px; margin-bottom: 10px;">
                          <span`+"`"+`msgA`+"`"+`span>
                      </div>
                      <div style="margin-top: 10px; margin-bottom: 10px;">
                          <input id="mainTextID" type="text" />
                      </div>
                      <div style="margin-top: 10px; margin-bottom: 10px;">
                          <input id="submitButtonID" type="button" value="Ok" />
                          <input id="cancelButtonID" type="button" value="Cancel" />
                      </div>
                  </center>
                  <script type="text/tiscript">
                      $(#submitButtonID).onClick = function() {
                          view.close($(#mainTextID).value);
                      };
  
                      $(#cancelButtonID).onClick = function() {
                          view.close();
                      };
                  </scr`+"`"+`+`+"`"+`ipt>
                </body>
                </html>
                `+"`"+`
              });
  
              return res;
          }

        event click $(#btnEncrypt)
        {
            var res = getInput("Secure Code");

            if (res == undefined) {
                return;
            }

            var sourceT = $(plaintext).value;

            var encStrT = view.encryptText(sourceT, res);
        
            if (isErrStr(encStrT)) {
                showError("Error", String.printf("failed to encrypt content: %v",getErrStr(encStrT)));
                return;
            }
        
            $(plaintext).value = "\/\/TXDEF#" + encStrT;
            editFileCleanFlagG = "*";
        }
    
        event click $(#btnDecrypt)
        {
            var res = getInput("Secure Code");

            if (res == undefined) {
                return;
            }

            var sourceT = $(plaintext).value;

            var encStrT = view.decryptText(sourceT, res);
        
            if (isErrStr(encStrT)) {
                showError("Error", String.printf("failed to decrypt content: %v",getErrStr(encStrT)));
                return;
            }
        
            $(plaintext).value = encStrT;
            editFileCleanFlagG = "*";
        }
    
        event click $(#btnRun)
        {
            var res = getInput("Arguments to pass to script")

            if (res == undefined) {
                return;
            }

            var rs = view.runScript($(plaintext).value, res);

            showInfo("Result", rs)
        }
    

        function editCloseClick() {
            view.close();
        }

        function editFile(fileNameA) {
            var fcT string;

            if (fileNameA == "") {
                editFileNameG = "";

                fcT = "";

                editFileCleanFlagG = "*";
            } else {
                editFileNameG = fileNameA;

                fcT = view.loadStringFromFile(fileNameA);

                editFileCleanFlagG = "";
            }

            $(plaintext).attributes["type"] = "text/script";

            $(plaintext).value = fcT;

            updateFileName();

        }

        function self.ready() {
            $(#btnLoad).onClick = editFileLoadClick;
            $(#btnSave).onClick = editFileSaveClick;
            $(#btnSaveAs).onClick = editFileSaveAsClick;
            // $(#btnEncrypt).onClick = editFEncryptClick;
            // $(#btnDecrypt).onClick = editDecryptClick;
            // $(#btnRun).onClick = editRunClick;
            $(#btnClose).onClick = editCloseClick;

            $(plaintext#source).onControlEvent = function(evt) {
                switch (evt.type) {
                    case Event.EDIT_VALUE_CHANGED:      
                        editFileCleanFlagG = "*";
                        updateFileName();
                        return true;
                }
            };

        }
    </script>

</head>
<body>
    <div style="margin-top: 10px; margin-bottom: 10px;"><span id="fileNameLabelID"></span></div>
    <div style="margin-top: 10px; margin-bottom: 10px;">
        <button id="btn1" style="display: none">Load...</button>
        <button id="btnLoad">Load</button>
        <button id="btnSave">Save</button>
        <button id="btnSaveAs">SaveAs</button>
        <button id="btnEncrypt">Encrypt</button>
        <button id="btnDecrypt">Decrypt</button>
        <button id="btnRun">Run</button>
        <button id="btnClose">Close</button>
    </div>
    <plaintext#source type="text/html" style="font-size: 1.2em;"></plaintext>

</body>
</html>
`

runtime.LockOSThread()

w, err := window.New(sciter.DefaultWindowCreateFlag, sciter.DefaultRect)

checkError(err)

w.SetOption(sciter.SCITER_SET_SCRIPT_RUNTIME_FEATURES, sciter.ALLOW_FILE_IO | sciter.ALLOW_SOCKET_IO | sciter.ALLOW_EVAL | sciter.ALLOW_SYSINFO)

w.LoadHtml(htmlT, "")

w.SetTitle("Gox Editor")

w.DefineFunction("prints", func(args) {
    tk.Pl("%v", args[0].String())
    return sciter.NewValue("")
})

w.DefineFunction("loadStringFromFile", func(args) {
    rs := tk.LoadStringFromFile(args[0].String())
    return sciter.NewValue(rs)
})

w.DefineFunction("saveStringToFile", func(args) {
    rs := tk.SaveStringToFile(args[0].String(), args[1].String())
    return sciter.NewValue(rs)
})

w.DefineFunction("encryptText", func(args) {
    rs := tk.EncryptStringByTXDEF(args[0].String(), args[1].String())
    return sciter.NewValue(rs)
})

w.DefineFunction("decryptText", func(args) {
    rs := tk.DecryptStringByTXDEF(args[0].String(), args[1].String())
    return sciter.NewValue(rs)
})

w.DefineFunction("runScript", func(args) {
    rs := runScript(args[0].String(), "", args[1].String())
    return sciter.NewValue(spr("%v", rs))
})

w.DefineFunction("exit", func(args) {
    os.Exit(1);
})

data, _ := w.Call("getScreenWH")

fileNameT := getParam(os.Args, 2, "")

w.Call("editFile", sciter.NewValue(fileNameT))

w.Show()

w.Run()


另外,Ubuntu也需要安裝GTK3的環(huán)境,執(zhí)行下述命令即可安裝:

apt install libgtk-3-dev
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容