Gox語言(官網(wǎng))是以Go語言(Golang)為基礎(chǔ)的解釋型/腳本語言,它除了具有一般腳本語言所具有的編寫快捷、語言簡潔、易于理解等特點外,還支持其他語言所不具備的跨平臺原生圖形界面(GUI)開發(fā),并且代碼寫起來非常舒暢。文檔參看這里。循序漸進學(xué)習(xí)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編寫代碼的方便程度,這是一個實現(xiàn)了簡易計算器功能的代碼:
expression = getInput("Please enter the expression: ")
result = eval(expression)
println("result:", result)
僅僅4行代碼而已,實現(xiàn)的效果如下:

可以看到,4行語言就實現(xiàn)了一個命令行版本的簡易表達(dá)式計算器。
也可以直接在內(nèi)置的代碼編輯器中編輯執(zhí)行,直接運行帶參數(shù)的gox -edit命令即可,效果如下:

如果用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),運行效果如下圖所示,有效代碼僅需不到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


