├── .gitignore ├── .travis.yml ├── AUTHORS ├── LICENSE ├── README.md ├── doc ├── css │ ├── shCoreDefault.css │ └── table.css ├── js │ ├── shBrushGo.js │ └── shCore.js └── manual_rus.html ├── extmain.go ├── extmenu.go ├── extwidg.go ├── go.mod └── testdata ├── forms ├── example.xml └── testget2.xml ├── images ├── book.bmp ├── cl_fl.bmp └── op_fl.bmp ├── test.bat ├── test.ini ├── test1.go ├── test2.go ├── test3.go ├── test_form1.go └── test_form2.go /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | *.exe 3 | *.log 4 | *.jpg 5 | *.pdf 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | sudo: false 3 | go: 4 | - "1.10" 5 | script: go build ./ 6 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the official list of External authors for copyright purposes. 2 | 3 | Alexander S.Kresin 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 The External Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # external 2 | 3 | [![GoDoc](https://godoc.org/github.com/alkresin/external?status.svg)](https://godoc.org/github.com/alkresin/external) 4 | 5 | External is a GUI library for Go (Golang), based on connection to external GUI server application. 6 | The connection can be esstablished via tcp/ip sockets or via regular files. 7 | To use it you need to have the GuiServer executable, which may be compiled from sources, hosted in https://github.com/alkresin/guiserver, or downloaded from http://www.kresin.ru/en/guisrv.html. 8 | Join the multilanguage group https://groups.google.com/d/forum/guiserver to discuss the GuiServer, External and related issues. 9 | 10 | 11 | To get rid of a console window, *use -ldflags "-H windowsgui"* option in *go build* statement for your application. 12 | 13 | -------------------- 14 | Alexander S.Kresin 15 | http://www.kresin.ru/ 16 | mailto: alkresin@yahoo.com 17 | 18 | Attention! Since October 6, 2023, we have been forced to use two-factor identification in order to 19 | log in to github.com under your account. I can still do git push from the command line, but I can't 20 | use other services, for example, to answer questions. That's why I'm opening new projects on 21 | https://gitflic.ru /, Sourceforge, or somewhere else. Follow the news on my website http://www.kresin.ru/ 22 | 23 | Внимание! С 6 октября 2023 года нас вынуждили использовать двухфакторную идентификацию для того, чтобы 24 | входить на github.com под своим аккаунтом. Я пока могу делать git push из командной строки, но не могу 25 | использовать другие сервисы, например, отвечать на вопросы. Поэтому новые проекты я открываю на 26 | https://gitflic.ru/, Sourceforge, или где-то еще. Следите за новостями на моем сайте http://www.kresin.ru/ 27 | -------------------------------------------------------------------------------- /doc/css/shCoreDefault.css: -------------------------------------------------------------------------------- 1 | /** 2 | * SyntaxHighlighter 3 | * http://alexgorbatchev.com/SyntaxHighlighter 4 | * 5 | * SyntaxHighlighter is donationware. If you are using it, please donate. 6 | * http://alexgorbatchev.com/SyntaxHighlighter/donate.html 7 | * 8 | * @version 9 | * 3.0.83 (July 02 2010) 10 | * 11 | * @copyright 12 | * Copyright (C) 2004-2010 Alex Gorbatchev. 13 | * 14 | * @license 15 | * Dual licensed under the MIT and GPL licenses. 16 | */ 17 | .syntaxhighlighter a, 18 | .syntaxhighlighter div, 19 | .syntaxhighlighter code, 20 | .syntaxhighlighter table, 21 | .syntaxhighlighter table td, 22 | .syntaxhighlighter table tr, 23 | .syntaxhighlighter table tbody, 24 | .syntaxhighlighter table thead, 25 | .syntaxhighlighter table caption, 26 | .syntaxhighlighter textarea { 27 | -moz-border-radius: 0 0 0 0 !important; 28 | -webkit-border-radius: 0 0 0 0 !important; 29 | background: none !important; 30 | border: 0 !important; 31 | bottom: auto !important; 32 | float: none !important; 33 | height: auto !important; 34 | left: auto !important; 35 | line-height: 1.1em !important; 36 | margin: 0 !important; 37 | outline: 0 !important; 38 | overflow: visible !important; 39 | padding: 0 !important; 40 | position: static !important; 41 | right: auto !important; 42 | text-align: left !important; 43 | top: auto !important; 44 | vertical-align: baseline !important; 45 | width: auto !important; 46 | box-sizing: content-box !important; 47 | font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important; 48 | font-weight: normal !important; 49 | font-style: normal !important; 50 | font-size: 1em !important; 51 | min-height: inherit !important; 52 | min-height: auto !important; 53 | } 54 | 55 | .syntaxhighlighter { 56 | width: 100% !important; 57 | margin: 1em 0 1em 0 !important; 58 | position: relative !important; 59 | overflow: auto !important; 60 | font-size: 1em !important; 61 | } 62 | .syntaxhighlighter.source { 63 | overflow: hidden !important; 64 | } 65 | .syntaxhighlighter .bold { 66 | font-weight: bold !important; 67 | } 68 | .syntaxhighlighter .italic { 69 | font-style: italic !important; 70 | } 71 | .syntaxhighlighter .line { 72 | white-space: pre !important; 73 | } 74 | .syntaxhighlighter table { 75 | width: 100% !important; 76 | } 77 | .syntaxhighlighter table caption { 78 | text-align: left !important; 79 | padding: .5em 0 0.5em 1em !important; 80 | } 81 | .syntaxhighlighter table td.code { 82 | width: 100% !important; 83 | } 84 | .syntaxhighlighter table td.code .container { 85 | position: relative !important; 86 | } 87 | .syntaxhighlighter table td.code .container textarea { 88 | box-sizing: border-box !important; 89 | position: absolute !important; 90 | left: 0 !important; 91 | top: 0 !important; 92 | width: 100% !important; 93 | height: 100% !important; 94 | border: none !important; 95 | background: white !important; 96 | padding-left: 1em !important; 97 | overflow: hidden !important; 98 | white-space: pre !important; 99 | } 100 | .syntaxhighlighter table td.gutter .line { 101 | text-align: right !important; 102 | padding: 0 0.5em 0 1em !important; 103 | } 104 | .syntaxhighlighter table td.code .line { 105 | padding: 0 1em !important; 106 | } 107 | .syntaxhighlighter.nogutter td.code .container textarea, .syntaxhighlighter.nogutter td.code .line { 108 | padding-left: 0em !important; 109 | } 110 | .syntaxhighlighter.show { 111 | display: block !important; 112 | } 113 | .syntaxhighlighter.collapsed table { 114 | display: none !important; 115 | } 116 | .syntaxhighlighter.collapsed .toolbar { 117 | padding: 0.1em 0.8em 0em 0.8em !important; 118 | font-size: 1em !important; 119 | position: static !important; 120 | width: auto !important; 121 | height: auto !important; 122 | } 123 | .syntaxhighlighter.collapsed .toolbar span { 124 | display: inline !important; 125 | margin-right: 1em !important; 126 | } 127 | .syntaxhighlighter.collapsed .toolbar span a { 128 | padding: 0 !important; 129 | display: none !important; 130 | } 131 | .syntaxhighlighter.collapsed .toolbar span a.expandSource { 132 | display: inline !important; 133 | } 134 | .syntaxhighlighter .toolbar { 135 | position: absolute !important; 136 | right: 1px !important; 137 | top: 1px !important; 138 | width: 11px !important; 139 | height: 11px !important; 140 | font-size: 10px !important; 141 | z-index: 10 !important; 142 | } 143 | .syntaxhighlighter .toolbar span.title { 144 | display: inline !important; 145 | } 146 | .syntaxhighlighter .toolbar a { 147 | display: block !important; 148 | text-align: center !important; 149 | text-decoration: none !important; 150 | padding-top: 1px !important; 151 | } 152 | .syntaxhighlighter .toolbar a.expandSource { 153 | display: none !important; 154 | } 155 | .syntaxhighlighter.ie { 156 | font-size: .9em !important; 157 | padding: 1px 0 1px 0 !important; 158 | } 159 | .syntaxhighlighter.ie .toolbar { 160 | line-height: 8px !important; 161 | } 162 | .syntaxhighlighter.ie .toolbar a { 163 | padding-top: 0px !important; 164 | } 165 | .syntaxhighlighter.printing .line.alt1 .content, 166 | .syntaxhighlighter.printing .line.alt2 .content, 167 | .syntaxhighlighter.printing .line.highlighted .number, 168 | .syntaxhighlighter.printing .line.highlighted.alt1 .content, 169 | .syntaxhighlighter.printing .line.highlighted.alt2 .content { 170 | background: none !important; 171 | } 172 | .syntaxhighlighter.printing .line .number { 173 | color: #bbbbbb !important; 174 | } 175 | .syntaxhighlighter.printing .line .content { 176 | color: black !important; 177 | } 178 | .syntaxhighlighter.printing .toolbar { 179 | display: none !important; 180 | } 181 | .syntaxhighlighter.printing a { 182 | text-decoration: none !important; 183 | } 184 | .syntaxhighlighter.printing .plain, .syntaxhighlighter.printing .plain a { 185 | color: black !important; 186 | } 187 | .syntaxhighlighter.printing .comments, .syntaxhighlighter.printing .comments a { 188 | color: #008200 !important; 189 | } 190 | .syntaxhighlighter.printing .string, .syntaxhighlighter.printing .string a { 191 | color: blue !important; 192 | } 193 | .syntaxhighlighter.printing .keyword { 194 | color: #006699 !important; 195 | font-weight: bold !important; 196 | } 197 | .syntaxhighlighter.printing .preprocessor { 198 | color: gray !important; 199 | } 200 | .syntaxhighlighter.printing .variable { 201 | color: #aa7700 !important; 202 | } 203 | .syntaxhighlighter.printing .value { 204 | color: #009900 !important; 205 | } 206 | .syntaxhighlighter.printing .functions { 207 | color: #006699 !important; 208 | } 209 | .syntaxhighlighter.printing .constants { 210 | color: #0066cc !important; 211 | } 212 | .syntaxhighlighter.printing .script { 213 | font-weight: bold !important; 214 | } 215 | .syntaxhighlighter.printing .color1, .syntaxhighlighter.printing .color1 a { 216 | color: gray !important; 217 | } 218 | .syntaxhighlighter.printing .color2, .syntaxhighlighter.printing .color2 a { 219 | color: #ff1493 !important; 220 | } 221 | .syntaxhighlighter.printing .color3, .syntaxhighlighter.printing .color3 a { 222 | color: red !important; 223 | } 224 | .syntaxhighlighter.printing .break, .syntaxhighlighter.printing .break a { 225 | color: black !important; 226 | } 227 | 228 | .syntaxhighlighter { 229 | background-color: white !important; 230 | } 231 | .syntaxhighlighter .line.alt1 { 232 | background-color: white !important; 233 | } 234 | .syntaxhighlighter .line.alt2 { 235 | background-color: white !important; 236 | } 237 | .syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 { 238 | background-color: #e0e0e0 !important; 239 | } 240 | .syntaxhighlighter .line.highlighted.number { 241 | color: black !important; 242 | } 243 | .syntaxhighlighter table caption { 244 | color: black !important; 245 | } 246 | .syntaxhighlighter .gutter { 247 | color: #afafaf !important; 248 | } 249 | .syntaxhighlighter .gutter .line { 250 | border-right: 3px solid #6ce26c !important; 251 | } 252 | .syntaxhighlighter .gutter .line.highlighted { 253 | background-color: #6ce26c !important; 254 | color: white !important; 255 | } 256 | .syntaxhighlighter.printing .line .content { 257 | border: none !important; 258 | } 259 | .syntaxhighlighter.collapsed { 260 | overflow: visible !important; 261 | } 262 | .syntaxhighlighter.collapsed .toolbar { 263 | color: blue !important; 264 | background: white !important; 265 | border: 1px solid #6ce26c !important; 266 | } 267 | .syntaxhighlighter.collapsed .toolbar a { 268 | color: blue !important; 269 | } 270 | .syntaxhighlighter.collapsed .toolbar a:hover { 271 | color: red !important; 272 | } 273 | .syntaxhighlighter .toolbar { 274 | color: white !important; 275 | background: #6ce26c !important; 276 | border: none !important; 277 | } 278 | .syntaxhighlighter .toolbar a { 279 | color: white !important; 280 | } 281 | .syntaxhighlighter .toolbar a:hover { 282 | color: black !important; 283 | } 284 | .syntaxhighlighter .plain, .syntaxhighlighter .plain a { 285 | color: black !important; 286 | } 287 | .syntaxhighlighter .comments, .syntaxhighlighter .comments a { 288 | color: #008200 !important; 289 | } 290 | .syntaxhighlighter .string, .syntaxhighlighter .string a { 291 | color: blue !important; 292 | } 293 | .syntaxhighlighter .keyword { 294 | color: #006699 !important; 295 | } 296 | .syntaxhighlighter .preprocessor { 297 | color: gray !important; 298 | } 299 | .syntaxhighlighter .variable { 300 | color: #aa7700 !important; 301 | } 302 | .syntaxhighlighter .value { 303 | color: #009900 !important; 304 | } 305 | .syntaxhighlighter .functions { 306 | color: #006699 !important; 307 | } 308 | .syntaxhighlighter .constants { 309 | color: #0066cc !important; 310 | } 311 | .syntaxhighlighter .script { 312 | font-weight: bold !important; 313 | color: #006699 !important; 314 | background-color: none !important; 315 | } 316 | .syntaxhighlighter .color1, .syntaxhighlighter .color1 a { 317 | color: gray !important; 318 | } 319 | .syntaxhighlighter .color2, .syntaxhighlighter .color2 a { 320 | color: #ff1493 !important; 321 | } 322 | .syntaxhighlighter .color3, .syntaxhighlighter .color3 a { 323 | color: red !important; 324 | } 325 | 326 | .syntaxhighlighter .keyword { 327 | font-weight: bold !important; 328 | } 329 | -------------------------------------------------------------------------------- /doc/css/table.css: -------------------------------------------------------------------------------- 1 | 2 | #header { background-color: #053061; border-bottom: 1px solid #fff; padding: 20px 0 0; } 3 | #header > div { width: 940px; line-height: 24px; margin: 0 auto; padding: 0 10px; } 4 | #logo { color: #ffc947; font-size: 40px; } 5 | #logo .subt { color: #fff; font-size: 20px; } 6 | 7 | #lang { font-size: 14px; } 8 | #lang .link { color: #fff; text-decoration: none; } 9 | #lang .sel { color: #ffc947; } 10 | 11 | #body { background: #818E9F; } 12 | #content { 13 | width: 82%; 14 | padding: 10px; 15 | margin: 0 auto; 16 | font-family: Verdana, Arial; 17 | font-size: 16px; 18 | background-color: #fff; box-shadow: 0 1px 4px rgba(0, 0, 0, .3), -23px 0 20px -23px rgba(0, 0, 0, .8), 23px 0 20px -23px rgba(0, 0, 0, .8), 0 0 40px rgba(0, 0, 0, .1) inset; 19 | } 20 | #content p { margin: 8px; } 21 | #content h1 { margin: 0; } 22 | @media screen and (max-width: 1100px) { 23 | #content { 24 | width: 94%; 25 | } 26 | } 27 | 28 | /* table.tabl1 { margin-left: auto; margin-right: auto; } */ 29 | table.tabl1 tr > td { padding: 4px; padding-left:12px } 30 | table.tabl1 tr > td:first-child { font-style: italic; } 31 | table.tabl1 tr:nth-child(odd){background-color: #f2f2f2;} 32 | 33 | table.tabl2, table.tabl2 td { border: 1px solid #ddd; border-collapse: collapse; padding: 4px; } 34 | table.tabl2 tr > td { padding-left:12px; padding-right:8px } 35 | table.tabl2 tr:nth-child(odd){background-color: #f2f2f2;} 36 | 37 | #footer { background-color: #053061; color: #ab7d0f; border-top: 1px solid #fff; padding: 28px 0 14px; } 38 | #footer p { font-size: 12px; line-height: 18px; margin: 0; } 39 | #footer > div { width: 940px; margin: 0 auto; padding: 0 10px; } 40 | #footer a { color:#fff; text-decoration:none; } 41 | #footer a:hover { text-decoration:underline; } 42 | 43 | .shad1 { box-shadow: 6px 8px 5px 0px #888; } 44 | 45 | blockquote { color: #101310; background-color: #e5e5e5; font-size: 1em; border: 1px solid #050505; padding: .5em margin: 0 2em; } 46 | 47 | /* Old */ 48 | .code-sample { background-color: #e5e5e5; border: 1px solid #050505; } 49 | .sidebar { background-color: #fff; border-right: 1px solid #050505; border-bottom: 1px solid #050505; } 50 | .title {font-size: 120%; color: #3333CC} 51 | .subtitle {font-size: 110%; color: #3333CC} 52 | .subhead {font-size: 110%} 53 | 54 | .hdr { font:110%; font-family: "Arial Cyr"; font-weight: bold; color: #ffffff; ;background-color: #4777D6 } 55 | .dt { font-weight: bold } 56 | 57 | a:Link{ Color: #0044ff; text-decoration: none; font-family: Arial; } 58 | a:Visited{ Color: #0088ee; text-decoration: none; font-family: Arial; } 59 | a:Hover{ Color: #000088; text-decoration: underline; font-family: Arial; } 60 | p { text-indent: 0.3in } 61 | pre.code { color: #101310; background-color: #e5e5e5; font-size: 1em; border: 1px solid #050505; padding: .5em; margin: 0 2em; } 62 | pre.subh { color: #101310; background-color: #f1f1f1; padding: .1em; font-weight: bold; font-size: 150%;} 63 | -------------------------------------------------------------------------------- /doc/js/shBrushGo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SyntaxHighlighter 3 | * http://alexgorbatchev.com/SyntaxHighlighter 4 | * 5 | * SyntaxHighlighter is donationware. If you are using it, please donate. 6 | * http://alexgorbatchev.com/SyntaxHighlighter/donate.html 7 | * 8 | * @version 9 | * 3.0.83 (July 02 2010) 10 | * 11 | * @copyright 12 | * Copyright (C) 2004-2010 Alex Gorbatchev. 13 | * 14 | * @license 15 | * Dual licensed under the MIT and GPL licenses. 16 | */ 17 | ;(function() 18 | { 19 | // CommonJS 20 | typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null; 21 | 22 | function Brush() 23 | { 24 | // Copyright 2021 Alexander Kresin 25 | var keywords = 'break case chan const continue default defer else fallthrough for ' + 26 | 'func go goto if import interface map package range return select ' + 27 | 'struct switch true type var ' + 28 | 'bool byte rune string error false true iota nil int8 int16 int32 ' + 29 | 'int64 int uint8 uint16 uint32 uint64 uint float32 float64 complex128 complex64 '; 30 | 31 | var functions = 'append cap close complex copy delete imag len make new panic real recover '; 32 | 33 | this.regexList = [ 34 | { regex: SyntaxHighlighter.regexLib.singleLineCComments, css: 'comments' }, // one line comments 35 | { regex: /\/\*([^\*][\s\S]*)?\*\//gm, css: 'comments' }, // multiline comments 36 | { regex: /\/\*(?!\*\/)\*[\s\S]*?\*\//gm, css: 'preprocessor' }, // documentation comments 37 | { regex: SyntaxHighlighter.regexLib.doubleQuotedString, css: 'string' }, // strings 38 | { regex: SyntaxHighlighter.regexLib.singleQuotedString, css: 'string' }, // strings 39 | { regex: /\b([\d]+(\.[\d]+)?|0x[a-f0-9]+)\b/gi, css: 'value' }, // numbers 40 | { regex: new RegExp(this.getKeywords(functions), 'gmi'), css: 'functions bold' }, 41 | { regex: new RegExp(this.getKeywords(keywords), 'gmi'), css: 'keyword bold' } 42 | ]; 43 | 44 | this.forHtmlScript({ 45 | left : /(<|<)%[@!=]?/g, 46 | right : /%(>|>)/g 47 | }); 48 | }; 49 | 50 | Brush.prototype = new SyntaxHighlighter.Highlighter(); 51 | Brush.aliases = ['go']; 52 | 53 | SyntaxHighlighter.brushes.Go = Brush; 54 | 55 | // CommonJS 56 | typeof(exports) != 'undefined' ? exports.Brush = Brush : null; 57 | })(); 58 | -------------------------------------------------------------------------------- /doc/js/shCore.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SyntaxHighlighter 3 | * http://alexgorbatchev.com/SyntaxHighlighter 4 | * 5 | * SyntaxHighlighter is donationware. If you are using it, please donate. 6 | * http://alexgorbatchev.com/SyntaxHighlighter/donate.html 7 | * 8 | * @version 9 | * 3.0.83 (July 02 2010) 10 | * 11 | * @copyright 12 | * Copyright (C) 2004-2010 Alex Gorbatchev. 13 | * 14 | * @license 15 | * Dual licensed under the MIT and GPL licenses. 16 | */ 17 | eval(function(p,a,c,k,e,d){e=function(c){return(c35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('K M;I(M)1S 2U("2a\'t 4k M 4K 2g 3l 4G 4H");(6(){6 r(f,e){I(!M.1R(f))1S 3m("3s 15 4R");K a=f.1w;f=M(f.1m,t(f)+(e||""));I(a)f.1w={1m:a.1m,19:a.19?a.19.1a(0):N};H f}6 t(f){H(f.1J?"g":"")+(f.4s?"i":"")+(f.4p?"m":"")+(f.4v?"x":"")+(f.3n?"y":"")}6 B(f,e,a,b){K c=u.L,d,h,g;v=R;5K{O(;c--;){g=u[c];I(a&g.3r&&(!g.2p||g.2p.W(b))){g.2q.12=e;I((h=g.2q.X(f))&&h.P===e){d={3k:g.2b.W(b,h,a),1C:h};1N}}}}5v(i){1S i}5q{v=11}H d}6 p(f,e,a){I(3b.Z.1i)H f.1i(e,a);O(a=a||0;a-1},3d:6(g){e+=g}};c1&&p(e,"")>-1){a=15(J.1m,n.Q.W(t(J),"g",""));n.Q.W(f.1a(e.P),a,6(){O(K c=1;c<14.L-2;c++)I(14[c]===1d)e[c]=1d})}I(J.1w&&J.1w.19)O(K b=1;be.P&&J.12--}H e};I(!D)15.Z.1A=6(f){(f=n.X.W(J,f))&&J.1J&&!f[0].L&&J.12>f.P&&J.12--;H!!f};1r.Z.1C=6(f){M.1R(f)||(f=15(f));I(f.1J){K e=n.1C.1p(J,14);f.12=0;H e}H f.X(J)};1r.Z.Q=6(f,e){K a=M.1R(f),b,c;I(a&&1j e.58()==="3f"&&e.1i("${")===-1&&y)H n.Q.1p(J,14);I(a){I(f.1w)b=f.1w.19}Y f+="";I(1j e==="6")c=n.Q.W(J,f,6(){I(b){14[0]=1f 1r(14[0]);O(K d=0;dd.L-3;){i=1r.Z.1a.W(g,-1)+i;g=1Q.3i(g/10)}H(g?d[g]||"":"$")+i}Y{g=+i;I(g<=d.L-3)H d[g];g=b?p(b,i):-1;H g>-1?d[g+1]:h}})})}I(a&&f.1J)f.12=0;H c};1r.Z.1e=6(f,e){I(!M.1R(f))H n.1e.1p(J,14);K a=J+"",b=[],c=0,d,h;I(e===1d||+e<0)e=5D;Y{e=1Q.3i(+e);I(!e)H[]}O(f=M.3c(f);d=f.X(a);){I(f.12>c){b.U(a.1a(c,d.P));d.L>1&&d.P=e)1N}f.12===d.P&&f.12++}I(c===a.L){I(!n.1A.W(f,"")||h)b.U("")}Y b.U(a.1a(c));H b.L>e?b.1a(0,e):b};M.1h(/\\(\\?#[^)]*\\)/,6(f){H n.1A.W(A,f.2S.1a(f.P+f[0].L))?"":"(?:)"});M.1h(/\\((?!\\?)/,6(){J.19.U(N);H"("});M.1h(/\\(\\?<([$\\w]+)>/,6(f){J.19.U(f[1]);J.2N=R;H"("});M.1h(/\\\\k<([\\w$]+)>/,6(f){K e=p(J.19,f[1]);H e>-1?"\\\\"+(e+1)+(3R(f.2S.3a(f.P+f[0].L))?"":"(?:)"):f[0]});M.1h(/\\[\\^?]/,6(f){H f[0]==="[]"?"\\\\b\\\\B":"[\\\\s\\\\S]"});M.1h(/^\\(\\?([5A]+)\\)/,6(f){J.3d(f[1]);H""});M.1h(/(?:\\s+|#.*)+/,6(f){H n.1A.W(A,f.2S.1a(f.P+f[0].L))?"":"(?:)"},M.1B,6(){H J.2K("x")});M.1h(/\\./,6(){H"[\\\\s\\\\S]"},M.1B,6(){H J.2K("s")})})();1j 2e!="1d"&&(2e.M=M);K 1v=6(){6 r(a,b){a.1l.1i(b)!=-1||(a.1l+=" "+b)}6 t(a){H a.1i("3e")==0?a:"3e"+a}6 B(a){H e.1Y.2A[t(a)]}6 p(a,b,c){I(a==N)H N;K d=c!=R?a.3G:[a.2G],h={"#":"1c",".":"1l"}[b.1o(0,1)]||"3h",g,i;g=h!="3h"?b.1o(1):b.5u();I((a[h]||"").1i(g)!=-1)H a;O(a=0;d&&a\'+c+""});H a}6 n(a,b){a.1e("\\n");O(K c="",d=0;d<50;d++)c+=" ";H a=v(a,6(h){I(h.1i("\\t")==-1)H h;O(K g=0;(g=h.1i("\\t"))!=-1;)h=h.1o(0,g)+c.1o(0,b-g%b)+h.1o(g+1,h.L);H h})}6 x(a){H a.Q(/^\\s+|\\s+$/g,"")}6 D(a,b){I(a.Pb.P)H 1;Y I(a.Lb.L)H 1;H 0}6 y(a,b){6 c(k){H k[0]}O(K d=N,h=[],g=b.2D?b.2D:c;(d=b.1I.X(a))!=N;){K i=g(d,b);I(1j i=="3f")i=[1f e.2L(i,d.P,b.23)];h=h.1O(i)}H h}6 E(a){K b=/(.*)((&1G;|&1y;).*)/;H a.Q(e.3A.3M,6(c){K d="",h=N;I(h=b.X(c)){c=h[1];d=h[2]}H\'\'+c+""+d})}6 z(){O(K a=1E.36("1k"),b=[],c=0;c<1z 4I="1Z://2y.3L.3K/4L/5L"><3J><4N 1Z-4M="5G-5M" 6K="2O/1z; 6J=6I-8" /><1t>6L 1v<3B 1L="25-6M:6Q,6P,6O,6N-6F;6y-2f:#6x;2f:#6w;25-22:6v;2O-3D:3C;">1v3v 3.0.76 (72 73 3x)1Z://3u.2w/1v70 17 6U 71.6T 6X-3x 6Y 6D.6t 61 60 J 1k, 5Z 5R 5V <2R/>5U 5T 5S!\'}},1Y:{2j:N,2A:{}},1U:{},3A:{6n:/\\/\\*[\\s\\S]*?\\*\\//2c,6m:/\\/\\/.*$/2c,6l:/#.*$/2c,6k:/"([^\\\\"\\n]|\\\\.)*"/g,6o:/\'([^\\\\\'\\n]|\\\\.)*\'/g,6p:1f M(\'"([^\\\\\\\\"]|\\\\\\\\.)*"\',"3z"),6s:1f M("\'([^\\\\\\\\\']|\\\\\\\\.)*\'","3z"),6q:/(&1y;|<)!--[\\s\\S]*?--(&1G;|>)/2c,3M:/\\w+:\\/\\/[\\w-.\\/?%&=:@;]*/g,6a:{18:/(&1y;|<)\\?=?/g,1b:/\\?(&1G;|>)/g},69:{18:/(&1y;|<)%=?/g,1b:/%(&1G;|>)/g},6d:{18:/(&1y;|<)\\s*1k.*?(&1G;|>)/2T,1b:/(&1y;|<)\\/\\s*1k\\s*(&1G;|>)/2T}},16:{1H:6(a){6 b(i,k){H e.16.2o(i,k,e.13.1x[k])}O(K c=\'\',d=e.16.2x,h=d.2X,g=0;g";H c},2o:6(a,b,c){H\'<2W>\'+c+""},2b:6(a){K b=a.1F,c=b.1l||"";b=B(p(b,".20",R).1c);K d=6(h){H(h=15(h+"6f(\\\\w+)").X(c))?h[1]:N}("6g");b&&d&&e.16.2x[d].2B(b);a.3N()},2x:{2X:["21","2P"],21:{1H:6(a){I(a.V("2l")!=R)H"";K b=a.V("1t");H e.16.2o(a,"21",b?b:e.13.1x.21)},2B:6(a){a=1E.6j(t(a.1c));a.1l=a.1l.Q("47","")}},2P:{2B:6(){K a="68=0";a+=", 18="+(31.30-33)/2+", 32="+(31.2Z-2Y)/2+", 30=33, 2Z=2Y";a=a.Q(/^,/,"");a=1P.6Z("","38",a);a.2C();K b=a.1E;b.6W(e.13.1x.37);b.6V();a.2C()}}}},35:6(a,b){K c;I(b)c=[b];Y{c=1E.36(e.13.34);O(K d=[],h=0;h(.*?))\\\\]$"),s=1f M("(?<27>[\\\\w-]+)\\\\s*:\\\\s*(?<1T>[\\\\w-%#]+|\\\\[.*?\\\\]|\\".*?\\"|\'.*?\')\\\\s*;?","g");(j=s.X(k))!=N;){K o=j.1T.Q(/^[\'"]|[\'"]$/g,"");I(o!=N&&m.1A(o)){o=m.X(o);o=o.2V.L>0?o.2V.1e(/\\s*,\\s*/):[]}l[j.27]=o}g={1F:g,1n:C(i,l)};g.1n.1D!=N&&d.U(g)}H d},1M:6(a,b){K c=J.35(a,b),d=N,h=e.13;I(c.L!==0)O(K g=0;g")==o-3){m=m.4h(0,o-3);s=R}l=s?m:l}I((i.1t||"")!="")k.1t=i.1t;k.1D=j;d.2Q(k);b=d.2F(l);I((i.1c||"")!="")b.1c=i.1c;i.2G.74(b,i)}}},2E:6(a){w(1P,"4k",6(){e.1M(a)})}};e.2E=e.2E;e.1M=e.1M;e.2L=6(a,b,c){J.1T=a;J.P=b;J.L=a.L;J.23=c;J.1V=N};e.2L.Z.1q=6(){H J.1T};e.4l=6(a){6 b(j,l){O(K m=0;md)1N;Y I(g.P==c.P&&g.L>c.L)a[b]=N;Y I(g.P>=c.P&&g.P\'+c+""},3Q:6(a,b){K c="",d=a.1e("\\n").L,h=2u(J.V("2i-1s")),g=J.V("2z-1s-2t");I(g==R)g=(h+d-1).1q().L;Y I(3R(g)==R)g=0;O(K i=0;i\'+j+"":"")+i)}H a},4f:6(a){H a?"<4a>"+a+"":""},4b:6(a,b){6 c(l){H(l=l?l.1V||g:g)?l+" ":""}O(K d=0,h="",g=J.V("1D",""),i=0;i|&1y;2R\\s*\\/?&1G;/2T;I(e.13.46==R)b=b.Q(h,"\\n");I(e.13.44==R)b=b.Q(h,"");b=b.1e("\\n");h=/^\\s*/;g=4Q;O(K i=0;i0;i++){K k=b[i];I(x(k).L!=0){k=h.X(k);I(k==N){a=a;1N a}g=1Q.4q(k[0].L,g)}}I(g>0)O(i=0;i\'+(J.V("16")?e.16.1H(J):"")+\'<3Z 5z="0" 5H="0" 5J="0">\'+J.4f(J.V("1t"))+"<3T><3P>"+(1u?\'<2d 1g="1u">\'+J.3Q(a)+"":"")+\'<2d 1g="17">\'+b+""},2F:6(a){I(a===N)a="";J.17=a;K b=J.3Y("T");b.3X=J.1H(a);J.V("16")&&w(p(b,".16"),"5c",e.16.2b);J.V("3V-17")&&w(p(b,".17"),"56",f);H b},2Q:6(a){J.1c=""+1Q.5d(1Q.5n()*5k).1q();e.1Y.2A[t(J.1c)]=J;J.1n=C(e.2v,a||{});I(J.V("2k")==R)J.1n.16=J.1n.1u=11},5j:6(a){a=a.Q(/^\\s+|\\s+$/g,"").Q(/\\s+/g,"|");H"\\\\b(?:"+a+")\\\\b"},5f:6(a){J.28={18:{1I:a.18,23:"1k"},1b:{1I:a.1b,23:"1k"},17:1f M("(?<18>"+a.18.1m+")(?<17>.*?)(?<1b>"+a.1b.1m+")","5o")}}};H e}();1j 2e!="1d"&&(2e.1v=1v);',62,441,'||||||function|||||||||||||||||||||||||||||||||||||return|if|this|var|length|XRegExp|null|for|index|replace|true||div|push|getParam|call|exec|else|prototype||false|lastIndex|config|arguments|RegExp|toolbar|code|left|captureNames|slice|right|id|undefined|split|new|class|addToken|indexOf|typeof|script|className|source|params|substr|apply|toString|String|line|title|gutter|SyntaxHighlighter|_xregexp|strings|lt|html|test|OUTSIDE_CLASS|match|brush|document|target|gt|getHtml|regex|global|join|style|highlight|break|concat|window|Math|isRegExp|throw|value|brushes|brushName|space|alert|vars|http|syntaxhighlighter|expandSource|size|css|case|font|Fa|name|htmlScript|dA|can|handler|gm|td|exports|color|in|href|first|discoveredBrushes|light|collapse|object|cache|getButtonHtml|trigger|pattern|getLineHtml|nbsp|numbers|parseInt|defaults|com|items|www|pad|highlighters|execute|focus|func|all|getDiv|parentNode|navigator|INSIDE_CLASS|regexList|hasFlag|Match|useScriptTags|hasNamedCapture|text|help|init|br|input|gi|Error|values|span|list|250|height|width|screen|top|500|tagName|findElements|getElementsByTagName|aboutDialog|_blank|appendChild|charAt|Array|copyAsGlobal|setFlag|highlighter_|string|attachEvent|nodeName|floor|backref|output|the|TypeError|sticky|Za|iterate|freezeTokens|scope|type|textarea|alexgorbatchev|version|margin|2010|005896|gs|regexLib|body|center|align|noBrush|require|childNodes|DTD|xhtml1|head|org|w3|url|preventDefault|container|tr|getLineNumbersHtml|isNaN|userAgent|tbody|isLineHighlighted|quick|void|innerHTML|create|table|links|auto|smart|tab|stripBrs|tabs|bloggerMode|collapsed|plain|getCodeLinesHtml|caption|getMatchesHtml|findMatches|figureOutLineNumbers|removeNestedMatches|getTitleHtml|brushNotHtmlScript|substring|createElement|Highlighter|load|HtmlScript|Brush|pre|expand|multiline|min|Can|ignoreCase|find|blur|extended|toLowerCase|aliases|addEventListener|innerText|textContent|wasn|select|createTextNode|removeChild|option|same|frame|xmlns|dtd|twice|1999|equiv|meta|htmlscript|transitional|1E3|expected|PUBLIC|DOCTYPE|on|W3C|XHTML|TR|EN|Transitional||configured|srcElement|Object|after|run|dblclick|matchChain|valueOf|constructor|default|switch|click|round|execAt|forHtmlScript|token|gimy|functions|getKeywords|1E6|escape|within|random|sgi|another|finally|supply|MSIE|ie|toUpperCase|catch|returnValue|definition|event|border|imsx|constructing|one|Infinity|from|when|Content|cellpadding|flags|cellspacing|try|xhtml|Type|spaces|2930402|hosted_button_id|lastIndexOf|donate|active|development|keep|to|xclick|_s|Xml|please|like|you|paypal|cgi|cmd|webscr|bin|highlighted|scrollbars|aspScriptTags|phpScriptTags|sort|max|scriptScriptTags|toolbar_item|_|command|command_|number|getElementById|doubleQuotedString|singleLinePerlComments|singleLineCComments|multiLineCComments|singleQuotedString|multiLineDoubleQuotedString|xmlComments|alt|multiLineSingleQuotedString|If|https|1em|000|fff|background|5em|xx|bottom|75em|Gorbatchev|large|serif|CDATA|continue|utf|charset|content|About|family|sans|Helvetica|Arial|Geneva|3em|nogutter|Copyright|syntax|close|write|2004|Alex|open|JavaScript|highlighter|July|02|replaceChild|offset|83'.split('|'),0,{})) 18 | -------------------------------------------------------------------------------- /doc/manual_rus.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | External: Справочное руководство 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 28 | 29 |
30 |
31 |

External: Справочное руководство

32 | 58 |
59 |

1. Введение

60 |

External - это GUI библиотека для golang, которая для реализации интерфейса использует отдельную 61 | программу, GuiServer - см. здесь.

62 |

GuiServer написан на Harbour и GUI-библиотеке HwGUI. Я специально это оговариваю, поскольку в 63 | дальнейшем буду ссылаться на HwGUI при описании некоторых виджетов.

64 |

При старте External запускает GuiServer, если он еще не запущен, и присоединяется к нему. 65 | Связь связь эта может осуществляться одним из двух способов по вашему выбору.

66 |
    67 |
  1. С помощью tcp/ip соединения. При этом используются два tcp/ip порта. GuiServer может 68 | работать как на том же компьютере, что и ваше приложение, так и на удаленном - в этом 69 | случае запустить его из приложения не удастся, так что он должен быть запущен заранее. 70 | По одному порту External отправляет команды GuiServer'у - создание окон, виджетов, 71 | всякие действия с ними и пр., по другому - принимает от него сообщения, поступающие при 72 | наступлении каких либо событий, нажатия кнопки, например. 73 |
  2. С помощью обычных файлов - используются два файла, логика взаимодействия та же, что и у 74 | двух tcp/ip портов в первом варианте. 75 |
76 |
77 |

2. Hello, World

78 |

Начнем, по традиции, с Hello, World. Вот пример простейшей программы, создающей окно с этой надписью:

79 |
80 |
 81 | package main
 82 | 
 83 | import egui "github.com/alkresin/external"
 84 | 
 85 | func main() {
 86 | 
 87 |     if egui.Init("") != 0 {
 88 |         return
 89 |     }
 90 |     pWindow := &egui.Widget{X: 100, Y: 100, W: 400, H: 140, Title: "My first GUI app"}
 91 |     egui.InitMainWindow(pWindow)
 92 | 
 93 |     pWindow.AddWidget(&egui.Widget{Type: "label",
 94 |         X: 20, Y: 60, W: 160, H: 24, Title: "Hello, World!" })
 95 | 
 96 |     pWindow.Activate()
 97 |     egui.Exit()
 98 | }
99 |
100 |

Функция Init() запускает GuiServer и присоединяется к нему. InitMainWindow() создает 101 | главное окно приложения с заданными параметрами. Метод AddWidget() — добавляет виджет типа label. 102 | Activate() — выводит окно на экран и переводит программу в режим ожидания.

103 |

Далее рассмотрим эти действия по порядку.

104 |
105 | 106 |

3. Инициализация

107 |

Как я уже говорил, первое, что следует сделать - это запустить GuiServer и присоединиться к нему. 108 | Это делает функция Init(sOpt string), которой передается строка параметров, разделенных символом 109 | конца строки ( "\n" ). Вот они (приведены значения по умолчанию):

110 |
111 |
112 |     guiserver=guiserver.exe   //Имя исполняемого файла GuiServer
113 |     type=1                    //Способ соединения, 1 - по tcp/ip, 2 - файловое соединение
114 |     dir=                      //Каталог для файлов (при type=2), по умолчанию - временный каталог
115 |     file=gs                   //Префикс имен файлов (при type=2)
116 |     address=127.0.0.1         //Ip компьютера с GuiServer (при type=1)
117 |     port=3101                 //Порт GuiServer (при type=1)
118 |     log=0                     //Уровень журналирования (0...2)
119 |

Если вам надо изменить, например, номер порта и уровень журналирования, вы можете 120 | использовать такой вызов Init():

121 |
122 |     egui.Init("port=4801\nlog=1")
123 | 124 |

Символ конца строки используется в качестве разделителя, чтобы было удобнее передать в 125 | качестве параметра содержимое ini файла:

126 |
127 |
128 |     var sInit string
129 |     b, err := ioutil.ReadFile("test.ini")
130 |     if err != nil {
131 |        sInit = ""
132 |     } else {
133 |        sInit = string(b)
134 |     }
135 | 
136 |     if egui.Init(sInit) != 0 {
137 |         return
138 |     }
139 |     // ...
140 |
141 |

Функция Init() возвращает 0 в случае успешного выполнения, 1 - если соединиться не удалось, 142 | и 2 - если протоколы связи вашей версии External и GuiServer отличаются.

143 |
144 | 145 |

4. Создание окон и виджетов

146 |

Итак, инициализация прошла успешно, теперь мы можем формировать интерфейс программы. В 147 | большинстве случаев начать следует с создания главного окна. Это делается посредством 148 | вызова функции InitMainWindow(pWindow), которой в качестве единственного параметра передается 149 | указатель на предварительно созданную структуру Widget, описывающую окно. Вот объявление 150 | этой структуры:

151 |
152 |
153 |    type Widget struct {
154 |       Parent   *Widget
155 |       Type     string
156 |       Name     string
157 |       X        int
158 |       Y        int
159 |       W        int
160 |       H        int
161 |       Title    string
162 |       Winstyle int32
163 |       TColor   int32
164 |       BColor   int32
165 |       Tooltip  string
166 |       Anchor   int32
167 |       Font     *Font
168 |       AProps   map[string]string
169 |       aWidgets []*Widget
170 |    }
171 |
172 |

Структура Widget используется для создания окон, диалогов и виджетов, конкретный тип 173 | объекта определяется полем Type; для главного окна это "main", для 174 | диалога - "dialog", для, например, кнопки, - "button". К описанию 175 | полей структуры мы вернемся чуть позже, а пока продолжим разговор о создании окна.

176 |

InitMainWindow(pWindow *Widget) создает окно, этот вызов приводит в конечном итоге к вызову на 177 | стороне GuiServer функции Windows API CreateWindowEx() или, если мы работаем под 178 | Unix/Linux, gtk_window_new().

179 |

Для того, чтобы окно появилось на экране, необходимо вызвать еще 180 | метод объекта Widget Activate(). GuiServer по получении соответствующей команды вызывает функцию 181 | Windows API ShowWindow() и начинает цикл обработки сообщений, в Unix/Linux делается вызов 182 | gtk_main(), что, по сути, то же самое. External после отправления команды на GuiServer тоже входит 183 | в цикл ожидания сообщений, (он принимает их, как вы помните, от GuiServer через второй порт), поэтому 184 | выполнение программы на этом приостанавливается. Следующие после вызова Activate() для главного окна 185 | строки программы (точнее, функции, в которой стоит Activate()) будут выполнены только после закрытия 186 | окна, обычно это завершение программы.

187 |

Для добавления виджетов используется метод объекта Widget AddWidget(pWidg *Widget). Добавить 188 | виджет можно в любой другой виджет, но практически нет какого-либо смысла добавлять, например, Edit 189 | в кнопку. Виджеты добавляются в окно (главное или диалог), на панель, на Tab. При добавлении в окно 190 | вызовы AddWidget() располагаются между InitMainWindow() и Activate(). При необходимости виджеты 191 | можно добавлять и в других частях программы при наступлении каких-то событий. Единственным 192 | параметром AddWidget() является указатель на соответствующую структуру Widget, содержащую описание 193 | нового виджета

194 |
195 | 196 |

4.1 Структура Widget - описание полей

197 | 198 | 199 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 209 | 210 | 211 | 212 |
Parent объект Widget, родительское окно/виджет
Type строка, определяющая тип виджета: "main", "dialog", 200 | "button", "label" и пр., полный список см. дальше
Name строка - идентификатор виджета
X, Y, W, H координаты верхнего левого угла окна/виджета, ширина и высота в пикселях
Title заголовок окна, надпись на кнопке, т.е., текст отображаемый на виджете
Winstyle число, стиль окна/виджета, принятый в Windows API
TColor, BColor число, цвет текста и фона, соответственно
Tooltip он и есть тултип - строчка текста, появляющаяся при наведении мышки на виджет
Anchor якорь, число, определяющее привязку виджета к краям окна при изменении размера окна, подробнее 208 | см. в Приложении
Font шрифт, объект структуры Font, подробнее см. дальше
AProps поле типа map, список дополнительных атрибутов окна/виджета, подробнее см. дальше;
aWidgets массив объектов Widget - тех, что включены в состав этого окна/виджета
213 | 214 |

4.2 Список виджетов

215 |

В таблице - список типов виджетов (поле Type структуры Widget), поддерживаемых к моменту написания 216 | этого руководства.

217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 |
main главное окно
dialog диалог
label стандартный статический текст
edit стандартный виджет для ввода/редактирования текста
button стандартная кнопка
check стандартная check - кнопка
radio стандартная радио - кнопка
radiogr группа радио-кнопок
group прямоугольная рамка для выделения группы виджетов
combo стандартный комбобокс
bitmap картинка
line разделительная линия
panel панель (тулбар), на ней можно размещать другие виджеты
paneltop панель, прикрепленная к верхней части окна
panelbot панель, прикрепленная к нижней части окна (статусбар)
panelhead панель - заголовок окна
ownbtn нестандартная кнопка
splitter сплиттер - ну, вы знаете
updown стандартный виджет для редактирования числа с кнопками вверх/вниз
tree дерево
progress прогресс - бар
tab таб с вкладками
browse виджет для отображения таблицы данных
cedit виджет для редактирования текста с расширенными возможностями
link виджет - ссылка
monthcal стандартный календарик
245 |
246 | 247 |

4.3 AProps - дополнительные атрибуты

248 |

Поскольку структура Widget - одна для всех типов виджетов, а наборы свойств у виджетов - разные, то 249 | помимо свойств, общих для большинства виджетов (координаты, текст, цвета, ...), в структуру включена 250 | хэш-таблица (map) AProps, которая содержит свойства/атрибуты, которые вы хотите установить именно 251 | для этого виджета. Конечно, вы можете указывать там не всё, что угодно, а только те атрибуты, которые 252 | определены для виджетов этого типа. Ниже - список типов виджетов с соответствующими свойствами:

253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 |
main Icon: C
dialog Icon: C, NoExitOnEsc: L, NoCloseAble: L
label Transpa: L
edit Picture: C
check Transpa: L
radio Transpa: L
combo AItems: AC
bitmap Transpa: L, TrColor: N, Image: C
line Vertical: L
panel HStyle: C
paneltop HStyle: C
panelbot HStyle: C, AParts: AC
panelhead HStyle: C, Xt: N, Yt: N, BtnClose: L, BtnMax: L, BtnMin: L
ownbtn Transpa: L, TrColor: N, Image: C, HStyles: AC, Xt: N, Yt: N
splitter Vertical: L, From: N, TO: N, ALeft: AC, ARight: AC, HStyle: C
updown From: N, TO: N
tree AImages: AC, EditLabel: L
progress Maxpos: N
browse Append: L, Autoedit: L, NoVScroll: L, NoBorder: L
cedit NoVScroll: L, NoBorder: L
link Link: C, ClrVisited: N, ClrLink: N, ClrOver: N
monthcal NoToday: L, NoTodayCirc: L, WeekNumb: L
277 |

Здесь перед двоеточием - имя атрибута, после - его тип: 278 |
- C - строка; 279 |
- N - число; 280 |
- L - логическое значение (boolean); 281 |
- AC - массив строк. 282 |

283 |

Поскольку AProps имеет тип map[string]string, мы можем значения всех атрибутов указывать только 284 | как строки. Поэтому числа берем в кавычки, логические значения указываем как "t" и 285 | "f", для массивов строк лучше всего использовать функцию ToString(xParam ...interface{}), например:

286 |
287 |
288 |    AProps: map[string]string{"AItems": egui.ToString("first", "second", "third")}}
289 |    AProps: map[string]string{"Transpa": "t"}}
290 |    AProps: map[string]string{"HStyle": "st2", "BtnClose": "true", "Xt": "60"}}
291 |      
292 |
293 | 294 |

Далее - список атрибутов с комментариями (в скобках - значения по умолчанию):

295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 |
Icon
NoExitOnEsc Для диалога: не закрывать диалог при нажатии ESC (Нет)
NoCloseAble
Transpa Прозрачность (Нет)
TrColor Цвет прозрачного слоя
Picture
AItems Для combobox: массив элементов
Image
Vertical Для линии (line): вертикальная или горизонтальная (Нет)
HStyle Стиль - подробнее см. дальше
HStyles
AParts Для panelbot: массив с размерами (шириной) в пикселях частей панели
Xt, Yt x и y координаты для размещения текста
BtnClose Для panelhead: показать ли кнопку закрытия окна (Да)
BtnMax Для panelhead: показать ли кнопку максимизации окна (Да)
BtnMin Для panelhead: показать ли кнопку минимизации окна (Да)
From, TO
ALeft, ARight
AImages
EditLabel
Maxpos
Append Для browse: можно добавлять строчки (Нет)
Autoedit Для browse: можно редактировать ячейки (Нет)
NoVScroll Без вертикального скролла (Нет)
NoBorder Без окантовки (Нет)
Link Для link: url ссылки
ClrVisited Для link: цвет уже посещенной ссылки
ClrLink Для link: цвет ссылки
ClrOver Для link: цвет ссылки при наведении мыши
NoToday Для monthcal:
NoTodayCirc Для monthcal:
WeekNumb Для monthcal:
329 |

Как видите, набор атрибутов, которые можно указать в AProps, невелик. На самом деле список 330 | атрибутов виджетов HwGUI (и, соответственно, GuiServer) гораздо больше. External позволяет 331 | установить все эти атрибуты, не поддерживаемые через AProps, с помощью метода SetParam()

332 |
333 | 334 |

5. Font, Style

335 |

Для работы со шрифтами в External предусмотрена структура Font:

336 |
337 |
338 | type Font struct {
339 | 	Family    string     // наименование шрифта (Arial, Serif, ...)
340 | 	Name      string     // идентификатор шрифта
341 | 	Height    int        // размер (высота) шрифта
342 | 	Bold      bool       // жирный
343 | 	Italic    bool       // наклонный
344 | 	Underline bool       // подчеркнутый
345 | 	Strikeout bool       // перечеркнутый
346 | 	Charset   int16      // 204 - русский
347 | }
348 |
349 |

Поля этой структуры, думаю, в комментариях не нуждаются - это стандартные атрибуты шрифта. Поле Name, как 350 | и в структуре Widget, используется для идентификации шрифта в вашей программе: вы сможете найти 351 | ранее созданный экземпляр шрифта (указатель на соответствующую структуру Font) с помощью функции GetFont(sName string).

352 |

Для создания шрифта используется функция CreateFont(pFont *Font). Она возвращает указатель на 353 | структуру Font (ту же, что передается ей в качестве параметра), который можно затем использовать при 354 | объявлении структуры Widget с полем Font. Для изменения шрифта виджета - метод структуры Widget SetFont(pFont *Font):

355 |
356 |
357 |     // Создаем шрифт с идентификатором "flbl"
358 |     egui.CreateFont(&egui.Font{Name: "flbl", Family: "Georgia", Height: 16})
359 |     // Создаем шрифт и указываем его как атрибут диалогового окна
360 | 	pFont := egui.CreateFont(&egui.Font{Family: "Georgia", Height: 16})
361 | 	pDlg := &egui.Widget{Name: "dlg", X: 300, Y: 200, W: 400, H: 260, Title: "Dialog Test", Font: pFont}
362 | 	egui.InitDialog(pDlg)
363 | 
364 | 	pLbl1 := pDlg.AddWidget(&egui.Widget{Type: "label", X: 20, Y: 10, W: 160, H: 24, Title: "Identifier:"})
365 | 	// ...
366 | 	// Изменяем шрифт метки на созданный ранее с идентификатором "flbl"
367 | 	pLbl1.SetFont( egui.GetFont("flbl") )
368 |      
369 |
370 |

Style (не путать с Winstyle) - это структура, описывающая вневний вид виджета. Она может 371 | использоваться для нестандартных виджетов, которые рисуются самим HwGUI: "panel", 372 | "paneltop", "panelbot", "panelhead", "ownbtn".

373 |
374 |
375 | type Style struct {
376 | 	Name      string    // идентификатор стиля
377 | 	Orient    int16     // тип градиента, его направление
378 | 	Colors    []int32   // массив цветов для заполнения фона
379 | 	Corners   []int32   // массив с радиусами закругления углов, [верхний левый,
380 | 	                    //   верхний правый, нижний правый, нижний левый]
381 | 	BorderW   int8      // толщина рамки в пикселях, если не задана - рамка
382 |                         //   рисоваться не будет
383 | 	BorderClr int32     // цвет рамки
384 | 	Bitmap    string    // картинка для заполнения фона
385 | }
386 |
387 |

Главным образом Style используется для создания градиентного фона виджета. Поле 388 | Colors - массив цветов градиента, Orient - направление подробнее см. дальше.

389 |

Поле Name, как и в структурах Widget и Font, используется для идентификации 390 | стиля в вашей программе: вы сможете найти ранее созданный экземпляр стиля (указатель на 391 | соответствующую структуру Style) с помощью функции GetStyle(sName string).

392 |

Для создания стиля используется функция CreateStyle(pStyle *Style). Она возвращает указатель на 393 | структуру Style (ту же, что передается ей в качестве параметра), который можно затем использовать при 394 | объявлении структуры Widget в составе хэш-массива AProps. Ключом соответствующего 395 | элемента будет HStyle, а значением - идентификатор (Name) структуры Style.

396 |

397 |
398 | 399 |

6. Callback функции

400 |

Callback функции (функция обратного вызова) - одно из важнейших понятий в External, это основная часть вашего 401 | приложения. В главной функции (main()) вы обычно создаете главное окно, его меню, набор 402 | виджетов. После вызова Activate() выполнение программы приостанавливается, она переходит в 403 | режим ожидания сообщений от GuiServer, от GUI-элементов вашего окна, вашего интерфейса. Эти 404 | сообщения вызывают выполнение callback функций вашего приложения, тех функций, которые вы 405 | установили как callback. Это функции, указанные в объявлении пунктов меню 406 | (AddMenuItem()), переданные c помощью SetCallBackProc() для обработки того или иного 407 | события (нажатия кнопки, потери фокуса ввода, ...), установленные для вызова после закрытия 408 | стандартного диалога (MsgInfo(), SelectFile(), ...), ...

409 |

Спецификация callback функции имеет вид func([]string) string, т.е. она принимает в 410 | качестве параметра массив строк и возвращает строку. В функциях, которые предназначены для 411 | передачи на GuiServer информации о callback функции, используются для этого два параметра: 412 | fu func([]string) string и sCode string. Первый - та самая callback 413 | функция, второй - идентификатор этой функции (это может быть имя функции или любая другая 414 | уникальная строка). External строит хэш-массив map[string]func([]string) string и 415 | передает на GuiServer строковый идентификатор. GuiServer при наступлении соответствующего 416 | события этот идентификатор возвращает, External находит по нему нужную функцию в хэш-массиве 417 | и выполняет ее. Вот так это и работает. Идентификатор нужен для того, чтобы передавать его 418 | на GuiServer и обратно - как, например, имя (sName) структур Widget, Font и пр. 419 | Кроме функции и ее идентификатора может передаваться произвольное количество строковых 420 | параметров - они будут потом переданы обратно callback - функции.

421 |

Вот как это может выглядеть в программе:

422 |
423 |
424 |      ...
425 |      // Передается ссылка на объявленную в программе функцию fu1
426 |      egui.AddMenuItem("Date", 0, fu1, "fu1" )
427 |      ...
428 |      // Анонимная Callback функция, "Bye...1" - параметр, который она получит в массиве
429 |      egui.AddMenuItem("Set text to label", 0,
430 |         func(p []string) string { egui.Widg("main.l1").SetText(p[1]); return "" },
431 |          "fsett2", "Bye...1")
432 |      ...
433 |      // Callback функция fsett1, которая будет вызвана при нажатии pButton
434 |      pButton.SetCallBackProc("onclick", fsett1, "fsett1", "first parameter")
435 |      
436 |
437 |

При вызове callback функции из меню первым элементом в передаваемом обратно параметре-массиве 438 | всегда будет строка "menu", при вызове от какого-либо виджета - идентификатор 439 | этого виджета. Т.е., в приведенном выше примере в функции fu1(p []string) массив p будет 440 | содержать единственный элемент "menu", а в функции fsett1 - два элемента: 441 | идентификатор pButton (sName) и строка "first parameter".

442 |

Кроме функции, объявленной в вашем приложении, можно также использовать в качестве 443 | callback функции программный код, написанный на Harbour. Для этого вместо функции указываете nil, а 444 | вместо ее идентификатора - строку кода. В этом случае событие обрабатывается на GuiServer и 445 | в ваше приложение ничего не передаётся:

446 |
447 |
448 |      ...
449 |      // Вызов Harbour/HwGUI функции hwg_EndWindow(), которая закрывает главное окно
450 |      egui.AddMenuItem("Exit", 0, nil, "hwg_EndWindow()")
451 |      ...
452 |      // В 1-ую часть нижней панели (panelbot) главного окна пишется текущая дата
453 |      pButton.SetCallBackProc("onclick", nil, "hwg_WriteStatus(HWindow():GetMain(),1,Dtoc(Date()),.T.)")
454 |      
455 |
456 |

В External есть набор функций, вызывающих стандартные диалоги - информационные, выбора файла и 457 | пр. При вызове этих функций можно, а в тех случаях, когда вам требуется результат от диалога, необходимо 458 | указать функцию - получатель сообщения о закрытии диалога, т.е., callback функцию. Возьмем, например, диалог 459 | выбора цвета:

460 |
461 |
462 | func fsele_color(p []string) string {
463 |     // Функция первоначально вызывается из главного меню
464 | 	if p[0] == "menu" {
465 | 		egui.SelectColor(0, fsele_color, "fsele_color", "mm1")
466 | 	} else if p[0] == "mm1" {
467 | 	    // Функция была вызвана после закрытия диалога и первым параметром передана метка "mm1"
468 | 	    // Второй параметр - выбранный цвет
469 | 		iColor, _ := strconv.Atoi(p[1])
470 | 		egui.Widg("main.l1").SetColor(int32(iColor), -1)
471 | 	}
472 | 	return ""
473 | }
474 |
475 |

Здесь функции SelectColor(iColor int32, fu func([]string) string, sFunc string, sName string) переданы те же параметры fu и sCode (fsele_color, "fsele_color"), определяющие callback 476 | функцию и параметр sName ("mm1") - метка, идентифицирующая источник вызова callback 477 | функции - он передается ей первым в массиве строк - параметров. Вторым в данном случае передается значение 478 | выбранного цвета.

479 |

Если вызова callback функции при закрытии диалога не требуется, соответствующие параметры оставляйте пустыми:

480 |
481 |         egui.MsgInfo("Some message", "Title", nil, "", "")
482 |      
483 |
484 | 485 |

7. Widget - методы

486 |

487 | 488 | 489 | 490 | 491 | 492 | 493 | 495 | 496 | 497 | 498 | 499 | 501 | 502 | 504 | 505 | 506 | 507 |
Activate() bool Активирует главного окно или диалог
Close() bool Закрывает главное окно или диалог
AddWidget(pWidg *Widget) *Widget Добавляет виджет
SetText(sText string) Устанавливает текст виджета
SetImage(sImage string) Для виджета "bitmap" - изменить картинку, в качестве параметра передается путь и имя файла изображения
SetParam(sParam string, xParam interface{}) Устанавливает значение 494 | xParam атрибута sParam. Атрибуты sParam - это переменные объекта соответствующего виджета HwGUI.
GetText() string Возвращает текст виджета
SetColor(tColor int32, bColor int32) Устанавливает цвет текста и фона виджета
SetFont(pFont *Font) Устанавливает шрифт виджета
SetCallBackProc(sbName string,
  fu func([]string) string, sCode string,
  params ...string)
Устанавливает callback процедуру для виджета, sbName - идентификатор события, для которого 500 | устанавливается процедура, подробнее о callback функциях см. выше.
SetCallBackFunc(sbName string,
  fu func([]string) string, sCode string,
  params ...string)
Устанавливает callback функцию для виджета, sbName - идентификатор события, для которого 503 | устанавливается процедура, подробнее о callback функциях см. выше.
Move(iLeft, iTop, iWidth, iHeight int32) Перемещает виджет и/или изменяет его размер
Enable(bEnable bool) Enable или Disable виджет, в зависимости от параметра bEnable
Hide(bHide bool) Спрятать или показать виджет - в зависимости от параметра bHide
508 |
509 | 510 |

8. Создание меню

511 |

Главное меню программы создается между вызовами InitMainWindow() и Activate(), 512 | для этого используются функции Menu(sTitle string), EndMenu(), 513 | AddMenuItem(sName string, id int, fu func([]string) string, sCode string, params ...string), 514 | AddMenuSeparator(). Фигурные скобки после Menu() необязательны, они добавлены здесь для 515 | большей выразительности.

516 |
517 |
518 | package main
519 | 
520 | import (
521 |     "time"
522 |     egui "github.com/alkresin/external"
523 | )
524 | 
525 | func main() {
526 | 
527 |     if egui.Init("") != 0 {
528 |         return
529 |     }
530 | 
531 |     pWindow := &(egui.Widget{X: 100, Y: 100, W: 400, H: 280, Title: "GUI for Golang"})
532 |     egui.InitMainWindow(pWindow)
533 | 
534 |     // Создание меню начинается с вызва функции Menu("")
535 |     // с пустым заголовком
536 |     egui.Menu("")
537 |     {
538 |         // Субменю тоже создается с помощью вызова Menu(),
539 |         // но уже с текстом заголовка
540 |         egui.Menu("File")
541 |         {
542 |             // Добавляем пункт меню
543 |             // fu1() - callback функция, которая должна выполняться при
544 |             // выборе этого пункта меню
545 |             egui.AddMenuItem("Date", 0, fu1, "fu1" )
546 |             // Добавляем разделитель
547 |             egui.AddMenuSeparator()
548 |             // Здесь в качестве callback-функции указывается Harbour-код,
549 |             // который будет выполняться на GuiServer.
550 |             // Здесь это функция, которая закрывает главно окно.
551 |             egui.AddMenuItem("Exit", 0, nil, "hwg_EndWindow()")
552 |         }
553 |         egui.EndMenu()
554 |         egui.Menu("Help")
555 |         {
556 |             egui.AddMenuItem("About", 0, nil,
557 |                 "hwg_MsgInfo(\"My Golang GUI application\"+chr(10)+chr(13)+hwg_version(),\"About\")")
558 |         }
559 |         egui.EndMenu()
560 |     }
561 |     egui.EndMenu()
562 | 
563 |     pWindow.Activate()
564 |     egui.Exit()
565 | }
566 | 
567 | func fu1([]string) string {
568 | 
569 |     egui.MsgInfo("Today is " + time.Now().Format("02.01.2006"), "Info", nil, "", "")
570 |     return ""
571 | }
572 |
573 | 574 |

9. Функции

575 |

576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 605 | 606 | 607 | 610 | 611 | 612 | 613 | 614 | 615 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 629 | 630 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 658 | 659 | 660 | 661 | 663 | 664 | 665 | 666 | 669 | 670 | 672 |
Init(sOpt string) int Инициализация соединения с GuiServer, подробнее см. здесь
InitMainWindow(pWnd *Widget) bool Создает главное окно
InitDialog(pWnd *Widget) bool Создает диалоговое окно
BeginPacket() Начало пакетной передачи данных
EndPacket() Завершение пакетной передачи данных
WriteLog(sText string) Добавляет строку в файл egui.log
GetFont(sName string) *Font Ищет по имени в списке созданных в программе шрифтов, возвращает найденный указатель на структуру Font
GetStyle(sName string) *Style Ищет по имени в списке созданных в программе стилей, возвращает найденный указатель на структуру Style
Wnd(sName string) *Widget Ищет по имени в списке созданных в программе окон, возвращает найденный указатель на структуру Widget
Widg(sName string) *Widget Ищет по имени в списке созданных в программе виджетов, возвращает найденный указатель на структуру Widget
ToString(xParam ...interface{}) string Преобразует параметры в json-строку для отправки на GuiServer
OpenMainForm(sForm string) bool Открывает xml форму с описанием главного окна, предварительно подготовленную с помощью HwGUI Designer
OpenForm(sForm string) bool Открывает xml форму с описанием диалогового окна, предварительно подготовленную с помощью HwGUI Designer
OpenReport(sForm string) bool Открывает xml форму с описанием отчета, предварительно подготовленную с помощью HwGUI Designer
CreateFont(pFont *Font) *Font Создает шрифт на основе структуры Font
CreateStyle(pStyle *Style) *Style Создает стиль на основе структуры Style
CreateHighliter(sName string, sCommands string,
   sFuncs string, 594 | sSingleLineComm string,
   sMultiLineComm string, bCase bool) *Highlight
Создает Highlight структуру для подсветки синтаксиса в виджете редактирования cedit.
SetHighliter(pEdit *Widget, p *Highlight) Устанавливает структуру Highlight в виджет типа cedit.
SetHiliOpt(pEdit *Widget, iGroup int,
   pFont *Font, tColor int32, bColor int32)
Устанавливает опции подсветки синтаксиса для виджета типа cedit.
InitPrinter(pPrinter *Printer, sFunc string,
   fu func([]string) string, sMark string) *Printer
Инициализирует принтер - подробнее см. здесь.
EvalProc(s string) Передает на GuiServer для выполнения программный код на языке Harbour
EvalFunc(s string) []byte Передает на GuiServer для выполнения программный код на языке Harbour, возвращает результат
GetValues(pWnd *Widget, aNames []string) []string Возвращает массив строк - значений 604 | виджетов окна pWnd, перечисленных по идентификаторам в массиве aNames.
GetVersion(i int) string Возвращает версию GuiServer. Если i==0 - номер версии, i==1 - полную строку с номером версии, i==2 - версию GuiServer, HwGUI и Harbour
MsgInfo(sMessage string, sTitle string,
   fu func([]string) string, sFunc string, sName string)
Создает стандартный Info диалог. sMessage - текст сообщения, sTitle - заголовок окна;
fu, sFunc - callback функция 608 | и ее идентификатор, эта функция вызывается после закрытия диалога; sName - эта строка будет первой в массиве, передаваемом callback 609 | функции при ее вызове. Подробнее см. выше.
MsgStop(sMessage string, sTitle string,
   fu func([]string) string, sFunc string, sName string)
Создает стандартный Stop диалог. Параметры те же, что и в MsgInfo.
MsgYesNo(sMessage string, sTitle string,
   fu func([]string) string, sFunc string, sName string)
Создает стандартный YesNo диалог. Параметры те же, что и в MsgInfo. Возвращает true или false через callback функцию.
MsgGet(sMessage string,
   sTitle string, iStyle int32,
   fu func([]string) string, sFunc string, sName string)
Создает диалог для ввода строки. Параметры те же, что и в MsgInfo, iStyle - стиль виджета 616 | Edit. Возвращает введенную строку через callback функцию.
Choice(arr []string, sTitle string,
   fu func([]string) string, sFunc string, sName string)
Создает диалог для выбора из массива строк arr. Остальные параметры те же, что и в MsgInfo. Возвращает номер выбранной строки через callback функцию.
SelectFile(sPath string,
   fu func([]string) string, sFunc string,
   sName string)
Вызывает стандартный диалог выбора файла, sPath - начальный путь. Остальные параметры те же, что и в MsgInfo. Возвращает имя файла через callback функцию.
SelectFolder(fu func([]string) string,
   sFunc string, sName string)
Вызывает стандартный диалог выбора каталога. Параметры те же, что и в MsgInfo. Возвращает имя каталога через callback функцию.
SelectColor(iColor int32,
   fu func([]string) string, sFunc string, sName string)
Вызывает стандартный диалог выбора цвета, iColor - начальный цвет. Остальные параметры те же, что и в MsgInfo. Возвращает код цвета через callback функцию.
SelectFont(fu func([]string) string,
   sFunc string, sName string)
Вызывает стандартный диалог выбора шрифта. Параметры те же, что и в MsgInfo. Возвращает параметры выбранного шрифта через callback функцию.
InsertNode(pTree *Widget,
   sNodeName string, sNodeNew string,
   sTitle string, 628 | sNodeNext string,
   aImages []string, fu func([]string) string, sCode string)
Добавляет новый элемент в дерево - pTree.
SelectNode(pTree *Widget, sNodeName string) Делает текущим узел дерева, указывая его по имени 631 | sNodeName, заданному в InsertNode().
PBarStep(pPBar *Widget) Передает прогресс-бару команду сделать следующий шаг.
PBarSet(pPBar *Widget, iPos int) { Устанавливает прогресс-бар в положение iPos.
InitTray(sIcon string,
   sMenuName string, sTooltip string)
Устанавливает опции размещения иконки главного окна в tray (только для Windows).
ModifyTrayIcon(sIcon string) Изменяет иконку в tray.
RadioEnd(p *Widget, iSel int) Завершает объявление радиогруппы.
TabPage(pTab *Widget, sCaption string) Начинает объявление страницы виджета tab
TabPageEnd(pTab *Widget) Завершает объявление страницы виджета tab
BrwSetArray(pBrowse *Widget,
   arr *[][]string)
Устанавливает массив для отображения в виджет browse
BrwGetArray(pBrowse*Widget) [][]string Получает отредактированный массив от обратно от виджета browse.
BrwSetColumn(pBrowse *Widget, ic int,
   sHead string, iAlignHead int, 643 |
   iAlignData int, bEditable bool, iLength int)
Устанавливает опции колонки номер ic виджета browse:
644 | iAlignHead - выравнивание заголовка, iAlignData - выравнивание ячейки, 645 | bEditable - возможность редактирования, iLength - длина.
BrwSetColumnEx(pBrowse *Widget, ic int,
   sParam string, xParam interface{})
Устанавливает дополнительные опции колонки номер ic виджета browse; sParam - название опции, xParam - значение.
BrwDelColumn(pBrowse *Widget, ic int) Удаляет колонку номер ic виджета browse
SetVar(sVarName string, sValue string) Создает на GuiServer Public переменную sVarName (если ее нет) и устанавливает ее значение sValue
GetVar(sVarName string) string Возвращает с GuiServer значение переменной sVarName
SetImagePath(sValue string) Передает на GuiServer путь по умолчанию для файлов изображений
SetPath(sValue string) Передает на GuiServer путь по умолчанию для чтения/записи файлов
SetDateFormat(sValue string) Передает на GuiServer желаемый формат вывода даты
Menu(sTitle string) Начинает объявление меню или субменю
MenuContext(sName string) Начинает объявление контекстного меню
ShowMenuContext(sName string,
   pWnd *Widget)
Показывает контекстное 657 | меню, sName - идентификатор меню, pWnd - окно, к которому оно относится.
EndMenu() Завершает объявление меню или субменю
AddMenuItem(sName string, id int,
   fu func([]string) string,
   sCode string, params ...string)
Добавляет пункт меню, sName - имя, id - числовой идентификатор, остальные параметры те же, что и в MsgInfo.
AddCheckMenuItem(sName string,
   id int,fu func([]string) string, 662 |
   sCode string, params ...string)
Добавляет пункт меню, который можно помечать. Параметры - те же, что в AddMenuItem().
AddMenuSeparator() Добавляет в меню линию-разделитель
MenuItemEnable(sWndName string,
   sMenuName string,
   iItem int, bValue bool)
Меняет доступность элемента меню - в зависимости от значения bValue. Здесь 667 | sWndName - идентификатор окна, если это главное меню или меню диалога, sMenuName - идентификатор 668 | меню, если это контекстное меню, iItem - id пункта меню.
MenuItemCheck(sWndName string,
   sMenuName string,
   iItem int, bValue bool)
Отмечает или снимает отметку с пункта меню - в зависимости от значения bValue. Параметры 671 | те же, что и в MenuItemEnable.
673 |
674 | 675 |

10. Печать

676 |

Первое, что надо сделать при при создании печатного документа - это создать структуру Printer, которая 677 | имеет следующий вид:

678 |
679 |
680 | type Printer struct {
681 | 	Name       string      // Идентификатор принтера
682 | 	SPrinter   string      // Имя принтера
683 | 	BPreview   bool        // True, если нужно превью
684 | 	IFormType  int         // Тип бумаги, см. в Приложении, по умолчанию - DMPAPER_A4
685 | 	BLandscape bool        // True, если нужна альбомная ориентация
686 | }
687 |
688 |

Здесь Name - идентификатор принтера, поле, аналогичное таким же идентификаторам в Widget, Font, Style. SPrinter - имя 689 | принтера в системе. Если SPrinter не указан, будет использоваться принтер по умолчанию; если 690 | его значение - "...", то появится стандартное диалоговое окно для выбора принтера.

691 |

Затем инициализируем принтер с помощью функции InitPrinter(), которой передаем созданную структуру.

692 |
693 |      InitPrinter(pPrinter *Printer, sFunc string, fu func([]string) string, sMark string) *Printer
694 |      
695 |

Параметры sFunc, fu, sMark (те же, что в функциях, вызывающих стандартные диалоги), используются в 696 | тех случаях, когда поле Sprinter структуры Printer равно "...", т.е., требуется стандартный 697 | диалог выбора принтера. Напомню, fu - функция, которая должна быть вызвана по завершении диалога, sFunc - ее 698 | идентификатор, sMark - первый элемент массива строк, который будет передан этой функции.

699 |

После инициализации принтера можно приступать к печати. Ниже - список методов структуры Printer, которые 700 | при этом используются:

701 | 702 | 703 | 704 | 707 | 708 | 709 | 710 | 711 | 712 |
AddFont(pFont *Font) *Font Добавляет шрифт
SetFont(pFont *Font) Устанавливает текущий шрифт для печати
Say(iTop, iLeft, iRight, iBottom int32, sText string, iOpt int32) Печатает 705 | текст sText в прямоугольной области с координатами iTop, iLeft, iRight, iBottom. iOpt - тип 706 | выравнивания ( DT_LEFT, DT_RIGHT, DT_CENTER )
Line(iTop, iLeft, iRight, iBottom int32) Рисует линию в заданных координатах
Box(iTop, iLeft, iRight, iBottom int32) Рисует прямоугольник в заданных координатах
StartPage() Начинает новую страницу печати, этот вызов обязателен.
EndPage() Завершает печать страницы, этот вызов обязателен.
End() Завершает печать, освобождает принтер.
713 |

Все координаты указаны в миллиметрах.

714 |

Вот пример печати одной простой страницы:

715 |
716 |
717 | func fprint(p []string) string {
718 |     // Функция первоначально вызывается из главного меню
719 | 	if p[0] == "menu" {
720 | 		egui.InitPrinter(&egui.Printer{SPrinter: "...", BPreview: true}, "fprint", fprint, "mm1")
721 | 	} else {
722 | 	    // Повторно она же вызывается после выбора принтера из стандартного диалога
723 | 		pPrinter := egui.PLastPrinter
724 | 		pFont := pPrinter.AddFont(&egui.Font{Family: "Times New Roman", Height: 10})
725 | 		pPrinter.StartPage()
726 | 		pPrinter.SetFont(pFont)
727 | 		pPrinter.Box(5, 5, 200, 282)
728 | 		pPrinter.Say(50, 10, 165, 26, "Printing first sample !", egui.DT_CENTER)
729 | 		pPrinter.Line(45, 30, 170, 30)
730 | 		pPrinter.Line(45, 5, 45, 30)
731 | 		pPrinter.Line(170, 5, 170, 30)
732 | 		pPrinter.Say(50, 120, 150, 132, "----------", egui.DT_CENTER)
733 | 		pPrinter.Box(50, 134, 160, 146)
734 | 		pPrinter.Say(50, 135, 160, 146, "End Of Report", egui.DT_CENTER)
735 | 		pPrinter.EndPage()
736 | 		pPrinter.End()
737 | 	}
738 | 	return ""
739 | }
740 |
741 | 742 |

11. Заключение

743 |

Если что-то осталось неясным, какой-то информации не хватает, смотрите примеры в external/testdata, 744 | раздел по External в туториале etutor. 745 | Еще один источник информации - группа Guiserver на GoogleGroups.

746 |

В некоторых случаях вам придется обратиться к документации 747 | Harbour и 748 | HwGUI. Так, чтобы установить в 749 | качестве callback функции Harbour код, выполняемый на GuiServer, или выполнить код на GuiServer 750 | с помошью EvalProc(), EvalFunc(), желательно чуть-чуть знать и понимать язык Harbour и HwGUI. Точно 751 | так же, чтобы непосредственно установить какой-либо атрибут виджета с помощью SetParam() или BrwSetColumnEx(), надо 752 | знать , что это за атрибуты (подсказка: это переменные классов HwGUI).

753 |

Фактически, Harbour - встроенный скриптовый язык вашего приложения. Наличие такого инструмента может оказаться очень полезным.

754 |
755 | 756 |

A. Приложения

757 |

Anchor

758 |

Список возможных значений поля Anchor структуры Widget:

759 | 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | 770 | 771 |
A_TOPLEFT = -1 // Anchors control to the top and left borders of the container and does not change the distance between the top and left borders. (Default)
A_TOPABS = 1 // Anchors control to top border of container and does not change the distance between the top border.
A_LEFTABS = 2 // Anchors control to left border of container and does not change the distance between the left border.
A_BOTTOMABS = 4 // Anchors control to bottom border of container and does not change the distance between the bottom border.
A_RIGHTABS = 8 // Anchors control to right border of container and does not change the distance between the right border.
A_TOPREL = 16 // Anchors control to top border of container and maintains relative distance between the top border.
A_LEFTREL = 32 // Anchors control to left border of container and maintains relative distance between the left border.
A_BOTTOMREL = 64 // Anchors control to bottom border of container and maintains relative distance between the bottom border.
A_RIGHTREL = 128 // Anchors control to right border of container and maintains relative distance between the right border.
A_HORFIX = 256 // Anchors center of control relative to left and right borders but remains fixed in size.
A_VERTFIX = 512 // Anchors center of control relative to top and bottom borders but remains fixed in size.
772 |

Эти значения можно комбинировать. Например, egui.A_LEFTABS+egui.A_RIGHTABS - такой виджет, привязанный 773 | к левому и правому краям, будет растягиваться в ширину при растягивании окна.

774 |
775 | 776 |

WinAPI стили

777 | 778 | 779 | 780 | 781 | 782 | 783 | 784 | 785 | 786 | 787 | 788 | 789 |
DT_LEFT Выравнивание влево
DT_CENTER Выравнивание вправо
DT_RIGHT Выравнивание по центру
ES_PASSWORD Ввод пароля (для Edit)
ES_MULTILINE Многострочный Edit
ES_READONLY Edit - только для чтения
WS_HSCROLL Горизонтальный скролл
WS_VSCROLL Вертикальный скролл
WND_NOTITLE
WND_NOSYSMENU
WND_NOSIZEBOX
790 |
791 | 792 |

Типы бумаги

793 | 794 | 795 | 796 | 797 | 798 |
DMPAPER_A3A3 297 x 420 mm
DMPAPER_A4A4 210 x 297 mm
DMPAPER_A5A5 148 x 210 mm
DMPAPER_A6A6 105 x 148 mm
799 |
800 | 801 |

Типы градиента в Style

802 | 803 | 804 | 805 | 806 | 807 | 808 | 809 | 810 | 811 | 812 |
1 вертикальный сверху вниз
2 вертикальный снизу вверх
3 горизонтальный слева направо
4 горизонтальный справа налево
5 диагональный справа вверх
6 диагональный слева вниз
7 диагональный справа вниз
8 диагональный слева вверх
9 радиальный градиент
813 |
814 | 815 |

Идентификаторы событий

816 |

Здесь приведен список идентификаторов событий, поддерживаемых к моменту написания этого 817 | руководства - тех идентификаторов, которые можно указать в SetCallBackProc() и SetCallBackFunc().

818 | 819 | 820 | 821 | 822 | 823 | 824 | 825 | 826 | 827 | 828 |
oninit
ondestroy
onclick
onsize
onpaint
onrclick
ondblclick
onenter
onposchanged
829 |
830 | 831 |
832 | 833 | 834 | 835 |
836 |
837 | 838 | 843 | 844 | 845 | 846 | -------------------------------------------------------------------------------- /extmain.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Alexander S.Kresin , http://www.kresin.ru 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | // 5 | // Package external is a GUI framework for Go language. 6 | // External is a Go library to build GUI application, using a 7 | // standalone GUI server application: https://github.com/alkresin/guiserver. 8 | package external 9 | 10 | import ( 11 | "encoding/json" 12 | "fmt" 13 | "net" 14 | "os" 15 | "os/exec" 16 | "strconv" 17 | "strings" 18 | "sync" 19 | "time" 20 | ) 21 | 22 | const ( 23 | VerProto = "1.1" 24 | Version = "1.1" 25 | FileRoot = "gs" 26 | ) 27 | 28 | type ConnEx struct { 29 | iType int8 30 | iPort int 31 | sIp string 32 | sFileName string 33 | conn net.Conn 34 | f *os.File 35 | } 36 | 37 | var sLogName = "egui.log" 38 | var bEndProg = false 39 | var bWait = false 40 | 41 | //var connOut, connIn net.Conn 42 | var bConnExist = false 43 | 44 | var pConnOut, pConnIn *ConnEx 45 | var sDir = "" 46 | var sFileRoot = FileRoot 47 | 48 | var bPacket = false 49 | var sPacketBuf string 50 | 51 | var aRunProc [][]string 52 | var muxRunProc sync.Mutex 53 | 54 | var aRunFu []func() 55 | var muxRunFu sync.Mutex 56 | 57 | // Init runs, if needed, the Guiserver application, and connects to it. 58 | // It returns 0, if the connection is successful, 1 - in other case, 59 | // 2 -if a protocol version of a GuiServer isn't equal to a local protocol. 60 | // The sOpt argument specifies connection details. It may contain following strings: 61 | // guiserver= 62 | // address= 63 | // port= 64 | // log=<0, 1 or 2> - logging level 65 | func Init(sOpt string) int { 66 | 67 | var err error 68 | 69 | iConnType := 1 70 | iPort := 3101 71 | sServer := "guiserver" 72 | sIp := "127.0.0.1" 73 | sFileName := "" 74 | sLog := "" 75 | if sOpt != "" { 76 | var arr []string 77 | sep := "\r\n" 78 | if !strings.Contains(sOpt, sep) { 79 | if sep = "\n"; !strings.Contains(sOpt, sep) { 80 | sep = "" 81 | } 82 | } 83 | if sep == "" { 84 | arr = make([]string, 1, 1) 85 | arr[0] = sOpt 86 | } else { 87 | arr = strings.Split(sOpt, sep) 88 | } 89 | for i := 0; i < len(arr); i++ { 90 | s := strings.ToLower(arr[i]) 91 | if len(s) > 9 && s[:9] == "guiserver" { 92 | sServer = strings.TrimSpace(s[10:]) 93 | } else if len(s) > 8 && s[:7] == "address" { 94 | sIp = strings.TrimSpace(s[8:]) 95 | } else if len(s) > 5 && s[:4] == "port" { 96 | iPort, _ = strconv.Atoi(strings.TrimSpace(s[5:])) 97 | } else if len(s) > 5 && s[:4] == "type" { 98 | iConnType, _ = strconv.Atoi(strings.TrimSpace(s[5:])) 99 | } else if len(s) > 5 && s[:3] == "dir" { 100 | sDir = strings.TrimSpace(s[4:]) 101 | } else if len(s) > 5 && s[:4] == "file" { 102 | sFileRoot = strings.TrimSpace(s[5:]) 103 | if strings.Contains(sFileRoot, "*") { 104 | sz := strconv.Itoa(int((time.Now().UnixNano() / 1000000) % 100000)) 105 | sFileRoot = strings.Replace(sFileRoot, "*", sz, -1) 106 | } 107 | } else if len(s) > 2 && s[:3] == "log" { 108 | if s[4:5] == "1" { 109 | sLog = "-log1" 110 | } else if s[4:5] == "2" { 111 | sLog = "-log2" 112 | } 113 | } 114 | } 115 | } 116 | 117 | buf := make([]byte, 128) 118 | 119 | if iConnType == 2 { 120 | if sDir == "" { 121 | sDir = os.TempDir() 122 | } 123 | sFileName = sDir + string(os.PathSeparator) + sFileRoot 124 | if sServer != "" { 125 | os.Remove(sFileName) 126 | } 127 | } 128 | 129 | if sServer != "" { 130 | var cmd *exec.Cmd 131 | if iConnType == 2 && (sDir != "" || sFileRoot != FileRoot) { 132 | cmd = exec.Command(sServer, fmt.Sprintf("-p%d", iPort), "-t2", sLog, "-d"+sDir, "-f"+sFileRoot ) 133 | } else { 134 | cmd = exec.Command(sServer, fmt.Sprintf("-p%d", iPort), fmt.Sprintf("-t%d", iConnType), sLog) 135 | } 136 | cmd.Start() 137 | } 138 | time.Sleep(100 * time.Millisecond) 139 | 140 | pConnOut = &ConnEx{ iType: int8(iConnType), iPort: iPort, sIp: sIp, sFileName: sFileName+".gs1" } 141 | pConnIn = &ConnEx{ iType: int8(iConnType), iPort: iPort+1, sIp: sIp, sFileName: sFileName+".gs2" } 142 | 143 | if !pConnOut.Connect() { 144 | return 1 145 | } 146 | 147 | bConnExist = true 148 | 149 | iBufLen, err := pConnOut.Read(&buf) 150 | 151 | if err != nil { 152 | WriteLog(fmt.Sprintln(err)) 153 | pConnOut.Close() 154 | bConnExist = false 155 | return 1 156 | } 157 | sVer := string(buf[:iBufLen-1]) 158 | sVer = sVer[(strings.Index(sVer, "/") + 1):] 159 | 160 | if !pConnIn.Connect() { 161 | return 1 162 | } 163 | 164 | _, err = pConnIn.Read(&buf) 165 | 166 | if err != nil { 167 | WriteLog(fmt.Sprintln(err)) 168 | Exit() 169 | return 1 170 | } 171 | 172 | if sVer != VerProto { 173 | WriteLog("\r\nProtocol version mismatched. Need " + VerProto + ", received " + sVer) 174 | Exit() 175 | return 2 176 | } 177 | 178 | if iConnType == 2 { 179 | pConnIn.Write( "+[\"Ok\"]\n" ) 180 | } 181 | 182 | WriteLog( "Init-1\r\n" ) 183 | go listen(iPort + 1) 184 | time.Sleep(100 * time.Millisecond) 185 | 186 | return 0 187 | } 188 | 189 | // Exit closes the connection to Guiserver. 190 | func Exit() { 191 | if bConnExist { 192 | bConnExist = false 193 | pConnOut.Write("+[\"exit\"]\n") 194 | time.Sleep(10 * time.Millisecond) 195 | pConnOut.Close() 196 | pConnIn.Close() 197 | } 198 | } 199 | 200 | func listen(iPort int) { 201 | 202 | var bErr bool 203 | 204 | buffer := make([]byte, 1024) 205 | arr := make([]string, 5) 206 | for { 207 | 208 | bErr = false 209 | length, err := pConnIn.Read(&buffer) 210 | 211 | if err != nil { 212 | //WriteLog("Read error\r\n") 213 | return 214 | } 215 | 216 | if buffer[0] != byte('+') || buffer[length-1] != byte('\n') { 217 | bErr = true 218 | } 219 | 220 | if !bErr { 221 | err = json.Unmarshal(buffer[1:length-1], &arr) 222 | if err != nil { 223 | WriteLog("Unmarshal error\r\n") 224 | bErr = true 225 | } 226 | } 227 | 228 | //fmt.Printf("Received command %d\t:%s %d\n", length, string(buffer[:length]), len(arr)) 229 | 230 | if !bErr && len(arr) > 0 { 231 | switch arr[0] { 232 | case "runproc": 233 | //sendResponse(connIn, "[\"Ok\"]") 234 | pConnIn.Write( "+[\"Ok\"]\n" ) 235 | if len(arr) > 1 { 236 | if bWait { 237 | tmp := make([]string, len(arr)) 238 | muxRunProc.Lock() 239 | copy(tmp, arr) 240 | aRunProc = append(aRunProc, tmp) 241 | muxRunProc.Unlock() 242 | } else { 243 | runproc(arr) 244 | } 245 | } else { 246 | bErr = true 247 | } 248 | case "runfunc": 249 | if len(arr) > 1 { 250 | if fnc, bExist := mfu[arr[1]]; bExist { 251 | var ap []string 252 | if len(arr) > 2 { 253 | ap = make([]string, 5) 254 | err = json.Unmarshal([]byte(arr[2]), &ap) 255 | if err != nil { 256 | WriteLog(fmt.Sprintf("runproc param Unmarshal error (%s)\r\n", arr[2])) 257 | } 258 | } 259 | //WriteLog(fmt.Sprintf("pgo> (%s) len:%d\r\n",arr[2],len(ap) )) 260 | s := fnc(ap) 261 | b, _ := json.Marshal(s) 262 | //sendResponse(connIn, string(b)) 263 | pConnIn.Write("+" + string(b) + "\n") 264 | } else { 265 | //sendResponse(connIn, "[\"Err\"]") 266 | pConnIn.Write("+[\"Err\"]" + "\n") 267 | } 268 | } else { 269 | bErr = true 270 | //sendResponse(connIn, "[\"Err\"]") 271 | pConnIn.Write("+[\"Err\"]\n") 272 | } 273 | case "exit": 274 | //sendResponse(connIn, "[\"Ok\"]") 275 | pConnIn.Write("+[\"Ok\"]\n") 276 | if len(arr) > 1 { 277 | oW := Wnd(arr[1]) 278 | if oW != nil { 279 | oW.delete() 280 | } 281 | } else { 282 | bErr = true 283 | } 284 | case "endapp": 285 | //sendResponse(connIn, "[\"Goodbye\"]") 286 | pConnIn.Write("+[\"Goodbye\"]\n") 287 | time.Sleep(100 * time.Millisecond) 288 | bEndProg = true 289 | //connIn.Close() 290 | //pConnIn.Close() 291 | Exit() 292 | //WriteLog("The End") 293 | return 294 | default: 295 | //sendResponse(connIn, "[\"Error\"]") 296 | pConnIn.Write("+[\"Error\"]\n") 297 | bErr = true 298 | } 299 | } 300 | if bErr { 301 | WriteLog(fmt.Sprintf("Wrong message: %s]\r\n", string(buffer[:length]))) 302 | } 303 | } 304 | } 305 | 306 | /* 307 | func read(conn net.Conn, pBuff *[]byte) (int, error) { 308 | *pBuff = (*pBuff)[:0] 309 | tmp := make([]byte, 256) 310 | for { 311 | length, err := conn.Read(tmp) 312 | if err != nil { 313 | WriteLog("Read error\r\n") 314 | return 0, err 315 | } 316 | *pBuff = append(*pBuff, tmp[:length]...) 317 | if tmp[length-1] == '\n' { 318 | break 319 | } 320 | } 321 | return len(*pBuff), nil 322 | } 323 | 324 | func sendResponse(conn net.Conn, s string) { 325 | conn.Write([]byte("+" + s + "\n")) 326 | } 327 | */ 328 | 329 | func sendout(s string) bool { 330 | 331 | var err error 332 | 333 | if bPacket { 334 | sPacketBuf += "," + s 335 | } else { 336 | if !bConnExist { 337 | WriteLog("sendout: No connection established.\r\n") 338 | return false 339 | } 340 | 341 | //_, err = connOut.Write([]byte("+" + s + "\n")) 342 | _, err = pConnOut.Write("+" + s + "\n") 343 | if err != nil { 344 | fmt.Println(err) 345 | return false 346 | } 347 | 348 | buf := make([]byte, 128) 349 | //_, err = connOut.Read(buf) 350 | _, err = pConnOut.Read(&buf) 351 | if err != nil { 352 | fmt.Println(err) 353 | return false 354 | } 355 | } 356 | return true 357 | } 358 | 359 | func sendoutAndReturn(s string) []byte { 360 | 361 | var err error 362 | buf := make([]byte, 1024) 363 | 364 | if !bConnExist { 365 | WriteLog("sendoutAndReturn: No connection established.\r\n") 366 | return []byte("") 367 | } 368 | 369 | //_, err = connOut.Write([]byte("+" + s + "\n")) 370 | _, err = pConnOut.Write("+" + s + "\n") 371 | if err != nil { 372 | fmt.Println(err) 373 | return []byte("") 374 | } 375 | //length, err = connOut.Read(buf) 376 | //length, err := read(connOut, &buf) 377 | length, err := pConnOut.Read(&buf) 378 | if err != nil { 379 | fmt.Println(err) 380 | return []byte("") 381 | } 382 | return buf[:length-1] 383 | } 384 | 385 | // BeginPacket begins a sequence of functions, which creates or modifies GUI elements, 386 | // for to join messages to Guiserver to one packet. 387 | func BeginPacket() { 388 | bPacket = true 389 | sPacketBuf = "[\"packet\"" 390 | } 391 | 392 | // EndPacket completes a sequence of functions, started by BeginPacket 393 | func EndPacket() { 394 | bPacket = false 395 | sendout(sPacketBuf + "]") 396 | sPacketBuf = "" 397 | } 398 | 399 | // WriteLog writes the sText to a log file egui.log. 400 | func WriteLog(sText string) { 401 | 402 | f, err := os.OpenFile(sLogName, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600) 403 | if err != nil { 404 | return 405 | } 406 | defer f.Close() 407 | 408 | f.WriteString(sText) 409 | 410 | } 411 | 412 | // RegFunc adds the fu func to a map of functions, 413 | // sName argument is a function identifier - a key of this map. 414 | // You may need to call this function in case of using HWGui's xml forms. 415 | func RegFunc(sName string, fu func([]string) string) { 416 | 417 | if mfu == nil { 418 | mfu = make(map[string]func([]string) string) 419 | } 420 | mfu[sName] = fu 421 | } 422 | 423 | // AddFuncToIdle adds a function to be executed while wait state. 424 | func AddFuncToIdle(fu func()) { 425 | muxRunFu.Lock() 426 | aRunFu = append(aRunFu, fu) 427 | muxRunFu.Unlock() 428 | } 429 | 430 | func runproc(arr []string) { 431 | if fnc, bExist := mfu[arr[1]]; bExist { 432 | var ap []string 433 | if len(arr) > 2 { 434 | ap = make([]string, 5) 435 | err := json.Unmarshal([]byte(arr[2]), &ap) 436 | if err != nil { 437 | WriteLog(fmt.Sprintf("runproc param Unmarshal error (%s)\r\n", arr[2])) 438 | } 439 | } 440 | //WriteLog(fmt.Sprintf("pgo> (%s) len:%d\r\n",arr[2],len(ap) )) 441 | fnc(ap) 442 | } 443 | } 444 | 445 | func Wait() { 446 | bWait = true 447 | for !bEndProg { 448 | for { 449 | muxRunFu.Lock() 450 | if len(aRunFu) == 0 { 451 | muxRunFu.Unlock() 452 | break 453 | } 454 | fu := aRunFu[0] 455 | aRunFu = append(aRunFu[:0], aRunFu[1:]...) 456 | muxRunFu.Unlock() 457 | 458 | fu() 459 | } 460 | for { 461 | muxRunProc.Lock() 462 | if len(aRunProc) == 0 { 463 | muxRunProc.Unlock() 464 | break 465 | } 466 | arr := aRunProc[0] 467 | aRunProc = append(aRunProc[:0], aRunProc[1:]...) 468 | muxRunProc.Unlock() 469 | 470 | runproc(arr) 471 | } 472 | time.Sleep(20 * time.Millisecond) 473 | } 474 | bWait = false 475 | } 476 | 477 | func (p *ConnEx) Connect() bool { 478 | 479 | var err error 480 | 481 | if p.iType == 1 { 482 | p.conn, err = net.Dial("tcp4", fmt.Sprintf("%s:%d", p.sIp, p.iPort)) 483 | if err != nil { 484 | time.Sleep(1000 * time.Millisecond) 485 | p.conn, err = net.Dial("tcp4", fmt.Sprintf("%s:%d", p.sIp, p.iPort)) 486 | if err != nil { 487 | time.Sleep(3000 * time.Millisecond) 488 | p.conn, err = net.Dial("tcp4", fmt.Sprintf("%s:%d", p.sIp, p.iPort)) 489 | if err != nil { 490 | WriteLog(fmt.Sprintln(p.sIp, p.iPort)) 491 | WriteLog(fmt.Sprintln(err)) 492 | return false 493 | } 494 | } 495 | } 496 | } else if p.iType == 2 { 497 | //os.Remove(p.sFileName) 498 | p.f,err = os.OpenFile(p.sFileName, os.O_RDWR, 0644) 499 | if err != nil { 500 | time.Sleep(1000 * time.Millisecond) 501 | p.f,err = os.OpenFile(p.sFileName, os.O_RDWR, 0644) 502 | if err != nil { 503 | time.Sleep(3000 * time.Millisecond) 504 | p.f,err = os.OpenFile(p.sFileName, os.O_RDWR, 0644) 505 | if err != nil { 506 | WriteLog(p.sFileName) 507 | WriteLog(fmt.Sprintln(err)) 508 | return false 509 | } 510 | } 511 | } 512 | } 513 | 514 | return true 515 | } 516 | 517 | func (p *ConnEx) Close() { 518 | 519 | if p.iType == 1 { 520 | p.conn.Close() 521 | } else if p.iType == 2 { 522 | p.f.Close() 523 | } 524 | } 525 | 526 | func (p *ConnEx) Read(pBuff *[]byte) (int, error) { 527 | 528 | *pBuff = (*pBuff)[:0] 529 | tmp := make([]byte, 256) 530 | if p.iType == 1 { 531 | for { 532 | length, err := p.conn.Read(tmp) 533 | if err != nil { 534 | WriteLog("Read error\r\n") 535 | return 0, err 536 | } 537 | *pBuff = append(*pBuff, tmp[:length]...) 538 | if tmp[length-1] == '\n' { 539 | break 540 | } 541 | } 542 | return len(*pBuff), nil 543 | } else if p.iType == 2 { 544 | b1 := make([]byte, 1) 545 | for bConnExist { 546 | p.f.Seek(0,0) 547 | _, err := p.f.Read(b1) 548 | if err != nil { 549 | WriteLog("Read error\r\n") 550 | return 0, err 551 | } 552 | if b1[0] == 2 { 553 | for ;; { 554 | n, err := p.f.Read(tmp) 555 | if err != nil { 556 | WriteLog("Read error\r\n") 557 | return 0, err 558 | } 559 | iPos := -1 560 | for i := 0; i < n; i++ { 561 | if tmp[i] == 10 { 562 | iPos = i 563 | break 564 | } 565 | } 566 | if iPos >= 0 { 567 | *pBuff = append(*pBuff, tmp[:iPos+1]...) 568 | break 569 | } else if n < len(tmp) { 570 | *pBuff = append(*pBuff, tmp[:n+1]...) 571 | break 572 | } else { 573 | *pBuff = append(*pBuff, tmp...) 574 | } 575 | } 576 | break 577 | } else { 578 | time.Sleep(2 * time.Millisecond) 579 | } 580 | } 581 | //WriteLog(string(*pBuff)) 582 | return len(*pBuff), nil 583 | } 584 | 585 | return 0, nil 586 | } 587 | 588 | func (p *ConnEx) Write( s string ) (int, error) { 589 | 590 | var err error 591 | 592 | if p.iType == 1 { 593 | _, err = p.conn.Write( []byte(s) ) 594 | } else if p.iType == 2 { 595 | p.f.WriteAt( []byte(s), 1 ) 596 | p.f.WriteAt( []byte{1}, 0 ) 597 | } 598 | 599 | return 0, err 600 | } -------------------------------------------------------------------------------- /extmenu.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Alexander S.Kresin , http://www.kresin.ru 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package external 6 | 7 | import ( 8 | "strconv" 9 | "encoding/json" 10 | ) 11 | 12 | var sMenu = "" 13 | var iStackLen = 0 14 | 15 | // Menu starts a window's menu or submenu definition, sTitle is a menu title. 16 | func Menu(sTitle string) { 17 | 18 | if sMenu == "" { 19 | sMenu = "[\"menu\",[" 20 | } else { 21 | if sMenu[len(sMenu)-1] != '[' { 22 | sMenu += "," 23 | } 24 | sMenu += "[\"" + sTitle + "\",[" 25 | iStackLen++ 26 | } 27 | } 28 | 29 | // MenuContext starts a context menu, sName is a menu identifier 30 | func MenuContext(sName string) { 31 | 32 | if sMenu == "" { 33 | sMenu = "[\"menucontext\",\"create\",\"" + sName + "\",[" 34 | } 35 | } 36 | 37 | // Show context menu on the screen 38 | func ShowMenuContext(sName string, pWnd *Widget) { 39 | var sWndName string 40 | if pWnd == nil { 41 | sWndName = "" 42 | } else { 43 | sWndName = pWnd.Name 44 | } 45 | sendout("[\"menucontext\",\"show\",\"" + sName + "\",\"" + sWndName + "\"]") 46 | } 47 | 48 | // EndMenu completes a window's menu or submenu definition 49 | func EndMenu() { 50 | sMenu += "]]" 51 | if iStackLen == 0 { 52 | sendout(sMenu) 53 | sMenu = "" 54 | } else { 55 | iStackLen-- 56 | } 57 | } 58 | 59 | func getscode(fu func([]string) string, sCode string, params ...string) string { 60 | if fu != nil { 61 | RegFunc(sCode, fu) 62 | sCode = "pgo(\"" + sCode + "\",{\"menu\"" 63 | for _, v := range params { 64 | sCode += ",\"" + v + "\"" 65 | } 66 | sCode += "})" 67 | } 68 | b, _ := json.Marshal(sCode) 69 | return string(b) 70 | } 71 | 72 | // AddMenuItem adds a new item to the Window's menu or submenu, 73 | // sName argument is a title of the item, 74 | // id - menu item identifier; if 0 - it is created automatically 75 | // fu - a function in the program, which must be called, when this menu item is selected, 76 | // sCode - the identifier (name) of this function. 77 | // If the fu value is nil, sCode contains the Harbour's code, which must be executed by 78 | // the GuiServer when this menu item is selected. 79 | // params - arguments for the fu function. 80 | func AddMenuItem(sName string, id int, fu func([]string) string, sCode string, params ...string) { 81 | 82 | if sMenu[len(sMenu)-1] != '[' { 83 | sMenu += "," 84 | } 85 | sMenu += "[\"" + sName + "\"," + getscode(fu, sCode, params...) + "," + strconv.Itoa(id) + "]" 86 | } 87 | 88 | // AddCheckMenuItem is the same as AddMenuItem, but it creates a menu item, which may be checked. 89 | // In fact, it is needed in a GTK version only, but for the sake of compatibility it is 90 | // recommended to use it in your code if this is a check menu item. 91 | func AddCheckMenuItem(sName string, id int, fu func([]string) string, sCode string, params ...string) { 92 | 93 | if sMenu[len(sMenu)-1] != '[' { 94 | sMenu += "," 95 | } 96 | sMenu += "[\"" + sName + "\"," + getscode(fu, sCode, params...) + "," + strconv.Itoa(id) + ",true]" 97 | } 98 | 99 | // AddMenuSeparator adds a separator to the Window's menu or submenu, 100 | func AddMenuSeparator() { 101 | if sMenu[len(sMenu)-1] != '[' { 102 | sMenu += "," 103 | } 104 | sMenu += "[\"-\"]" 105 | } 106 | 107 | // MenuItemEnable enables (bValue == true) or disables a menu item. 108 | // If this is a window menu (main or a dialog), you need to pass the appropriate window name 109 | // via sWndName parameter, if context - the menu name via sMenuName. 110 | // iItem is a menu item id. 111 | func MenuItemEnable(sWndName string, sMenuName string, iItem int, bValue bool) { 112 | 113 | sendout("[\"menu\",\"enable\",\"" + sWndName + "\",\"" + sMenuName + "\"," + 114 | strconv.Itoa(iItem) + "," + strconv.FormatBool(bValue) + "]") 115 | } 116 | 117 | // MenuItemCheck checks (bValue == true) or unchecks a menu item. 118 | // If this is a window menu (main or a dialog), you need to pass the appropriate window name 119 | // via sWndName parameter, if context - the menu name via sMenuName. 120 | // iItem is a menu item id. 121 | 122 | func MenuItemCheck(sWndName string, sMenuName string, iItem int, bValue bool) { 123 | 124 | sendout("[\"menu\",\"check\",\"" + sWndName + "\",\"" + sMenuName + "\"," + 125 | strconv.Itoa(iItem) + "," + strconv.FormatBool(bValue) + "]") 126 | } 127 | -------------------------------------------------------------------------------- /extwidg.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Alexander S.Kresin , http://www.kresin.ru 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package external 6 | 7 | import ( 8 | "encoding/json" 9 | "fmt" 10 | "strconv" 11 | "strings" 12 | ) 13 | 14 | // A set of constants of the anchor values 15 | const ( 16 | A_TOPLEFT = -1 // Anchors control to the top and left borders of the container and does not change the distance between the top and left borders. (Default) 17 | A_TOPABS = 1 // Anchors control to top border of container and does not change the distance between the top border. 18 | A_LEFTABS = 2 // Anchors control to left border of container and does not change the distance between the left border. 19 | A_BOTTOMABS = 4 // Anchors control to bottom border of container and does not change the distance between the bottom border. 20 | A_RIGHTABS = 8 // Anchors control to right border of container and does not change the distance between the right border. 21 | A_TOPREL = 16 // Anchors control to top border of container and maintains relative distance between the top border. 22 | A_LEFTREL = 32 // Anchors control to left border of container and maintains relative distance between the left border. 23 | A_BOTTOMREL = 64 // Anchors control to bottom border of container and maintains relative distance between the bottom border. 24 | A_RIGHTREL = 128 // Anchors control to right border of container and maintains relative distance between the right border. 25 | A_HORFIX = 256 // Anchors center of control relative to left and right borders but remains fixed in size. 26 | A_VERTFIX = 512 // Anchors center of control relative to top and bottom borders but remains fixed in size. 27 | ) 28 | 29 | // A set of constants of Winstyle values 30 | const ( 31 | DT_LEFT = 0 32 | DT_CENTER = 1 33 | DT_RIGHT = 2 34 | 35 | ES_PASSWORD = 32 36 | ES_MULTILINE = 4 37 | ES_READONLY = 2048 38 | 39 | WS_HSCROLL = 2097152 40 | WS_VSCROLL = 1048576 41 | 42 | WND_NOTITLE = -1 43 | WND_NOSYSMENU = -2 44 | WND_NOSIZEBOX = -4 45 | ) 46 | 47 | // A set of constants of the printer paper types 48 | const ( 49 | DMPAPER_A3 = 8 // A3 297 x 420 mm 50 | DMPAPER_A4 = 9 // A4 210 x 297 mm 51 | DMPAPER_A5 = 11 // A5 148 x 210 mm 52 | DMPAPER_A6 = 70 // A6 105 x 148 mm 53 | ) 54 | 55 | // A set of constants of the code editor highliter 56 | const ( 57 | HILI_KEYW = 1 58 | HILI_FUNC = 2 59 | HILI_QUOTE = 3 60 | HILI_COMM = 4 61 | ) 62 | 63 | // The CodeBlock type for Harbour scripts, which are set via SetParam() method and BrwSetColumnEx() 64 | type CodeBlock string 65 | 66 | // The Font structure prepares data to create a new font 67 | type Font struct { 68 | Family string 69 | Name string 70 | Height int 71 | Bold bool 72 | Italic bool 73 | Underline bool 74 | Strikeout bool 75 | Charset int16 76 | } 77 | 78 | // The Style structure prepares data to create a new style 79 | type Style struct { 80 | Name string 81 | Orient int16 82 | Colors []int32 83 | Corners []int32 84 | BorderW int8 85 | BorderClr int32 86 | Bitmap string 87 | } 88 | 89 | // The Highlight structure serves to create a highlight rules for a code editor 90 | type Highlight struct { 91 | Name string 92 | } 93 | 94 | // The Printer structure prepares data to initialize a printer 95 | type Printer struct { 96 | Name string 97 | SPrinter string 98 | BPreview bool 99 | IFormType int 100 | BLandscape bool 101 | } 102 | 103 | // The Widget structure prepares data to create a new widget or window 104 | type Widget struct { 105 | Parent *Widget 106 | Type string 107 | Name string 108 | X int 109 | Y int 110 | W int 111 | H int 112 | Title string 113 | Winstyle int32 114 | TColor int32 115 | BColor int32 116 | Tooltip string 117 | Anchor int32 118 | Font *Font 119 | AProps map[string]string 120 | aWidgets []*Widget 121 | } 122 | 123 | var mfu map[string]func([]string) string 124 | var pMainWindow *Widget 125 | var aDialogs []*Widget 126 | var aFonts []*Font 127 | var aStyles []*Style 128 | var iIdCount int32 129 | 130 | // PLastWindow is a pointer to a last used window structure (*Widget) 131 | var PLastWindow *Widget 132 | 133 | // PLastWidget is a pointer to a last used widget structure (*Widget) 134 | var PLastWidget *Widget 135 | 136 | // PLastPrinter is a pointer to a last used printer structure (*Printer) 137 | var PLastPrinter *Printer 138 | 139 | // Var mWidgs includes all possible widgets types with 140 | // its properties, which may be installed, using AProps member of a Widget structure. 141 | var mWidgs = map[string]map[string]string{ 142 | "main": {"Icon": "C"}, 143 | "dialog": {"Icon": "C", "NoExitOnEsc": "L", "NoCloseAble": "L"}, 144 | "label": {"Transpa": "L"}, 145 | "edit": {"Picture": "C"}, 146 | "button": nil, 147 | "check": {"Transpa": "L"}, 148 | "radio": {"Transpa": "L"}, 149 | "radiogr": nil, 150 | "group": nil, 151 | "combo": {"AItems": "AC"}, 152 | "bitmap": {"Transpa": "L", "TrColor": "N", "Image": "C"}, 153 | "line": {"Vertical": "L"}, 154 | "panel": {"HStyle": "C"}, 155 | "paneltop": {"HStyle": "C"}, 156 | "panelbot": {"HStyle": "C", "AParts": "AC"}, 157 | "panelhead": {"HStyle": "C", "Xt": "N", "Yt": "N", "BtnClose": "L", "BtnMax": "L", "BtnMin": "L"}, 158 | "ownbtn": {"Transpa": "L", "TrColor": "N", "Image": "C", "HStyles": "AC", "Xt": "N", "Yt": "N"}, 159 | "splitter": {"Vertical": "L", "From": "N", "TO": "N", "ALeft": "AC", "ARight": "AC", "HStyle": "C"}, 160 | "updown": {"From": "N", "TO": "N"}, 161 | "tree": {"AImages": "AC", "EditLabel": "L"}, 162 | "progress": {"Maxpos": "N"}, 163 | "tab": nil, 164 | "browse": {"Append": "L", "Autoedit": "L", "NoVScroll": "L", "NoBorder": "L"}, 165 | "cedit": {"NoVScroll": "L", "NoBorder": "L"}, 166 | "link": {"Link": "C", "ClrVisited": "N", "ClrLink": "N", "ClrOver": "N"}, 167 | "monthcal": {"NoToday": "L", "NoTodayCirc": "L", "WeekNumb": "L"}} 168 | 169 | func widgFullName(pWidg *Widget) string { 170 | sName := pWidg.Name 171 | 172 | for pWidg.Parent != nil { 173 | pWidg = pWidg.Parent 174 | sName = pWidg.Name + "." + sName 175 | } 176 | return sName 177 | } 178 | 179 | // Returns a pointer to a Font structure with a Name member equal to sName argument. 180 | func GetFont(sName string) *Font { 181 | if aFonts != nil { 182 | for _, o := range aFonts { 183 | if o.Name == sName { 184 | return o 185 | } 186 | } 187 | } 188 | return nil 189 | } 190 | 191 | // Returns a pointer to a Style structure with a Name member equal to sName argument. 192 | func GetStyle(sName string) *Style { 193 | if aStyles != nil { 194 | for _, o := range aStyles { 195 | if o.Name == sName { 196 | return o 197 | } 198 | } 199 | } 200 | return nil 201 | } 202 | 203 | // Wnd returns a pointer to a Widget structure (a window or a dialog) with a Name member equal to sName argument. 204 | func Wnd(sName string) *Widget { 205 | if sName == "main" { 206 | return pMainWindow 207 | } else if aDialogs != nil { 208 | for _, o := range aDialogs { 209 | if o.Name == sName { 210 | return o 211 | } 212 | } 213 | } 214 | return nil 215 | } 216 | 217 | // Widg returns a pointer to a Widget structure (a widget) with a Name member corresponding to sName argument. 218 | // The sName must be compound name, containing a names of all parent widgets and windows, defined by dots. 219 | func Widg(sName string) *Widget { 220 | npos := strings.Index(sName, ".") 221 | if npos == -1 { 222 | return Wnd(sName) 223 | } 224 | sWnd := sName[:npos] 225 | sName = sName[npos+1:] 226 | if oWnd := Wnd(sWnd); oWnd != nil { 227 | for npos = strings.Index(sName, "."); npos > -1; npos = strings.Index(sName, ".") { 228 | sWnd := sName[:npos] 229 | sName = sName[npos+1:] 230 | for _, o := range oWnd.aWidgets { 231 | if o.Name == sWnd { 232 | oWnd = o 233 | break 234 | } 235 | } 236 | if oWnd == nil { 237 | return nil 238 | } 239 | } 240 | for _, o := range oWnd.aWidgets { 241 | if o.Name == sName { 242 | return o 243 | } 244 | } 245 | } 246 | return nil 247 | } 248 | 249 | func setprops(pWidg *Widget, mwidg map[string]string) string { 250 | 251 | sPar := "" 252 | if pWidg.Winstyle != 0 { 253 | sPar += fmt.Sprintf(",\"Winstyle\": %d", pWidg.Winstyle) 254 | } 255 | if pWidg.TColor != 0 { 256 | sPar += fmt.Sprintf(",\"TColor\": %d", pWidg.TColor) 257 | } 258 | if pWidg.BColor != 0 { 259 | sPar += fmt.Sprintf(",\"BColor\": %d", pWidg.BColor) 260 | } 261 | if pWidg.Tooltip != "" { 262 | sPar += fmt.Sprintf(",\"Tooltip\": \"%s\"", pWidg.Tooltip) 263 | } 264 | if pWidg.Font != nil { 265 | sPar += fmt.Sprintf(",\"Font\": \"%s\"", pWidg.Font.Name) 266 | } 267 | if pWidg.Anchor != 0 { 268 | if pWidg.Anchor == A_TOPLEFT { 269 | pWidg.Anchor = 0 270 | } 271 | sPar += fmt.Sprintf(",\"Anchor\": %d", pWidg.Anchor) 272 | } 273 | if pWidg.AProps != nil { 274 | for name, val := range pWidg.AProps { 275 | cType, bOk := mwidg[name] 276 | if bOk { 277 | if cType == "C" { 278 | sPar += fmt.Sprintf(",\"%s\": \"%s\"", name, val) 279 | } else if cType == "L" { 280 | sPar += fmt.Sprintf(",\"%s\": \"%s\"", name, val) 281 | } else if cType == "N" { 282 | sPar += fmt.Sprintf(",\"%s\": %s", name, val) 283 | } else if cType == "AC" { 284 | sPar += fmt.Sprintf(",\"%s\": %s", name, val) 285 | } 286 | } else { 287 | WriteLog(fmt.Sprintf("Error! \"%s\" does not defined for \"%s\"\r\n", name, pWidg.Type)) 288 | } 289 | } 290 | } 291 | if sPar != "" { 292 | sPar = ",{" + sPar[1:] + "}" 293 | } 294 | return sPar 295 | } 296 | 297 | // ToString converts function arguments to a json string 298 | func ToString(xParam ...interface{}) string { 299 | 300 | for i, x := range xParam { 301 | switch v := x.(type) { 302 | case *Font: 303 | xParam[i] = v.Name 304 | case *Style: 305 | xParam[i] = v.Name 306 | case *Widget: 307 | xParam[i] = v.Name 308 | } 309 | } 310 | 311 | b, _ := json.Marshal(xParam) 312 | return string(b) 313 | } 314 | 315 | // OpenMainForm reads a main window description from a xml file, prepared by HwGUI's Designer, 316 | // initialises and activates this window with all its widgets 317 | func OpenMainForm(sForm string) bool { 318 | var bres bool 319 | b, err := json.Marshal(sForm) 320 | if err != nil { 321 | WriteLog(fmt.Sprintln(err)) 322 | return false 323 | } 324 | bres = sendout("[\"openformmain\"," + string(b) + "]") 325 | Wait() 326 | return bres 327 | } 328 | 329 | // OpenForm reads a dialog window description from a xml file, prepared by HwGUI's Designer, 330 | // initialises and activates this dialog with all its widgets 331 | func OpenForm(sForm string) bool { 332 | var bres bool 333 | b, err := json.Marshal(sForm) 334 | if err != nil { 335 | WriteLog(fmt.Sprintln(err)) 336 | return false 337 | } 338 | bres = sendout("[\"openform\"," + string(b) + "]") 339 | return bres 340 | } 341 | 342 | // OpenReport reads a report description from a xml file, prepared by HwGUI's Designer 343 | // and prints this report 344 | func OpenReport(sForm string) bool { 345 | var b bool 346 | b = sendout("[\"openreport\",\"" + sForm + "\"]") 347 | return b 348 | } 349 | 350 | // CreateFont creates a font with parameters, defined in a structure, pointed by pFont argument. 351 | func CreateFont(pFont *Font) *Font { 352 | 353 | pFont.new() 354 | sParams := fmt.Sprintf("[\"crfont\",\"%s\",\"%s\",%d,%t,%t,%t,%t,%d]", pFont.Name, pFont.Family, pFont.Height, 355 | pFont.Bold, pFont.Italic, pFont.Underline, pFont.Strikeout, pFont.Charset) 356 | sendout(sParams) 357 | return pFont 358 | } 359 | 360 | // CreateStyle creates a style with parameters, defined in a structure, pointed by pStyle argument. 361 | func CreateStyle(pStyle *Style) *Style { 362 | 363 | if pStyle.Name == "" { 364 | pStyle.Name = fmt.Sprintf("s%d", iIdCount) 365 | iIdCount++ 366 | } 367 | if aStyles == nil { 368 | aStyles = make([]*Style, 0, 16) 369 | } 370 | aStyles = append(aStyles, pStyle) 371 | b1, _ := json.Marshal(pStyle.Colors) 372 | b2, _ := json.Marshal(pStyle.Corners) 373 | sParams := fmt.Sprintf("[\"crstyle\",\"%s\",%s,%d,%s,%d,%d,\"%s\"]", pStyle.Name, 374 | string(b1), pStyle.Orient, string(b2), 375 | pStyle.BorderW, pStyle.BorderClr, pStyle.Bitmap) 376 | sendout(sParams) 377 | return pStyle 378 | } 379 | 380 | // CreateHighliter creates a highlight rules for a code editor ("cedit" widget) 381 | func CreateHighliter(sName string, sCommands string, sFuncs string, 382 | sSingleLineComm string, sMultiLineComm string, bCase bool) *Highlight { 383 | 384 | sParams := fmt.Sprintf("[\"highl\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",%t]", sName, 385 | sCommands, sFuncs, sSingleLineComm, sMultiLineComm, bCase) 386 | sendout(sParams) 387 | return &(Highlight{Name: sName}) 388 | } 389 | 390 | // SetHighliter sets or unsets (if p == nil) a given Highliter to a "cedit" widget. 391 | func SetHighliter(pEdit *Widget, p *Highlight) { 392 | var sHiliName string 393 | if p == nil { 394 | sHiliName = "" 395 | } else { 396 | sHiliName = p.Name 397 | } 398 | sParams := fmt.Sprintf("[\"set\",\"%s\",\"hili\",\"%s\"]", 399 | widgFullName(pEdit), sHiliName) 400 | sendout(sParams) 401 | } 402 | 403 | // SetHili defines highlighting options for a code editor ("cedit" widget): a font, text color and background color 404 | func SetHiliOpt(pEdit *Widget, iGroup int, pFont *Font, tColor int32, bColor int32) { 405 | var sFontName string 406 | if pFont == nil { 407 | sFontName = "" 408 | } else { 409 | sFontName = pFont.Name 410 | } 411 | sParams := fmt.Sprintf("[\"set\",\"%s\",\"hiliopt\",[%d,\"%s\",%d,%d]]", 412 | widgFullName(pEdit), iGroup, sFontName, tColor, bColor) 413 | sendout(sParams) 414 | } 415 | 416 | // InitPrinter initializes a printer, the name of a printer is passed in SPrinter member of 417 | // a pPrinter structure. If it is an empty string, the default printer will be used, if it is 418 | // defined as "...", printer setup dialog will be opened. 419 | func InitPrinter(pPrinter *Printer, sFunc string, fu func([]string) string, sMark string) *Printer { 420 | 421 | if pPrinter.Name == "" { 422 | pPrinter.Name = fmt.Sprintf("p%d", iIdCount) 423 | iIdCount++ 424 | } 425 | if fu != nil && sFunc != "" { 426 | RegFunc(sFunc, fu) 427 | } else { 428 | sFunc = "" 429 | sMark = "" 430 | } 431 | sParams := fmt.Sprintf("[\"prninit\",\"%s\",[\"%s\",%t,%d,%t],\"%s\",\"%s\"]", pPrinter.Name, 432 | pPrinter.SPrinter, pPrinter.BPreview, pPrinter.IFormType, pPrinter.BLandscape, sFunc, sMark) 433 | sendout(sParams) 434 | PLastPrinter = pPrinter 435 | return pPrinter 436 | } 437 | 438 | // AddFont method adds a font, described in Font structure, to the printer. 439 | func (p *Printer) AddFont(pFont *Font) *Font { 440 | pFont.new() 441 | sParams := fmt.Sprintf("[\"print\",\"fontadd\",\"%s\",[\"%s\",\"%s\",%d,%t,%t,%t,%d]]", p.Name, 442 | pFont.Name, pFont.Family, pFont.Height, 443 | pFont.Bold, pFont.Italic, pFont.Underline, pFont.Charset) 444 | sendout(sParams) 445 | return pFont 446 | } 447 | 448 | // SetFont method sets a font, previously added with AddFont, as current while printing 449 | func (p *Printer) SetFont(pFont *Font) { 450 | sParams := fmt.Sprintf("[\"print\",\"fontset\",\"%s\",[\"%s\"]]", p.Name, pFont.Name) 451 | sendout(sParams) 452 | } 453 | 454 | // Say method prints s text string sText in a rectangle with iTop, iLeft, iRight, iBottom 455 | // coordinates, iOpt defines an alignment. 456 | func (p *Printer) Say(iTop, iLeft, iRight, iBottom int32, sText string, iOpt int32) { 457 | 458 | sParams := fmt.Sprintf("[\"print\",\"text\",\"%s\",[\"%s\",%d,%d,%d,%d,%d]]", 459 | p.Name, sText, iTop, iLeft, iRight, iBottom, iOpt) 460 | sendout(sParams) 461 | } 462 | 463 | // Line methods prints a line from iTop, iLeft to iRight, iBottom 464 | func (p *Printer) Line(iTop, iLeft, iRight, iBottom int32) { 465 | 466 | sParams := fmt.Sprintf("[\"print\",\"line\",\"%s\",[%d,%d,%d,%d]]", p.Name, iTop, iLeft, iRight, iBottom) 467 | sendout(sParams) 468 | } 469 | 470 | // Box method prints a rectangle with iTop, iLeft, iRight, iBottom coordinates 471 | func (p *Printer) Box(iTop, iLeft, iRight, iBottom int32) { 472 | 473 | sParams := fmt.Sprintf("[\"print\",\"box\",\"%s\",[%d,%d,%d,%d]]", p.Name, iTop, iLeft, iRight, iBottom) 474 | sendout(sParams) 475 | } 476 | 477 | // StartPage method begins a new page printed 478 | func (p *Printer) StartPage() { 479 | 480 | sParams := fmt.Sprintf("[\"print\",\"startpage\",\"%s\",[]]", p.Name) 481 | sendout(sParams) 482 | } 483 | 484 | // StartPage method ends a page printed 485 | func (p *Printer) EndPage() { 486 | 487 | sParams := fmt.Sprintf("[\"print\",\"endpage\",\"%s\",[]]", p.Name) 488 | sendout(sParams) 489 | } 490 | 491 | // End method closes a printer 492 | func (p *Printer) End() { 493 | 494 | sParams := fmt.Sprintf("[\"print\",\"end\",\"%s\",[]]", p.Name) 495 | sendout(sParams) 496 | } 497 | 498 | // Initialises a main window with parameters, defined in a structure, pointed by pWnd argument. 499 | // To show this window on a screen it is necessary to use Activate() method. 500 | func InitMainWindow(pWnd *Widget) bool { 501 | pMainWindow = pWnd 502 | PLastWindow = pWnd 503 | pWnd.Type = "main" 504 | pWnd.Name = "main" 505 | sPar2 := setprops(pWnd, mWidgs["main"]) 506 | sParams := fmt.Sprintf("[\"crmainwnd\",[%d,%d,%d,%d,\"%s\"]%s]", pWnd.X, pWnd.Y, pWnd.W, 507 | pWnd.H, pWnd.Title, sPar2) 508 | return sendout(sParams) 509 | } 510 | 511 | // Initialises a dialog window with parameters, defined in a structure, pointed by pWnd argument. 512 | // To show this window on a screen it is necessary to use Activate() method. 513 | func InitDialog(pWnd *Widget) bool { 514 | PLastWindow = pWnd 515 | pWnd.Type = "dialog" 516 | if pWnd.Name == "" { 517 | pWnd.Name = fmt.Sprintf("w%d", iIdCount) 518 | iIdCount++ 519 | } 520 | if aDialogs == nil { 521 | aDialogs = make([]*Widget, 0, 8) 522 | } 523 | aDialogs = append(aDialogs, pWnd) 524 | 525 | sPar2 := setprops(pWnd, mWidgs["dialog"]) 526 | sParams := fmt.Sprintf("[\"crdialog\",\"%s\",[%d,%d,%d,%d,\"%s\"]%s]", pWnd.Name, pWnd.X, pWnd.Y, pWnd.W, 527 | pWnd.H, pWnd.Title, sPar2) 528 | return sendout(sParams) 529 | } 530 | 531 | // EvalProc sends a code fragment, written on Harbour to a GuiServer to execute 532 | // and does not return a result. 533 | func EvalProc(s string) { 534 | 535 | b, _ := json.Marshal(s) 536 | sendout("[\"evalcode\"," + string(b) + "]") 537 | } 538 | 539 | // EvalFunc sends a code fragment, written on Harbour to a GuiServer to execute 540 | // and returns a result. 541 | func EvalFunc(s string) []byte { 542 | 543 | b, _ := json.Marshal(s) 544 | b = sendoutAndReturn("[\"evalcode\"," + string(b) + ",\"t\"]") 545 | if b[0] == byte('+') && b[1] == byte('"') { 546 | b = b[2 : len(b)-1] 547 | } 548 | return b 549 | } 550 | 551 | // GetValues returns list of values from widgets of a pWnd window (main or a dialog), 552 | // listed by names in aNames slice. 553 | func GetValues(pWnd *Widget, aNames []string) []string { 554 | sParams := "[\"getvalues\",\"" + pWnd.Name + "\",[" 555 | for i, v := range aNames { 556 | if i > 0 { 557 | sParams += "," 558 | } 559 | sParams += "\"" + v + "\"" 560 | } 561 | sParams += "]]" 562 | b := sendoutAndReturn(sParams) 563 | arr := make([]string, len(aNames)) 564 | err := json.Unmarshal(b[1:], &arr) 565 | if err != nil { 566 | return nil 567 | } else { 568 | return arr 569 | } 570 | } 571 | 572 | // GetVersion returns the version string in different verbosity level, depending of i value 573 | // i == 0 - GuiServer version only ("1.3", for example); 574 | // i == 1 - GuiServer version with "GuiServer" word; 575 | // i == 2 - GuiServer, Harbour and HwGUI versions. 576 | func GetVersion(i int) string { 577 | 578 | var sRes string 579 | b := sendoutAndReturn("[\"getver\"," + strconv.Itoa(i) + "]") 580 | if b[0] == byte('+') { 581 | b = b[1:len(b)] 582 | } 583 | err := json.Unmarshal(b, &sRes) 584 | if err != nil { 585 | return "" 586 | } 587 | return sRes 588 | } 589 | 590 | // MsgInfo creates a standard nessagebox 591 | // sTitle - box title, sMessage - text in a box 592 | // fu, sFunc - a definition of a callback procedure; fu - function, sFunc - identifier; 593 | // sName - a parameter, passed to a callback procedure. 594 | func MsgInfo(sMessage string, sTitle string, fu func([]string) string, sFunc string, sName string) { 595 | 596 | if fu != nil && sFunc != "" { 597 | RegFunc(sFunc, fu) 598 | } else { 599 | sFunc = "" 600 | sName = "" 601 | } 602 | b, _ := json.Marshal(sMessage) 603 | sParams := fmt.Sprintf("[\"common\",\"minfo\",\"%s\",\"%s\",%s,\"%s\"]", sFunc, sName, string(b), sTitle) 604 | sendout(sParams) 605 | } 606 | 607 | // MsgStop creates a standard nessagebox 608 | // sTitle - box title, sMessage - text in a box 609 | // fu, sFunc - a definition of a callback procedure; fu - function, sFunc - identifier; 610 | // sName - a parameter, passed to a callback procedure. 611 | func MsgStop(sMessage string, sTitle string, fu func([]string) string, sFunc string, sName string) { 612 | 613 | if fu != nil && sFunc != "" { 614 | RegFunc(sFunc, fu) 615 | } else { 616 | sFunc = "" 617 | sName = "" 618 | } 619 | b, _ := json.Marshal(sMessage) 620 | sParams := fmt.Sprintf("[\"common\",\"mstop\",\"%s\",\"%s\",%s,\"%s\"]", sFunc, sName, string(b), sTitle) 621 | sendout(sParams) 622 | } 623 | 624 | // MsgYesNo creates a standard nessagebox 625 | // sTitle - box title, sMessage - text in a box 626 | // fu, sFunc - a definition of a callback procedure; fu - function, sFunc - identifier; 627 | // sName - a parameter, passed to a callback procedure. 628 | func MsgYesNo(sMessage string, sTitle string, fu func([]string) string, sFunc string, sName string) { 629 | 630 | if fu != nil && sFunc != "" { 631 | RegFunc(sFunc, fu) 632 | } else { 633 | sFunc = "" 634 | sName = "" 635 | } 636 | b, _ := json.Marshal(sMessage) 637 | sParams := fmt.Sprintf("[\"common\",\"myesno\",\"%s\",\"%s\",%s,\"%s\"]", sFunc, sName, string(b), sTitle) 638 | sendout(sParams) 639 | } 640 | 641 | // MsgGet creates a messagebox, which allows to input a string 642 | // sTitle - box title, sMessage - text in a box, iStyle - a Winstyle for an "edit" widget (ES_PASSWORD, for example). 643 | // fu, sFunc - a definition of a callback procedure; fu - function, sFunc - identifier; 644 | // sName - a parameter, passed to a callback procedure. 645 | func MsgGet(sMessage string, sTitle string, iStyle int32, fu func([]string) string, sFunc string, sName string) { 646 | 647 | if fu != nil && sFunc != "" { 648 | RegFunc(sFunc, fu) 649 | } else { 650 | sFunc = "" 651 | sName = "" 652 | } 653 | b, _ := json.Marshal(sMessage) 654 | sParams := fmt.Sprintf("[\"common\",\"mget\",\"%s\",\"%s\",%s,\"%s\",%d]", sFunc, sName, string(b), sTitle, iStyle) 655 | sendout(sParams) 656 | } 657 | 658 | // Choice creates a dialog with a "browse" inside, which allows to select one of items in 659 | // a passed slice arr. 660 | // fu, sFunc - a definition of a callback procedure; fu - function, sFunc - identifier; 661 | // sName - a parameter, passed to a callback procedure. 662 | func Choice(arr []string, sTitle string, fu func([]string) string, sFunc string, sName string) { 663 | 664 | if fu != nil && sFunc != "" { 665 | RegFunc(sFunc, fu) 666 | } else { 667 | sFunc = "" 668 | sName = "" 669 | } 670 | b, _ := json.Marshal(arr) 671 | sParams := fmt.Sprintf("[\"common\",\"mchoi\",\"%s\",\"%s\",%s,\"%s\"]", sFunc, sName, string(b), sTitle) 672 | sendout(sParams) 673 | } 674 | 675 | // SelectFile creates a standard dialog to select file 676 | // sPath - initial path; 677 | // fu, sFunc - a definition of a callback procedure; fu - function, sFunc - identifier; 678 | // sName - a parameter, passed to a callback procedure. 679 | func SelectFile(sPath string, fu func([]string) string, sFunc string, sName string) { 680 | 681 | if fu != nil && sFunc != "" { 682 | RegFunc(sFunc, fu) 683 | } else { 684 | sFunc = "" 685 | sName = "" 686 | } 687 | sParams := fmt.Sprintf("[\"common\",\"cfile\",\"%s\",\"%s\",\"%s\"]", sFunc, sName, sPath) 688 | sendout(sParams) 689 | } 690 | 691 | // SelectFolder creates a standard dialog to select folder 692 | // fu, sFunc - a definition of a callback procedure; fu - function, sFunc - identifier; 693 | // sName - a parameter, passed to a callback procedure. 694 | func SelectFolder(fu func([]string) string, sFunc string, sName string) { 695 | 696 | if fu != nil && sFunc != "" { 697 | RegFunc(sFunc, fu) 698 | } else { 699 | sFunc = "" 700 | sName = "" 701 | } 702 | sParams := fmt.Sprintf("[\"common\",\"cfold\",\"%s\",\"%s\"]", sFunc, sName) 703 | sendout(sParams) 704 | } 705 | 706 | // SelectColor creates a standard dialog to select color 707 | // iColor - base color; 708 | // fu, sFunc - a definition of a callback procedure; fu - function, sFunc - identifier; 709 | // sName - a parameter, passed to a callback procedure. 710 | func SelectColor(iColor int32, fu func([]string) string, sFunc string, sName string) { 711 | 712 | if fu != nil && sFunc != "" { 713 | RegFunc(sFunc, fu) 714 | } else { 715 | sFunc = "" 716 | sName = "" 717 | } 718 | sParams := fmt.Sprintf("[\"common\",\"ccolor\",\"%s\",\"%s\",%d]", sFunc, sName, iColor) 719 | sendout(sParams) 720 | } 721 | 722 | // SelectFont creates a standard dialog to select font 723 | // fu, sFunc - a definition of a callback procedure; fu - function, sFunc - identifier; 724 | // sName - a parameter, passed to a callback procedure. 725 | func SelectFont(fu func([]string) string, sFunc string, sName string) { 726 | 727 | if fu != nil && sFunc != "" { 728 | RegFunc(sFunc, fu) 729 | } else { 730 | sFunc = "" 731 | } 732 | pFont := &(Font{Name: sName}) 733 | pFont.new() 734 | sParams := fmt.Sprintf("[\"common\",\"cfont\",\"%s\",\"%s\"]", sFunc, pFont.Name) 735 | sendout(sParams) 736 | } 737 | 738 | // InsertNode inserts a node to a tree widget pTree. 739 | // sNodeName - a name of a parent node, or empty, if it is root node; 740 | // sNodeNew - a name of an inserted node; 741 | // sTitle - a caption of an inserted node; 742 | // sNodeNext - a name of a node, you want to insert the new before; 743 | // aImages - path to images for the node ( unselected, selected ); 744 | // fu, sCode - a definition of a callback procedure; fu - function, sCode - identifier or Harbour script. 745 | func InsertNode(pTree *Widget, sNodeName string, sNodeNew string, sTitle string, 746 | sNodeNext string, aImages []string, fu func([]string) string, sCode string) { 747 | 748 | sParams := fmt.Sprintf("[\"set\",\"%s\",\"node\",[\"%s\",\"%s\",\"%s\",\"%s\",", 749 | widgFullName(pTree), sNodeName, sNodeNew, sTitle, sNodeNext) 750 | if sCode != "" { 751 | sName := widgFullName(pTree) 752 | if fu != nil { 753 | RegFunc(sCode, fu) 754 | sCode = "pgo(\"" + sCode + "\",{\"" + sName + "\",\"" + sNodeNew + "\"})" 755 | } 756 | b, _ := json.Marshal(sCode) 757 | sCode = string(b) 758 | } else { 759 | sCode = "null" 760 | } 761 | 762 | if aImages == nil { 763 | sParams += "null" 764 | } else { 765 | b, _ := json.Marshal(aImages) 766 | sParams += string(b) 767 | } 768 | sParams += "," + sCode + "]]" 769 | 770 | sendout(sParams) 771 | } 772 | 773 | func SelectNode(pTree *Widget, sNodeName string) { 774 | 775 | sParams := fmt.Sprintf("[\"set\",\"%s\",\"nodesele\",\"%s\"]", 776 | widgFullName(pTree), sNodeName) 777 | sendout(sParams) 778 | } 779 | 780 | // PBarStep does a next step for a pPBar progress bar widget 781 | func PBarStep(pPBar *Widget) { 782 | 783 | var sName = widgFullName(pPBar) 784 | sParams := fmt.Sprintf("[\"set\",\"%s\",\"step\",1]", sName) 785 | sendout(sParams) 786 | } 787 | 788 | // PBarSet sets a progress bar position 789 | func PBarSet(pPBar *Widget, iPos int) { 790 | 791 | var sName = widgFullName(pPBar) 792 | sParams := fmt.Sprintf("[\"set\",\"%s\",\"setval\",%d]", sName, iPos) 793 | sendout(sParams) 794 | } 795 | 796 | // InitTray forces the main window to be placed in a tray, 797 | // sIcon - a path to icon file, 798 | // sMenuName - a name of a context menu, 799 | // sTooltip - a tooltip for an icon in tray. 800 | func InitTray(sIcon string, sMenuName string, sTooltip string) { 801 | 802 | sParams := fmt.Sprintf("[\"tray\",\"init\",\"%s\",\"%s\",\"%s\"]", sIcon, sMenuName, sTooltip) 803 | sendout(sParams) 804 | } 805 | 806 | // ModifyTrayIcon changes a tray icon of a main window, 807 | // sIcon - a path to icon file. 808 | func ModifyTrayIcon(sIcon string) { 809 | 810 | sParams := fmt.Sprintf("[\"tray\",\"icon\",\"%s\"]", sIcon) 811 | sendout(sParams) 812 | } 813 | 814 | // RadioEnd completes a group of radio buttons, started with a "radiogr" widget 815 | func RadioEnd(p *Widget, iSel int) { 816 | 817 | var sName = widgFullName(p) 818 | sParams := fmt.Sprintf("[\"set\",\"%s\",\"radioend\",%d]", sName, iSel) 819 | sendout(sParams) 820 | } 821 | 822 | // TabPage initialises a new page of a tab widget. 823 | func TabPage(pTab *Widget, sCaption string) { 824 | 825 | var sName = widgFullName(pTab) 826 | sParams := fmt.Sprintf("[\"set\",\"%s\",\"pagestart\",\"%s\"]", sName, sCaption) 827 | sendout(sParams) 828 | } 829 | 830 | // TabPageEnd completes a description of a page of a tab widget. 831 | func TabPageEnd(pTab *Widget) { 832 | 833 | var sName = widgFullName(pTab) 834 | sParams := fmt.Sprintf("[\"set\",\"%s\",\"pageend\",1]", sName) 835 | sendout(sParams) 836 | } 837 | 838 | // BrwSetArray sets a two-dimensional slice to be represented in a browse widget p. 839 | func BrwSetArray(p *Widget, arr *[][]string) { 840 | 841 | var sName = widgFullName(p) 842 | b, _ := json.Marshal(*arr) 843 | sParams := fmt.Sprintf("[\"set\",\"%s\",\"brwarr\",%s]", sName, string(b)) 844 | sendout(sParams) 845 | } 846 | 847 | // BrwGetArray returns a two-dimensional slice from a browse widget p. 848 | func BrwGetArray(p *Widget) [][]string { 849 | 850 | var sName = widgFullName(p) 851 | var arr [][]string 852 | 853 | sParams := fmt.Sprintf("[\"get\",\"%s\",\"brwarr\"]", sName) 854 | b := sendoutAndReturn(sParams) 855 | 856 | err := json.Unmarshal(b[1:], &arr) 857 | if err != nil { 858 | return nil 859 | } else { 860 | return arr 861 | } 862 | } 863 | 864 | // BrwSetColumn defines options for a column with number ic of a browse widget p. 865 | // The options are: 866 | // sHead string - a column title; 867 | // iAlignHead int - the alignment of a column title ( 0 - left, 1 - center, 2 - right ); 868 | // iAlignData int - the alignment of a column data ( 0 - left, 1 - center, 2 - right ); 869 | // bEditable bool - is the data in a column editable. 870 | // iLength - column width in characters; 871 | func BrwSetColumn(p *Widget, ic int, sHead string, iAlignHead int, iAlignData int, 872 | bEditable bool, iLength int) { 873 | var sName = widgFullName(p) 874 | sParams := fmt.Sprintf("[\"set\",\"%s\",\"brwcol\",[%d,\"%s\",%d,%d,%t,%d]]", 875 | sName, ic, sHead, iAlignHead, iAlignData, bEditable, iLength) 876 | sendout(sParams) 877 | } 878 | 879 | // BrwSetColumnEx sets options for a column with number ic of a browse widget p - 880 | // those, which can not be set via BrwSetColumn. 881 | // sParam - option name, xParam - option value 882 | func BrwSetColumnEx(p *Widget, ic int, sParam string, xParam interface{}) { 883 | var sName = widgFullName(p) 884 | var sParValue string 885 | var sObj = "d" 886 | 887 | switch v := xParam.(type) { 888 | case *Font: 889 | sParValue = "\"" + v.Name + "\"" 890 | sObj = "o" 891 | case *Style: 892 | sParValue = "\"" + v.Name + "\"" 893 | sObj = "o" 894 | case CodeBlock: 895 | b, _ := json.Marshal(xParam) 896 | sParValue = string(b) 897 | sObj = "b" 898 | default: 899 | b, _ := json.Marshal(xParam) 900 | sParValue = string(b) 901 | } 902 | 903 | sParams := fmt.Sprintf("[\"set\",\"%s\",\"brwcolx\",[%d,\"%s\",%s,\"%s\"]]", 904 | sName, ic, sParam, sParValue, sObj) 905 | sendout(sParams) 906 | } 907 | 908 | // BrwDelColumn deletes a column with number ic of a browse widget p. 909 | func BrwDelColumn(p *Widget, ic int) { 910 | var sName = widgFullName(p) 911 | sParams := fmt.Sprintf("[\"set\",\"%s\",\"brwcoldel\",%d]", sName, ic) 912 | sendout(sParams) 913 | } 914 | 915 | // SetVar sets a variable value 916 | func SetVar(sVarName string, sValue string) { 917 | 918 | sParams := fmt.Sprintf("[\"setvar\",\"%s\",\"%s\"]", sVarName, sValue) 919 | sendout(sParams) 920 | } 921 | 922 | // GetVar gets a variable value 923 | func GetVar(sVarName string) string { 924 | 925 | var sRes string 926 | sParams := fmt.Sprintf("[\"getvar\",\"%s\"]", sVarName) 927 | b := sendoutAndReturn(sParams) 928 | if b[0] == byte('+') { 929 | b = b[1:len(b)] 930 | } 931 | err := json.Unmarshal(b, &sRes) 932 | if err != nil { 933 | return "" 934 | } 935 | 936 | return sRes 937 | } 938 | 939 | // SetImagePath sets a directory where GuiServer should look for image files. 940 | func SetImagePath(sValue string) { 941 | 942 | sParams := fmt.Sprintf("[\"setparam\",\"bmppath\",\"%s\"]", sValue) 943 | sendout(sParams) 944 | } 945 | 946 | // SetPath sets a directory where GuiServer should write files 947 | // and look for files to read. 948 | func SetPath(sValue string) { 949 | 950 | sParams := fmt.Sprintf("[\"setparam\",\"path\",\"%s\"]", sValue) 951 | sendout(sParams) 952 | } 953 | 954 | // SetDateFormat sets a date display format, 955 | // for example, "DD.MM.YYYY" 956 | func SetDateFormat(sValue string) { 957 | 958 | sParams := fmt.Sprintf("[\"setparam\",\"datef\",\"%s\"]", sValue) 959 | sendout(sParams) 960 | } 961 | 962 | func (p *Font) new() *Font { 963 | if p.Name == "" { 964 | p.Name = fmt.Sprintf("f%d", iIdCount) 965 | iIdCount++ 966 | } 967 | if aFonts == nil { 968 | aFonts = make([]*Font, 0, 16) 969 | } 970 | aFonts = append(aFonts, p) 971 | return p 972 | } 973 | 974 | func (p *Font) FillFont(arr []string) { 975 | p.Family = arr[1] 976 | i, _ := strconv.Atoi(arr[2]) 977 | p.Height = int(i) 978 | p.Bold = (arr[3] == "t") 979 | p.Italic = (arr[4] == "t") 980 | p.Underline = (arr[5] == "t") 981 | p.Strikeout = (arr[6] == "t") 982 | i, _ = strconv.Atoi(arr[7]) 983 | p.Charset = int16(i) 984 | } 985 | 986 | // Method Activate shows on the screen a main window or a dialog 987 | func (o *Widget) Activate() bool { 988 | var sParams string 989 | if o.Type == "main" { 990 | sParams = fmt.Sprintf("[\"actmainwnd\",[\"f\"]]") 991 | } else if o.Type == "dialog" { 992 | sParams = fmt.Sprintf("[\"actdialog\",\"%s\",\"f\",[\"f\"]]", o.Name) 993 | } else { 994 | return false 995 | } 996 | b := sendout("" + sParams) 997 | if o.Type == "main" { 998 | Wait() 999 | } 1000 | return b 1001 | } 1002 | 1003 | // Method Close closes a main window or a dialog 1004 | func (o *Widget) Close() bool { 1005 | if o.Type == "main" || o.Type == "dialog" { 1006 | sParams := fmt.Sprintf("[\"close\",\"%s\"]", o.Name) 1007 | b := sendout("" + sParams) 1008 | if o.Type == "dialog" { 1009 | o.delete() 1010 | } 1011 | return b 1012 | } 1013 | return false 1014 | } 1015 | 1016 | func (o *Widget) delete() bool { 1017 | if o.Type == "dialog" { 1018 | for i, od := range aDialogs { 1019 | if o.Name == od.Name { 1020 | aDialogs = append(aDialogs[:i], aDialogs[i+1:]...) 1021 | return true 1022 | } 1023 | } 1024 | } else if o.Type != "main" { 1025 | } 1026 | return false 1027 | } 1028 | 1029 | // Method AddWidget adds new child widget 1030 | // o - parent window or widget 1031 | // pWidg - a Widget structure with definition of a new widget 1032 | func (o *Widget) AddWidget(pWidg *Widget) *Widget { 1033 | pWidg.Parent = o 1034 | mwidg, bOk := mWidgs[pWidg.Type] 1035 | if !bOk { 1036 | WriteLog(fmt.Sprintf("Error! \"%s\" does not defined\r\n", pWidg.Type)) 1037 | return nil 1038 | } 1039 | if pWidg.Name == "" { 1040 | pWidg.Name = fmt.Sprintf("w%d", iIdCount) 1041 | iIdCount++ 1042 | } 1043 | 1044 | sPar2 := setprops(pWidg, mwidg) 1045 | sParams := fmt.Sprintf("[\"addwidg\",\"%s\",\"%s\",[%d,%d,%d,%d,\"%s\"]%s]", 1046 | pWidg.Type, widgFullName(pWidg), pWidg.X, pWidg.Y, pWidg.W, 1047 | pWidg.H, pWidg.Title, sPar2) 1048 | sendout(sParams) 1049 | PLastWidget = pWidg 1050 | if o.aWidgets == nil { 1051 | o.aWidgets = make([]*Widget, 0, 16) 1052 | } 1053 | o.aWidgets = append(o.aWidgets, pWidg) 1054 | return pWidg 1055 | } 1056 | 1057 | // Method SetText sets a text aText to a widget, pointed by o. 1058 | func (o *Widget) SetText(sText string) { 1059 | 1060 | var sName = widgFullName(o) 1061 | o.Title = sText 1062 | b, _ := json.Marshal(sText) 1063 | sParams := fmt.Sprintf("[\"set\",\"%s\",\"text\",%s]", sName, string(b)) 1064 | sendout(sParams) 1065 | } 1066 | 1067 | // Method SetImage sets an image widget, pointed by o, 1068 | // sImage - a path to an image 1069 | func (o *Widget) SetImage(sImage string) { 1070 | 1071 | var sName = widgFullName(o) 1072 | 1073 | mwidg, bOk := mWidgs[o.Type] 1074 | if !bOk { 1075 | return 1076 | } 1077 | _, bOk = mwidg["Image"] 1078 | if !bOk { 1079 | return 1080 | } 1081 | 1082 | if o.AProps == nil { 1083 | o.AProps = make(map[string]string) 1084 | } 1085 | o.AProps["Image"] = sImage 1086 | sParams := fmt.Sprintf("[\"set\",\"%s\",\"image\",\"%s\"]", sName, sImage) 1087 | sendout(sParams) 1088 | } 1089 | 1090 | // Method SetParam sets a property to a widget, pointed by o, 1091 | // sParam - a name of a property, 1092 | // xParam - a value of a property. 1093 | func (o *Widget) SetParam(sParam string, xParam interface{}) { 1094 | 1095 | var sName = widgFullName(o) 1096 | var sParValue string 1097 | var sObj = "d" 1098 | 1099 | switch v := xParam.(type) { 1100 | case *Font: 1101 | sParValue = "\"" + v.Name + "\"" 1102 | sObj = "o" 1103 | case *Style: 1104 | sParValue = "\"" + v.Name + "\"" 1105 | sObj = "o" 1106 | case *Widget: 1107 | sParValue = "\"" + v.Name + "\"" 1108 | sObj = "o" 1109 | case *Highlight: 1110 | sParValue = "\"" + v.Name + "\"" 1111 | sObj = "o" 1112 | case CodeBlock: 1113 | b, _ := json.Marshal(xParam) 1114 | sParValue = string(b) 1115 | sObj = "b" 1116 | default: 1117 | b, _ := json.Marshal(xParam) 1118 | sParValue = string(b) 1119 | } 1120 | sParams := fmt.Sprintf("[\"set\",\"%s\",\"xparam\",[\"%s\",%s,\"%s\"]]", sName, sParam, sParValue, sObj) 1121 | sendout(sParams) 1122 | } 1123 | 1124 | // Method GetText gets the text from a widget, pointed by o. 1125 | func (o *Widget) GetText() string { 1126 | var sRes string 1127 | var sName = widgFullName(o) 1128 | 1129 | sParams := fmt.Sprintf("[\"get\",\"%s\",\"text\"]", sName) 1130 | b := sendoutAndReturn(sParams) 1131 | if b[0] == byte('+') { 1132 | b = b[1:len(b)] 1133 | } 1134 | err := json.Unmarshal(b, &sRes) 1135 | if err != nil { 1136 | return "" 1137 | } 1138 | 1139 | return sRes 1140 | } 1141 | 1142 | // Method SetColor sets a text color tColor and background color bColor to a widget, pointed by o. 1143 | func (o *Widget) SetColor(tColor int32, bColor int32) { 1144 | 1145 | var sName = widgFullName(o) 1146 | 1147 | sParams := fmt.Sprintf("[\"set\",\"%s\",\"color\",[%d,%d]]", sName, tColor, bColor) 1148 | sendout(sParams) 1149 | } 1150 | 1151 | // Method SetFont sets a font pFont to a widget, pointed by o. 1152 | func (o *Widget) SetFont(pFont *Font) { 1153 | 1154 | var sName = widgFullName(o) 1155 | o.Font = pFont 1156 | sParams := fmt.Sprintf("[\"set\",\"%s\",\"font\",\"%s\"]", sName, pFont.Name) 1157 | sendout(sParams) 1158 | } 1159 | 1160 | func (o *Widget) SetCallBackProc(sbName string, fu func([]string) string, sCode string, params ...string) { 1161 | 1162 | var sName = widgFullName(o) 1163 | var sc1, sc2 string 1164 | 1165 | if fu != nil { 1166 | RegFunc(sCode, fu) 1167 | if sbName == "onposchanged" { 1168 | sc1 = "o,n" 1169 | sc2 = ",n" 1170 | } else if sbName == "onrclick" || sbName == "onenter" { 1171 | sc1 = "o,nc,nr" 1172 | sc2 = ",nc,nr" 1173 | } else { 1174 | sc1 = "" 1175 | sc2 = "" 1176 | } 1177 | sCode = "{|" + sc1 + "|pgo(\"" + sCode + "\",{\"" + sName + "\"" + sc2 1178 | for _, v := range params { 1179 | sCode += ",\"" + v + "\"" 1180 | } 1181 | sCode += "})}" 1182 | } 1183 | b, _ := json.Marshal(sCode) 1184 | sParams := fmt.Sprintf("[\"set\",\"%s\",\"cb.%s\",%s]", sName, sbName, string(b)) 1185 | sendout(sParams) 1186 | } 1187 | 1188 | func (o *Widget) SetCallBackFunc(sbName string, fu func([]string) string, sCode string, params ...string) { 1189 | 1190 | var sName = widgFullName(o) 1191 | 1192 | if fu != nil { 1193 | RegFunc(sCode, fu) 1194 | sCode = "fgo(\"" + sCode + "\",{\"" + sName + "\"" 1195 | for _, v := range params { 1196 | sCode += ",\"" + v + "\"" 1197 | } 1198 | sCode += "})" 1199 | } 1200 | b, _ := json.Marshal(sCode) 1201 | sParams := fmt.Sprintf("[\"set\",\"%s\",\"cb.%s\",%s]", sName, sbName, string(b)) 1202 | sendout(sParams) 1203 | } 1204 | 1205 | func (o *Widget) Move(iLeft, iTop, iWidth, iHeight int32) { 1206 | 1207 | var sName = widgFullName(o) 1208 | 1209 | sParams := fmt.Sprintf("[\"set\",\"%s\",\"move\",[%d,%d,%d,%d]]", sName, iLeft, iTop, iWidth, iHeight) 1210 | sendout(sParams) 1211 | } 1212 | 1213 | // Method Enable enables or disables a widget, pointed by o. 1214 | func (o *Widget) Enable(bEnable bool) { 1215 | 1216 | var sName = widgFullName(o) 1217 | 1218 | sParams := fmt.Sprintf("[\"set\",\"%s\",\"enable\",%t]", sName, bEnable) 1219 | sendout(sParams) 1220 | } 1221 | 1222 | // Method Hide hides or shows a widget, pointed by o. 1223 | func (o *Widget) Hide(bHide bool) { 1224 | 1225 | var sName = widgFullName(o) 1226 | 1227 | sParams := fmt.Sprintf("[\"set\",\"%s\",\"hide\",%t]", sName, bHide) 1228 | sendout(sParams) 1229 | } 1230 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/alkresin/external 2 | 3 | go 1.20 4 | -------------------------------------------------------------------------------- /testdata/forms/example.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 25 | 26 | 29 | 30 | 31 | 62 | 63 | 64 | 72 | 73 | 83 | 84 | 86 | 87 | 88 | 89 | 100 | 101 | 103 | 104 | 105 | 106 | 107 | 113 | 114 | 115 | 120 | 121 | 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /testdata/forms/testget2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 13 | 14 | 21 | 22 | 23 | 24 | 25 | 26 | 31 | 32 | 33 | 38 | 39 | 40 | 45 | 46 | 47 | 51 | 52 | 53 | 57 | 58 | 59 | 64 | 65 | 66 | 71 | 72 | 73 | 77 | 78 | 79 | 80 | 81 | 82 | 86 | 87 | 89 | 90 | 91 | 92 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /testdata/images/book.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alkresin/external/a06dc2aadbe9677b0772c5b8a8e6b9a97a6e1866/testdata/images/book.bmp -------------------------------------------------------------------------------- /testdata/images/cl_fl.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alkresin/external/a06dc2aadbe9677b0772c5b8a8e6b9a97a6e1866/testdata/images/cl_fl.bmp -------------------------------------------------------------------------------- /testdata/images/op_fl.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alkresin/external/a06dc2aadbe9677b0772c5b8a8e6b9a97a6e1866/testdata/images/op_fl.bmp -------------------------------------------------------------------------------- /testdata/test.bat: -------------------------------------------------------------------------------- 1 | @rem go build testsock1.go -ldflags="--subsystem windows" 2 | go build -ldflags "-H windowsgui -s -w" %1 -------------------------------------------------------------------------------- /testdata/test.ini: -------------------------------------------------------------------------------- 1 | #guiserver=guiserver.exe 2 | #type=2 3 | #dir= 4 | #file=gs 5 | #address=127.0.0.1 6 | #port=3101 7 | log=1 8 | -------------------------------------------------------------------------------- /testdata/test1.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Alexander S.Kresin , http://www.kresin.ru 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | egui "github.com/alkresin/external" 10 | "io/ioutil" 11 | "strconv" 12 | "time" 13 | ) 14 | 15 | const ( 16 | CLR_LBLUE = 16759929 17 | CLR_LBLUE0 = 12164479 18 | CLR_LBLUE2 = 16770002 19 | CLR_LBLUE3 = 16772062 20 | CLR_LBLUE4 = 16775920 21 | CLR_GRAY = 0x333333 22 | CLR_LGRAY1 = 0xeeeeee 23 | ) 24 | 25 | var arr = [][]string{{"Alex", "17", "1200", "f", "1"}, {"Victor", "42", "1600", "f", "2"}, 26 | {"John", "31", "1000", "f", "3"}} 27 | 28 | func main() { 29 | 30 | var sInit string 31 | 32 | { 33 | b, err := ioutil.ReadFile("test.ini") 34 | if err != nil { 35 | sInit = "" 36 | } else { 37 | sInit = string(b) 38 | } 39 | } 40 | 41 | if egui.Init(sInit) != 0 { 42 | return 43 | } 44 | 45 | egui.CreateStyle(&(egui.Style{Name: "st1", Orient: 1, Colors: []int32{CLR_LBLUE, CLR_LBLUE3}})) 46 | egui.CreateStyle(&(egui.Style{Name: "st2", Colors: []int32{CLR_LBLUE}, BorderW: 3})) 47 | egui.CreateStyle(&(egui.Style{Name: "st3", Colors: []int32{CLR_LBLUE}, 48 | BorderW: 2, BorderClr: CLR_LBLUE0})) 49 | egui.CreateStyle(&(egui.Style{Name: "st4", Colors: []int32{CLR_LBLUE2, CLR_LBLUE3}, 50 | BorderW: 1, BorderClr: CLR_LBLUE})) 51 | 52 | pWindow := &egui.Widget{X: 100, Y: 100, W: 400, H: 280, Title: "External"} 53 | egui.InitMainWindow(pWindow) 54 | 55 | egui.Menu("") 56 | { 57 | egui.Menu("File") 58 | { 59 | egui.AddMenuItem("Set text to label", 0, 60 | func(p []string) string { egui.Widg("main.l1").SetText(p[1]); return "" }, "fsett2", "Bye...1") 61 | egui.AddMenuSeparator() 62 | egui.AddMenuItem("Printing", 0, fprint, "fprint") 63 | egui.AddMenuSeparator() 64 | egui.AddMenuItem("Exit", 0, nil, "hwg_EndWindow()") 65 | } 66 | egui.EndMenu() 67 | 68 | egui.Menu("Dialogs") 69 | { 70 | egui.AddMenuItem("Open dialog", 0, fsett3, "fsett3") 71 | egui.AddMenuItem("Test Tab", 0, ftab, "ftab") 72 | egui.AddMenuItem("Test browse", 0, fbrowse, "fbrowse") 73 | } 74 | egui.EndMenu() 75 | 76 | egui.Menu("Standard dialogs") 77 | { 78 | egui.AddMenuItem("Message boxes", 0, fmbox1, "fmbox1") 79 | egui.AddMenuItem("MsgGet box", 0, fmbox2, "fmbox2") 80 | egui.AddMenuItem("Choice", 0, fmbox3, "fmbox3") 81 | egui.AddMenuItem("Select color", 0, fsele_color, "fsele_color") 82 | egui.AddMenuItem("Select font", 0, fsele_font, "fsele_font") 83 | egui.AddMenuItem("Select file", 0, fsele_file, "fsele_file") 84 | } 85 | egui.EndMenu() 86 | 87 | egui.Menu("Help") 88 | { 89 | egui.AddMenuItem("About", 0, nil, "hwg_MsgInfo(hb_version()+chr(10)+chr(13)+hwg_version(),\"About\")") 90 | } 91 | egui.EndMenu() 92 | } 93 | egui.EndMenu() 94 | 95 | pPanel := pWindow.AddWidget(&egui.Widget{Type: "paneltop", H: 40, 96 | AProps: map[string]string{"HStyle": "st1"}}) 97 | 98 | pPanel.AddWidget(&egui.Widget{Type: "ownbtn", X: 0, Y: 0, W: 56, H: 40, Title: "Date", 99 | AProps: map[string]string{"HStyles": egui.ToString("st1", "st2", "st3")}}) 100 | egui.PLastWidget.SetCallBackProc("onclick", nil, "hwg_WriteStatus(HWindow():GetMain(),1,Dtoc(Date()),.T.)") 101 | 102 | pPanel.AddWidget(&egui.Widget{Type: "ownbtn", X: 56, Y: 0, W: 56, H: 40, Title: "Time", 103 | AProps: map[string]string{"HStyles": egui.ToString("st1", "st2", "st3")}}) 104 | egui.PLastWidget.SetCallBackProc("onclick", nil, "hwg_WriteStatus(HWindow():GetMain(),2,Time(),.T.)") 105 | 106 | pPanel.AddWidget(&egui.Widget{Type: "ownbtn", X: 112, Y: 0, W: 56, H: 40, Title: "Get", 107 | AProps: map[string]string{"HStyles": egui.ToString("st1", "st2", "st3")}}) 108 | egui.PLastWidget.SetCallBackProc("onclick", fsett3, "fsett3") 109 | 110 | pWindow.AddWidget(&egui.Widget{Type: "label", Name: "l1", 111 | X: 20, Y: 60, W: 180, H: 24, Title: "Test of a label", 112 | AProps: map[string]string{"Transpa": "t"}}) 113 | 114 | pWindow.AddWidget(&egui.Widget{Type: "button", X: 200, Y: 56, W: 100, H: 32, Title: "SetText"}) 115 | egui.PLastWidget.SetCallBackProc("onclick", fsett1, "fsett1", "first parameter") 116 | 117 | pWindow.AddWidget(&egui.Widget{Type: "panelbot", H: 32, 118 | AProps: map[string]string{"HStyle": "st4", "AParts": egui.ToString(120, 120, 0)}}) 119 | 120 | pWindow.Activate() 121 | 122 | egui.Exit() 123 | 124 | } 125 | 126 | func fsett1(p []string) string { 127 | 128 | pLabel := egui.Widg("main.l1") 129 | fmt.Println(pLabel.GetText()) 130 | pLabel.SetText(p[1]) 131 | 132 | return "" 133 | } 134 | 135 | func fsett3([]string) string { 136 | 137 | egui.BeginPacket() 138 | egui.SetDateFormat("DD.MM.YYYY") 139 | pFont := egui.CreateFont(&egui.Font{Name: "f1", Family: "Georgia", Height: 16}) 140 | pDlg := &egui.Widget{Name: "dlg", X: 300, Y: 200, W: 400, H: 260, Title: "Dialog Test", Font: pFont} 141 | egui.InitDialog(pDlg) 142 | 143 | pDlg.AddWidget(&egui.Widget{Type: "label", X: 20, Y: 10, W: 160, H: 24, Title: "Identifier:"}) 144 | pDlg.AddWidget(&egui.Widget{Type: "edit", Name: "edi1", X: 20, Y: 32, W: 160, H: 24, 145 | AProps: map[string]string{"Picture": "@!R /XXX:XXX/"}}) 146 | pDlg.AddWidget(&egui.Widget{Type: "label", X: 220, Y: 10, W: 160, H: 24, Title: "Date:"}) 147 | pDlg.AddWidget(&egui.Widget{Type: "edit", Name: "edi2", X: 220, Y: 32, W: 120, H: 24, 148 | Title: time.Now().Format("20060102"), AProps: map[string]string{"Picture": "D@D"}}) 149 | 150 | pDlg.AddWidget(&egui.Widget{Type: "combo", Name: "comb", X: 20, Y: 68, W: 160, H: 24, 151 | AProps: map[string]string{"AItems": egui.ToString("first", "second", "third")}}) 152 | 153 | pDlg.AddWidget(&egui.Widget{Type: "label", X: 220, Y: 68, W: 80, H: 24, Title: "Age:"}) 154 | pDlg.AddWidget(&egui.Widget{Type: "updown", Name: "upd1", X: 280, Y: 68, W: 60, H: 24}) 155 | 156 | pDlg.AddWidget(&egui.Widget{Type: "group", X: 10, Y: 110, W: 180, H: 76, Title: "Check"}) 157 | pDlg.AddWidget(&egui.Widget{Type: "check", Name: "chk1", X: 24, Y: 124, W: 150, H: 24, Title: "Married"}) 158 | pDlg.AddWidget(&egui.Widget{Type: "check", Name: "chk2", X: 24, Y: 148, W: 150, H: 24, Title: "Has children"}) 159 | 160 | pGroup := pDlg.AddWidget(&egui.Widget{Type: "radiogr", Name: "rg", X: 200, Y: 110, W: 180, H: 76, Title: "Radio"}) 161 | pDlg.AddWidget(&egui.Widget{Type: "radio", X: 224, Y: 124, W: 150, H: 24, Title: "Male"}) 162 | pDlg.AddWidget(&egui.Widget{Type: "radio", X: 224, Y: 148, W: 150, H: 24, Title: "Female"}) 163 | egui.RadioEnd(pGroup, 1) 164 | 165 | pDlg.AddWidget(&egui.Widget{Type: "button", X: 150, Y: 220, W: 100, H: 32, Title: "Ok"}) 166 | egui.PLastWidget.SetCallBackProc("onclick", fsett4, "fsett4") 167 | 168 | pDlg.Activate() 169 | egui.EndPacket() 170 | 171 | return "" 172 | } 173 | 174 | func fsett4([]string) string { 175 | arr := egui.GetValues(egui.Wnd("dlg"), []string{"edi1", "edi2", "comb", "chk1", "chk2", "rg", "upd1"}) 176 | 177 | egui.PLastWindow.Close() 178 | egui.MsgInfo("Id: "+arr[0]+"\r\n"+"Date: "+arr[1]+"\r\n"+"Combo: "+arr[2]+"\r\n"+ 179 | "Married: "+arr[3]+"\r\n"+"Has children: "+arr[4]+"\r\n"+"Sex: "+arr[5]+"\r\n"+ 180 | "Age: "+arr[6], "Result", nil, "", "") 181 | 182 | return "" 183 | } 184 | 185 | func ftab([]string) string { 186 | 187 | egui.BeginPacket() 188 | pFont := egui.CreateFont(&egui.Font{Name: "f1", Family: "Georgia", Height: 16}) 189 | pDlg := &egui.Widget{Name: "dlg2", X: 300, Y: 200, W: 200, H: 340, Title: "Tab", Font: pFont, 190 | AProps: map[string]string{"NoExitOnEsc": "t","NoCloseAble": "t"}} 191 | egui.InitDialog(pDlg) 192 | 193 | pTab := pDlg.AddWidget(&egui.Widget{Type: "tab", X: 10, Y: 10, W: 180, H: 280}) 194 | 195 | egui.TabPage(pTab, "First") 196 | pTab.AddWidget(&egui.Widget{Type: "label", X: 20, Y: 30, W: 140, H: 24, Title: "Name:"}) 197 | pTab.AddWidget(&egui.Widget{Type: "edit", X: 20, Y: 52, W: 140, H: 24}) 198 | pTab.AddWidget(&egui.Widget{Type: "label", X: 20, Y: 84, W: 140, H: 24, Title: "Surname:"}) 199 | pTab.AddWidget(&egui.Widget{Type: "edit", X: 20, Y: 106, W: 140, H: 24}) 200 | egui.TabPageEnd(pTab) 201 | 202 | egui.TabPage(pTab, "Second") 203 | pTab.AddWidget(&egui.Widget{Type: "label", X: 20, Y: 40, W: 140, H: 24, Title: "Age:"}) 204 | pTab.AddWidget(&egui.Widget{Type: "edit", X: 20, Y: 62, W: 140, H: 24}) 205 | pTab.AddWidget(&egui.Widget{Type: "label", X: 20, Y: 94, W: 140, H: 24, Title: "Profession:"}) 206 | pTab.AddWidget(&egui.Widget{Type: "edit", X: 20, Y: 116, W: 140, H: 24}) 207 | egui.TabPageEnd(pTab) 208 | 209 | pDlg.AddWidget(&egui.Widget{Type: "button", X: 60, Y: 300, W: 100, H: 32, Title: "Ok"}) 210 | egui.PLastWidget.SetCallBackProc("onclick", ftabclose, "ftabclose") 211 | 212 | pDlg.Activate() 213 | egui.EndPacket() 214 | 215 | return "" 216 | } 217 | 218 | func ftabclose([]string) string { 219 | egui.PLastWindow.Close() 220 | return "" 221 | } 222 | 223 | func fbrowse([]string) string { 224 | 225 | egui.BeginPacket() 226 | pFont := egui.CreateFont(&egui.Font{Name: "f1", Family: "Georgia", Height: 16}) 227 | pDlg := &egui.Widget{Name: "dlg2", X: 300, Y: 200, W: 280, H: 240, Title: "browse", Font: pFont} 228 | egui.InitDialog(pDlg) 229 | 230 | pBrw := pDlg.AddWidget(&egui.Widget{Type: "browse", Name: "brw", X: 10, Y: 10, W: 260, H: 140}) 231 | 232 | pBrw.SetParam("oStyleHead", egui.GetStyle("st1")) 233 | pBrw.SetParam("tColor", CLR_GRAY) 234 | pBrw.SetParam("bColorSel", CLR_LGRAY1) 235 | pBrw.SetParam("htbColor", CLR_LGRAY1) 236 | pBrw.SetParam("tColorSel", 0) 237 | pBrw.SetParam("httColor", 0) 238 | pBrw.SetParam("lInFocus", true) 239 | egui.BrwSetArray(pBrw, &arr) 240 | egui.BrwDelColumn(pBrw, 5) 241 | egui.BrwSetColumn(pBrw, 1, "Name", 1, 0, false, 0) 242 | egui.BrwSetColumn(pBrw, 2, "Age", 1, 0, false, 0) 243 | egui.BrwSetColumn(pBrw, 3, "Salary", 1, 0, true, 0) 244 | egui.BrwSetColumnEx(pBrw, 2, "bColor", CLR_LBLUE3) 245 | egui.BrwSetColumnEx(pBrw, 2, "lResizable", false) 246 | pBrw.SetCallBackProc("onposchanged", fbrwpc, "fbrwpc") 247 | pBrw.SetCallBackProc("onrclick", fbrwrc, "fbrwrc") 248 | 249 | pDlg.AddWidget(&egui.Widget{Type: "label", Name: "l1", 250 | X: 90, Y: 160, W: 100, H: 24, Winstyle: egui.DT_CENTER}) 251 | 252 | pDlg.AddWidget(&egui.Widget{Type: "button", X: 90, Y: 200, W: 100, H: 32, Title: "Ok"}) 253 | egui.PLastWidget.SetCallBackProc("onclick", fbrwclose, "fbrwclose") 254 | 255 | pDlg.Activate() 256 | egui.EndPacket() 257 | 258 | return "" 259 | } 260 | 261 | func fbrwclose([]string) string { 262 | 263 | arrNew := egui.BrwGetArray(egui.Widg("dlg2.brw")) 264 | 265 | egui.PLastWindow.Close() 266 | s := "" 267 | for i, a := range arrNew { 268 | if a[2] != arr[i][2] { 269 | s += a[0] + " => " + a[2] + "\r\n" 270 | } 271 | } 272 | 273 | if s != "" { 274 | egui.MsgInfo(s, "Changes", nil, "", "") 275 | } 276 | 277 | return "" 278 | } 279 | 280 | func fbrwpc(p []string) string { 281 | 282 | pLabel := egui.Widg("dlg2.l1") 283 | if len(p) > 1 { 284 | i, _ := strconv.Atoi(p[1]) 285 | if i > 0 && i <= 3 { 286 | pLabel.SetText(arr[i-1][0]) 287 | } 288 | } 289 | return "" 290 | } 291 | 292 | func fbrwrc(p []string) string { 293 | 294 | if len(p) > 2 { 295 | egui.MsgInfo(p[0]+" Row: "+p[2]+" Col: "+p[1], "Right click position", nil, "", "") 296 | } 297 | return "" 298 | } 299 | 300 | func fmbox1(p []string) string { 301 | if p[0] == "menu" { 302 | egui.MsgYesNo("Yes or No???", "MsgBox", fmbox1, "fmbox1", "mm1") 303 | } else if p[0] == "mm1" { 304 | if p[1] == "t" { 305 | egui.MsgInfo("Yes!", "Answer", nil, "", "") 306 | } else { 307 | egui.MsgInfo("No...", "Answer", nil, "", "") 308 | } 309 | } 310 | return "" 311 | } 312 | 313 | func fmbox2(p []string) string { 314 | if p[0] == "menu" { 315 | egui.MsgGet("Input something:", "MsgGet", 0, fmbox2, "fmbox2", "mm1") 316 | } else if p[0] == "mm1" { 317 | egui.MsgInfo(p[1], "Answer", nil, "", "") 318 | } 319 | return "" 320 | } 321 | 322 | func fmbox3(p []string) string { 323 | if p[0] == "menu" { 324 | arr := []string{"Alex Petrov", "Serg Lama", "Jimmy Hendrix", "Dorian Gray", "Victor Peti"} 325 | egui.Choice(arr, "Select from a list", fmbox3, "fmbox3", "mm1") 326 | } else if p[0] == "mm1" { 327 | egui.MsgInfo(p[1], "Answer", nil, "", "") 328 | } 329 | return "" 330 | } 331 | 332 | func fsele_color(p []string) string { 333 | if p[0] == "menu" { 334 | egui.SelectColor(0, fsele_color, "fsele_color", "mm1") 335 | } else { 336 | iColor, _ := strconv.Atoi(p[1]) 337 | egui.Widg("main.l1").SetColor(int32(iColor), -1) 338 | } 339 | return "" 340 | } 341 | 342 | func fsele_font(p []string) string { 343 | if p[0] == "menu" { 344 | egui.SelectFont(fsele_font, "fsele_font", "") 345 | } else { 346 | fmt.Println("font id: ", p[0]) 347 | if pFont := egui.GetFont(p[0]); pFont != nil { 348 | if len(p) < 8 { 349 | } else { 350 | fmt.Println("font fam: ", p[1]) 351 | } 352 | egui.Widg("main.l1").SetFont( pFont ); 353 | } 354 | } 355 | return "" 356 | } 357 | 358 | func fsele_file(p []string) string { 359 | if p[0] == "menu" { 360 | egui.SelectFile("", fsele_file, "fsele_file", "mm1") 361 | } else { 362 | if p[1] == "" { 363 | egui.MsgInfo("Nothing selected", "Result", nil, "", "") 364 | } else { 365 | egui.MsgInfo(p[1], "File selected", nil, "", "") 366 | } 367 | } 368 | return "" 369 | } 370 | 371 | func fprint(p []string) string { 372 | if p[0] == "menu" { 373 | egui.InitPrinter(&egui.Printer{SPrinter: "...", BPreview: true}, "fprint", fprint, "mm1") 374 | } else { 375 | pPrinter := egui.PLastPrinter 376 | pFont := pPrinter.AddFont(&egui.Font{Family: "Times New Roman", Height: 10}) 377 | pPrinter.StartPage() 378 | pPrinter.SetFont(pFont) 379 | pPrinter.Box(5, 5, 200, 282) 380 | pPrinter.Say(50, 10, 165, 26, "Printing first sample !", egui.DT_CENTER) 381 | pPrinter.Line(45, 30, 170, 30) 382 | pPrinter.Line(45, 5, 45, 30) 383 | pPrinter.Line(170, 5, 170, 30) 384 | pPrinter.Say(50, 120, 150, 132, "----------", egui.DT_CENTER) 385 | pPrinter.Box(50, 134, 160, 146) 386 | pPrinter.Say(50, 135, 160, 146, "End Of Report", egui.DT_CENTER) 387 | pPrinter.EndPage() 388 | pPrinter.End() 389 | 390 | } 391 | return "" 392 | } 393 | -------------------------------------------------------------------------------- /testdata/test2.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Alexander S.Kresin , http://www.kresin.ru 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | egui "github.com/alkresin/external" 9 | "io/ioutil" 10 | ) 11 | 12 | const ( 13 | CLR_LBLUE = 16759929 14 | CLR_LBLUE0 = 12164479 15 | CLR_LBLUE2 = 16770002 16 | CLR_LBLUE3 = 16772062 17 | CLR_LBLUE4 = 16775920 18 | ) 19 | 20 | func main() { 21 | 22 | var sInit string 23 | 24 | { 25 | b, err := ioutil.ReadFile("test.ini") 26 | if err != nil { 27 | sInit = "" 28 | } else { 29 | sInit = string(b) 30 | } 31 | } 32 | 33 | if egui.Init(sInit) != 0 { 34 | return 35 | } 36 | 37 | egui.SetImagePath("images/") 38 | 39 | egui.CreateStyle(&egui.Style{Name: "st1", Orient: 1, Colors: []int32{CLR_LBLUE, CLR_LBLUE3}}) 40 | egui.CreateStyle(&egui.Style{Name: "st2", Colors: []int32{CLR_LBLUE}, BorderW: 3}) 41 | egui.CreateStyle(&egui.Style{Name: "st3", Colors: []int32{CLR_LBLUE}, 42 | BorderW: 2, BorderClr: CLR_LBLUE0}) 43 | egui.CreateStyle(&egui.Style{Name: "st4", Colors: []int32{CLR_LBLUE2, CLR_LBLUE3}, 44 | BorderW: 1, BorderClr: CLR_LBLUE}) 45 | 46 | pWindow := &egui.Widget{X: 100, Y: 100, W: 400, H: 280, Title: "External"} 47 | egui.InitMainWindow(pWindow) 48 | 49 | pPanel := pWindow.AddWidget(&egui.Widget{Type: "paneltop", H: 40, 50 | AProps: map[string]string{"HStyle": "st1"}}) 51 | 52 | pPanel.AddWidget(&egui.Widget{Type: "ownbtn", X: 0, Y: 0, W: 56, H: 40, Title: "Date", 53 | AProps: map[string]string{"HStyles": egui.ToString("st1", "st2", "st3")}}) 54 | egui.PLastWidget.SetCallBackProc("onclick", nil, "hwg_WriteStatus(HWindow():GetMain(),1,Dtoc(Date()),.T.)") 55 | 56 | pTree := pWindow.AddWidget(&egui.Widget{Type: "tree", X: 0, Y: 40, W: 200, H: 208, 57 | AProps: map[string]string{"AImages": egui.ToString("cl_fl.bmp", "op_fl.bmp")}}) 58 | pTree.SetCallBackProc("onsize", nil, "{|o,x,y|o:Move(,,,y-72)}") 59 | 60 | egui.InsertNode(pTree, "", "n1", "First", "", nil, nil, "") 61 | egui.InsertNode(pTree, "", "n2", "Second «item»", "", nil, nil, "") 62 | egui.InsertNode(pTree, "n2", "n2a", "second-1", "", []string{"book.bmp"}, nil, "hwg_msginfo(\"n2a\")") 63 | egui.InsertNode(pTree, "", "n3", "Third", "", nil, nil, "") 64 | 65 | pEdi := pWindow.AddWidget(&egui.Widget{Type: "edit", Name: "edim", X: 204, Y: 40, W: 196, H: 180, 66 | Winstyle: egui.ES_MULTILINE}) 67 | egui.PLastWidget.SetCallBackProc("onsize", nil, "{|o,x,y|o:Move(,,x-o:nLeft,y-72)}") 68 | 69 | pWindow.AddWidget(&egui.Widget{Type: "splitter", X: 200, Y: 40, W: 4, H: 208, 70 | AProps: map[string]string{"ALeft": egui.ToString(pTree), "ARight": egui.ToString(pEdi)}}) 71 | egui.PLastWidget.SetCallBackProc("onsize", nil, "{|o,x,y|o:Move(,,,y-72)}") 72 | 73 | pWindow.AddWidget(&egui.Widget{Type: "panelbot", H: 32, 74 | AProps: map[string]string{"HStyle": "st4", "AParts": egui.ToString(120, 120, 0)}}) 75 | 76 | pWindow.Activate() 77 | 78 | egui.Exit() 79 | 80 | } 81 | -------------------------------------------------------------------------------- /testdata/test3.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Alexander S.Kresin , http://www.kresin.ru 2 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 3 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 4 | // A sample from a "The Go programming language", a book of Alan A. A. Donovan & Brian W. Kernighan, 5 | // adapted by Alexander S.Kresin for External GUI framework. 6 | package main 7 | 8 | import ( 9 | egui "github.com/alkresin/external" 10 | "image" 11 | "image/color" 12 | "image/jpeg" 13 | "io/ioutil" 14 | "math/cmplx" 15 | "os" 16 | ) 17 | 18 | const ( 19 | CLR_LBLUE = 16759929 20 | CLR_LBLUE0 = 12164479 21 | CLR_LBLUE2 = 16770002 22 | CLR_LBLUE3 = 16772062 23 | CLR_LBLUE4 = 16775920 24 | 25 | CLR_LGRAY1 = 15658734 26 | CLR_LGRAY2 = 14540253 27 | ) 28 | 29 | var width, height = 680, 680 30 | 31 | func main() { 32 | 33 | var sInit string 34 | 35 | { 36 | b, err := ioutil.ReadFile("test.ini") 37 | if err != nil { 38 | sInit = "" 39 | } else { 40 | sInit = string(b) 41 | } 42 | } 43 | 44 | if egui.Init(sInit) != 0 { 45 | return 46 | } 47 | 48 | egui.CreateStyle(&egui.Style{Name: "st1", Orient: 1, Colors: []int32{CLR_LBLUE, CLR_LBLUE3}}) 49 | egui.CreateStyle(&egui.Style{Name: "st2", Colors: []int32{CLR_LBLUE}, BorderW: 3}) 50 | egui.CreateStyle(&egui.Style{Name: "st3", Colors: []int32{CLR_LBLUE}, 51 | BorderW: 2, BorderClr: CLR_LBLUE0}) 52 | pFont := egui.CreateFont(&egui.Font{Name: "f1", Family: "Georgia", Height: -14}) 53 | 54 | pWindow := &egui.Widget{X: 100, Y: 100, W: 716, H: 764, Title: "Test3", BColor: 1, Font: pFont} 55 | egui.InitMainWindow(pWindow) 56 | 57 | pPanel := pWindow.AddWidget(&egui.Widget{Type: "paneltop", H: 32, 58 | AProps: map[string]string{"HStyle": "st1"}}) 59 | 60 | pPanel.AddWidget(&egui.Widget{Type: "ownbtn", X: 0, Y: 0, W: 60, H: 32, Title: "Exit", 61 | AProps: map[string]string{"HStyles": egui.ToString("st1", "st2", "st3")}}) 62 | egui.PLastWidget.SetCallBackProc("onclick", nil, "hwg_EndWindow()") 63 | 64 | pPanel.AddWidget(&egui.Widget{Type: "ownbtn", X: 60, Y: 0, W: 60, H: 32, Title: "M-brot", 65 | AProps: map[string]string{"HStyles": egui.ToString("st1", "st2", "st3")}}) 66 | egui.PLastWidget.SetCallBackProc("onclick", fu1, "fu1", "1") 67 | 68 | pPanel.AddWidget(&egui.Widget{Type: "ownbtn", X: 120, Y: 0, W: 60, H: 32, Title: "Acos", 69 | AProps: map[string]string{"HStyles": egui.ToString("st1", "st2", "st3")}}) 70 | egui.PLastWidget.SetCallBackProc("onclick", fu1, "fu1", "2") 71 | 72 | pPanel.AddWidget(&egui.Widget{Type: "ownbtn", X: 180, Y: 0, W: 60, H: 32, Title: "Sqrt", 73 | AProps: map[string]string{"HStyles": egui.ToString("st1", "st2", "st3")}}) 74 | egui.PLastWidget.SetCallBackProc("onclick", fu1, "fu1", "3") 75 | 76 | pPanel.AddWidget(&egui.Widget{Type: "ownbtn", X: 240, Y: 0, W: 60, H: 32, Title: "Newton", 77 | AProps: map[string]string{"HStyles": egui.ToString("st1", "st2", "st3")}}) 78 | egui.PLastWidget.SetCallBackProc("onclick", fu1, "fu1", "4") 79 | 80 | pWindow.AddWidget(&egui.Widget{Type: "bitmap", Name: "img", X: 10, Y: 36, W: 680, 81 | H: 680, BColor: CLR_LGRAY2}) 82 | // Anchor: egui.A_TOPABS+egui.A_LEFTABS+egui.A_BOTTOMABS+egui.A_RIGHTABS })) 83 | //egui.PLastWidget.SetCallBackProc("onsize", nil, "{|o,x,y|o:Move(,,x-o:nLeft,y-72)}") 84 | 85 | pWindow.Activate() 86 | 87 | egui.Exit() 88 | 89 | } 90 | 91 | func fu1(p []string) string { 92 | 93 | egui.PLastWindow.Move(-1, -1, 716, 764) 94 | pImg := egui.Widg("main.img") 95 | pImg.SetImage("") 96 | 97 | switch p[1] { 98 | case "1": 99 | draw(mandelbrot) 100 | case "2": 101 | draw(acos) 102 | case "3": 103 | draw(sqrt) 104 | case "4": 105 | draw(newton) 106 | } 107 | 108 | pImg.SetImage("a1.jpg") 109 | return "" 110 | } 111 | 112 | func draw(fu func(z complex128) color.Color) { 113 | const ( 114 | xmin, ymin, xmax, ymax = -2, -2, +2, +2 115 | ) 116 | 117 | img := image.NewRGBA(image.Rect(0, 0, width, height)) 118 | for py := 0; py < height; py++ { 119 | y := float64(py)/float64(height)*(ymax-ymin) + ymin 120 | for px := 0; px < width; px++ { 121 | x := float64(px)/float64(width)*(xmax-xmin) + xmin 122 | z := complex(x, y) 123 | // Image point (px, py) represents complex value z. 124 | img.Set(px, py, fu(z)) 125 | } 126 | } 127 | fo, _ := os.Create("a1.jpg") 128 | jpeg.Encode(fo, img, nil) // NOTE: ignoring errors 129 | fo.Close() 130 | } 131 | 132 | func mandelbrot(z complex128) color.Color { 133 | const iterations = 200 134 | const contrast = 15 135 | 136 | var v complex128 137 | for n := uint8(0); n < iterations; n++ { 138 | v = v*v + z 139 | if cmplx.Abs(v) > 2 { 140 | return color.Gray{255 - contrast*n} 141 | } 142 | } 143 | return color.Black 144 | } 145 | 146 | func acos(z complex128) color.Color { 147 | v := cmplx.Acos(z) 148 | blue := uint8(real(v)*128) + 127 149 | red := uint8(imag(v)*128) + 127 150 | return color.YCbCr{192, blue, red} 151 | } 152 | 153 | func sqrt(z complex128) color.Color { 154 | v := cmplx.Sqrt(z) 155 | blue := uint8(real(v)*128) + 127 156 | red := uint8(imag(v)*128) + 127 157 | return color.YCbCr{128, blue, red} 158 | } 159 | 160 | // f(x) = x^4 - 1 161 | // 162 | // z' = z - f(z)/f'(z) 163 | // = z - (z^4 - 1) / (4 * z^3) 164 | // = z - (z - 1/z^3) / 4 165 | func newton(z complex128) color.Color { 166 | const iterations = 37 167 | const contrast = 7 168 | for i := uint8(0); i < iterations; i++ { 169 | z -= (z - 1/(z*z*z)) / 4 170 | if cmplx.Abs(z*z*z*z-1) < 1e-6 { 171 | return color.Gray{255 - contrast*i} 172 | } 173 | } 174 | return color.Black 175 | } 176 | -------------------------------------------------------------------------------- /testdata/test_form1.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Alexander S.Kresin , http://www.kresin.ru 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | egui "github.com/alkresin/external" 9 | ) 10 | 11 | func main() { 12 | 13 | if egui.Init("") != 0 { 14 | return 15 | } 16 | 17 | f := func (p []string)string { 18 | egui.EvalProc("oLabel1:SetText(\"Hi, friends! (" + p[0] + ")\")") 19 | return "" 20 | } 21 | egui.RegFunc("fmenu1", f) 22 | 23 | f = func ([]string)string { return "Hi from Go!" } 24 | egui.RegFunc("fmenu2", f) 25 | 26 | egui.OpenMainForm("forms/example.xml") 27 | 28 | egui.Exit() 29 | 30 | } 31 | 32 | -------------------------------------------------------------------------------- /testdata/test_form2.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Alexander S.Kresin , http://www.kresin.ru 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import egui "github.com/alkresin/external" 8 | 9 | func main() { 10 | 11 | if egui.Init("") != 0 { 12 | return 13 | } 14 | 15 | pWindow := &egui.Widget{X: 100, Y: 100, W: 400, H: 280, Title: "External"} 16 | egui.InitMainWindow(pWindow) 17 | 18 | egui.Menu("") 19 | egui.Menu("File") 20 | egui.AddMenuItem("Open form", 0, openf, "openf") 21 | egui.AddMenuSeparator() 22 | egui.AddMenuItem("Exit", 0, nil, "hwg_EndWindow()") 23 | egui.EndMenu() 24 | egui.Menu("Help") 25 | egui.AddMenuItem("About", 0, nil, "hwg_MsgInfo(hb_version()+chr(10)+chr(13)+hwg_version(),\"About\")") 26 | egui.EndMenu() 27 | egui.EndMenu() 28 | 29 | pWindow.Activate() 30 | 31 | egui.Exit() 32 | } 33 | 34 | func openf([]string) string { 35 | 36 | egui.OpenForm("forms/testget2.xml") 37 | return "" 38 | } 39 | --------------------------------------------------------------------------------