@@ -7,13 +7,24 @@ package egui
7
7
//go:generate core generate -add-types
8
8
9
9
import (
10
+ "embed"
11
+ "fmt"
10
12
"io/fs"
13
+ "net/http"
14
+ "strings"
11
15
"sync"
12
16
17
+ "cogentcore.org/core/base/errors"
18
+ "cogentcore.org/core/base/fileinfo/mimedata"
19
+ "cogentcore.org/core/base/labels"
13
20
"cogentcore.org/core/core"
14
21
"cogentcore.org/core/enums"
15
22
"cogentcore.org/core/events"
23
+ "cogentcore.org/core/htmlcore"
16
24
"cogentcore.org/core/styles"
25
+ "cogentcore.org/core/styles/abilities"
26
+ "cogentcore.org/core/system"
27
+ "cogentcore.org/core/text/textcore"
17
28
"cogentcore.org/core/tree"
18
29
_ "cogentcore.org/lab/gosl/slbool/slboolcore" // include to get gui views
19
30
"cogentcore.org/lab/lab"
@@ -40,6 +51,9 @@ type GUI struct {
40
51
// Body is the entire content of the sim window.
41
52
Body * core.Body `display:"-"`
42
53
54
+ // Readme is the sim readme frame
55
+ Readme * core.Frame `display:"-"`
56
+
43
57
// OnStop is called when running is stopped through the GUI,
44
58
// via the Stopped method. It should update the network view for example.
45
59
OnStop func (mode , level enums.Enum )
@@ -136,8 +150,8 @@ func NewGUIBody(b tree.Node, sim any, fsroot fs.FS, appname, title, about string
136
150
// a [core.Form] editor of the given sim object, and a filetree for the data filesystem
137
151
// rooted at fsroot, and with given app name, title, and about information.
138
152
// The first arg is an optional existing [core.Body] to make into: if nil then
139
- // a new body is made first.
140
- func (gui * GUI ) MakeBody (b tree.Node , sim any , fsroot fs.FS , appname , title , about string ) {
153
+ // a new body is made first. It takes an optional fs with a README.md file.
154
+ func (gui * GUI ) MakeBody (b tree.Node , sim any , fsroot fs.FS , appname , title , about string , readme ... embed. FS ) {
141
155
gui .StopLevel = etime .NoTime // corresponds to the first level typically
142
156
core .NoSentenceCaseFor = append (core .NoSentenceCaseFor , "github.com/emer" )
143
157
if b == nil {
@@ -183,8 +197,121 @@ func (gui *GUI) MakeBody(b tree.Node, sim any, fsroot fs.FS, appname, title, abo
183
197
gui .CycleUpdateInterval = 10
184
198
gui .UpdateFiles ()
185
199
gui .Files .Tabber = tabs
186
- split .SetTiles (core .TileSplit , core .TileSpan )
187
- split .SetSplits (.2 , .5 , .8 )
200
+
201
+ if len (readme ) > 0 {
202
+ gui .addReadme (readme [0 ], split )
203
+ } else {
204
+ split .SetTiles (core .TileSplit , core .TileSpan )
205
+ split .SetSplits (.2 , .5 , .8 )
206
+ }
207
+ }
208
+
209
+ func (gui * GUI ) addReadme (readmefs embed.FS , split * core.Splits ) {
210
+ gui .Readme = core .NewFrame (split )
211
+ gui .Readme .Name = "readme"
212
+
213
+ split .SetTiles (core .TileSplit , core .TileSpan , core .TileSpan )
214
+ split .SetSplits (.2 , .5 , .5 , .3 )
215
+
216
+ ctx := htmlcore .NewContext ()
217
+
218
+ ctx .GetURL = func (rawURL string ) (* http.Response , error ) {
219
+ return htmlcore .GetURLFromFS (readmefs , rawURL )
220
+ }
221
+
222
+ ctx .AddWikilinkHandler (gui .readmeWikilink ("sim" ))
223
+
224
+ ctx .OpenURL = gui .readmeOpenURL
225
+
226
+ eds := []* textcore.Editor {}
227
+
228
+ ctx .ElementHandlers ["sim-question" ] = func (ctx * htmlcore.Context ) bool {
229
+ ed := textcore .NewEditor (ctx .BlockParent )
230
+ ed .Lines .Settings .LineNumbers = false
231
+ eds = append (eds , ed )
232
+ id := htmlcore .GetAttr (ctx .Node , "id" )
233
+ ed .SetName (id )
234
+ return true
235
+ }
236
+
237
+ core .NewButton (gui .Readme ).SetText ("Copy answers" ).OnClick (func (e events.Event ) {
238
+ clipboard := gui .Readme .Clipboard ()
239
+ var ab strings.Builder
240
+ for _ , ed := range eds {
241
+ ab .WriteString ("## Question " + ed .Name + "\n " + ed .Lines .String () + "\n " )
242
+ }
243
+ answers := ab .String ()
244
+ md := mimedata .NewText (answers )
245
+ clipboard .Write (md )
246
+ core .MessageSnackbar (gui .Body , "Answers copied to clipboard" )
247
+ })
248
+
249
+ readme , err := readmefs .ReadFile ("README.md" )
250
+
251
+ if errors .Log (err ) == nil {
252
+ htmlcore .ReadMDString (ctx , gui .Readme , string (readme ))
253
+ }
254
+ }
255
+
256
+ func (gui * GUI ) readmeWikilink (prefix string ) htmlcore.WikilinkHandler {
257
+ return func (text string ) (url string , label string ) {
258
+ if ! strings .HasPrefix (text , prefix + ":" ) {
259
+ return "" , ""
260
+ }
261
+ text = strings .TrimPrefix (text , prefix + ":" )
262
+ url = prefix + "://" + text
263
+ if strings .Contains (text , "/" ) {
264
+ _ , text , _ = strings .Cut (text , "/" )
265
+ }
266
+ return url , text
267
+ }
268
+ }
269
+
270
+ // readmeOpenURL Parses URL, highlights linked button or opens URL
271
+ func (gui * GUI ) readmeOpenURL (url string ) {
272
+ focusSet := false
273
+ if ! strings .HasPrefix (url , "sim://" ) {
274
+ system .TheApp .OpenURL (url )
275
+ return
276
+ }
277
+
278
+ text := strings .TrimPrefix (url , "sim://" )
279
+ var pathPrefix string = ""
280
+ hasPath := false
281
+ if strings .Contains (text , "/" ) {
282
+ pathPrefix , text , hasPath = strings .Cut (text , "/" )
283
+ }
284
+
285
+ gui .Body .Scene .WidgetWalkDown (func (cw core.Widget , cwb * core.WidgetBase ) bool {
286
+ if focusSet {
287
+ return tree .Break
288
+ }
289
+ if ! hasPath && ! cwb .IsDisplayable () {
290
+ return tree .Break
291
+ }
292
+ if hasPath && ! strings .Contains (cw .AsTree ().Path (), pathPrefix ) {
293
+ return tree .Continue
294
+ }
295
+ label := labels .ToLabel (cw )
296
+ if ! strings .EqualFold (label , text ) {
297
+ return tree .Continue
298
+ }
299
+ if cwb .AbilityIs (abilities .Focusable ) {
300
+ cwb .SetFocus ()
301
+ focusSet = true
302
+ return tree .Break
303
+ }
304
+ next := core .AsWidget (tree .Next (cwb ))
305
+ if next .AbilityIs (abilities .Focusable ) {
306
+ next .SetFocus ()
307
+ focusSet = true
308
+ return tree .Break
309
+ }
310
+ return tree .Continue
311
+ })
312
+ if ! focusSet {
313
+ core .ErrorSnackbar (gui .Body , fmt .Errorf ("invalid sim url %q" , url ))
314
+ }
188
315
}
189
316
190
317
// AddNetView adds NetView in tab with given name
0 commit comments