GUI
GUI is the source package
# Open?(go)
# New?(go)
# View?(go)
# CreateNode?(go)
# extractStylesheets?(go)
# extractStyleTags?(go)
# localizePath?(go)
# encapsulateText?(go)
# matchFactory?(go)
# removeWhitespace?(go)
# removeHTMLComments?(go)
1package gui
2
3import (
4 "bufio"
5 _ "embed"
6 "gui/cstyle"
7 "gui/cstyle/plugins/block"
8 "gui/cstyle/plugins/flex"
9 "gui/cstyle/plugins/inline"
10 "gui/window"
11
12 "gui/element"
13 "gui/events"
14 "gui/utils"
15 "net/url"
16 "os"
17 "path/filepath"
18 "regexp"
19 "strconv"
20 "strings"
21
22 rl "github.com/gen2brain/raylib-go/raylib"
23 "golang.org/x/net/html"
24)
25
26// _ "net/http/pprof"
27
28//go:embed master.css
29var mastercss string
30
31type Window struct {
32 CSS cstyle.CSS
33 Document element.Node
34}
35
36func Open(path string) Window {
37 window := New()
38
39 styleSheets, styleTags, htmlNodes := parseHTMLFromFile(path)
40
41 for _, v := range styleSheets {
42 window.CSS.StyleSheet(v)
43 }
44
45 for _, v := range styleTags {
46 window.CSS.StyleTag(v)
47 }
48
49 CreateNode(htmlNodes, &window.Document)
50
51 return window
52}
53
54func New() Window {
55 css := cstyle.CSS{
56 Width: 800,
57 Height: 450,
58 }
59
60 css.StyleTag(mastercss)
61 // This is still apart of computestyle
62 // css.AddPlugin(position.Init())
63 css.AddPlugin(inline.Init())
64 css.AddPlugin(block.Init())
65 css.AddPlugin(flex.Init())
66
67 el := element.Node{}
68 document := el.CreateElement("ROOT")
69 document.Style["width"] = "800px"
70 document.Style["height"] = "450px"
71 document.Properties.Id = "ROOT"
72 return Window{
73 CSS: css,
74 Document: document,
75 }
76}
77
78func View(data *Window, width, height int32) {
79 data.Document.Properties.Computed["width"] = float32(width)
80 data.Document.Properties.Computed["height"] = float32(height)
81 data.Document.Style["width"] = strconv.Itoa(int(width)) + "px"
82 data.Document.Style["height"] = strconv.Itoa(int(height)) + "px"
83
84 wm := window.NewWindowManager()
85 wm.FPS = true
86
87 wm.OpenWindow(width, height)
88 defer wm.CloseWindow()
89
90 evts := map[string]element.EventList{}
91
92 eventStore := &evts
93
94 // Main game loop
95 for !wm.WindowShouldClose() {
96 rl.BeginDrawing()
97
98 // Check if the window size has changed
99 newWidth := int32(rl.GetScreenWidth())
100 newHeight := int32(rl.GetScreenHeight())
101
102 if newWidth != width || newHeight != height {
103 rl.ClearBackground(rl.RayWhite)
104 // Window has been resized, handle the event
105 width = newWidth
106 height = newHeight
107
108 data.CSS.Width = float32(width)
109 data.CSS.Height = float32(height)
110
111 data.Document.Style["width"] = strconv.Itoa(int(width)) + "px"
112 data.Document.Style["height"] = strconv.Itoa(int(height)) + "px"
113 data.Document.Properties.Computed["width"] = float32(width)
114 data.Document.Properties.Computed["height"] = float32(height)
115 }
116
117 eventStore = events.GetEvents(&data.Document.Children[0], eventStore)
118 data.CSS.ComputeNodeStyle(&data.Document.Children[0])
119 rd := data.CSS.Render(data.Document.Children[0])
120 wm.LoadTextures(rd)
121 wm.Draw(rd)
122
123 events.RunEvents(eventStore)
124
125 rl.EndDrawing()
126 }
127}
128
129func CreateNode(node *html.Node, parent *element.Node) {
130 if node.Type == html.ElementNode {
131 newNode := parent.CreateElement(node.Data)
132 for _, attr := range node.Attr {
133 if attr.Key == "class" {
134 classes := strings.Split(attr.Val, " ")
135 for _, class := range classes {
136 newNode.ClassList.Add(class)
137 }
138 } else if attr.Key == "id" {
139 newNode.Id = attr.Val
140 } else if attr.Key == "contenteditable" && (attr.Val == "" || attr.Val == "true") {
141 newNode.Properties.Editable = true
142 } else if attr.Key == "href" {
143 newNode.Href = attr.Val
144 } else if attr.Key == "src" {
145 newNode.Src = attr.Val
146 } else if attr.Key == "title" {
147 newNode.Title = attr.Val
148 } else {
149 newNode.SetAttribute(attr.Key, attr.Val)
150 }
151 }
152 newNode.InnerText = utils.GetInnerText(node)
153 // Recursively traverse child nodes
154 for child := node.FirstChild; child != nil; child = child.NextSibling {
155 if child.Type == html.ElementNode {
156 CreateNode(child, &newNode)
157 }
158 }
159 parent.AppendChild(newNode)
160
161 } else {
162 for child := node.FirstChild; child != nil; child = child.NextSibling {
163 if child.Type == html.ElementNode {
164 CreateNode(child, parent)
165 }
166 }
167 }
168
169}
170
171func parseHTMLFromFile(path string) ([]string, []string, *html.Node) {
172 file, _ := os.Open(path)
173 defer file.Close()
174
175 scanner := bufio.NewScanner(file)
176 var htmlContent string
177
178 for scanner.Scan() {
179 htmlContent += scanner.Text() + "\n"
180 }
181
182 // println(encapsulateText(htmlContent))
183 doc, _ := html.Parse(strings.NewReader(encapsulateText(htmlContent)))
184
185 // Extract stylesheet link tags and style tags
186 stylesheets := extractStylesheets(doc, filepath.Dir(path))
187 styleTags := extractStyleTags(doc)
188
189 return stylesheets, styleTags, doc
190}
191
192func extractStylesheets(n *html.Node, baseDir string) []string {
193 var stylesheets []string
194
195 var dfs func(*html.Node)
196 dfs = func(node *html.Node) {
197 if node.Type == html.ElementNode && node.Data == "link" {
198 var href string
199 isStylesheet := false
200
201 for _, attr := range node.Attr {
202 if attr.Key == "rel" && attr.Val == "stylesheet" {
203 isStylesheet = true
204 } else if attr.Key == "href" {
205 href = attr.Val
206 }
207 }
208
209 if isStylesheet {
210 resolvedHref := localizePath(baseDir, href)
211 stylesheets = append(stylesheets, resolvedHref)
212 }
213 }
214
215 for c := node.FirstChild; c != nil; c = c.NextSibling {
216 dfs(c)
217 }
218 }
219
220 dfs(n)
221 return stylesheets
222}
223
224func extractStyleTags(n *html.Node) []string {
225 var styleTags []string
226
227 var dfs func(*html.Node)
228 dfs = func(node *html.Node) {
229 if node.Type == html.ElementNode && node.Data == "style" {
230 var styleContent strings.Builder
231 for c := node.FirstChild; c != nil; c = c.NextSibling {
232 if c.Type == html.TextNode {
233 styleContent.WriteString(c.Data)
234 }
235 }
236 styleTags = append(styleTags, styleContent.String())
237 }
238
239 for c := node.FirstChild; c != nil; c = c.NextSibling {
240 dfs(c)
241 }
242 }
243
244 dfs(n)
245 return styleTags
246}
247
248func localizePath(rootPath, filePath string) string {
249 // Check if the file path has a scheme, indicating it's a URL
250 u, err := url.Parse(filePath)
251 if err == nil && u.Scheme != "" {
252 return filePath
253 }
254
255 // Join the root path and the file path to create an absolute path
256 absPath := filepath.Join(rootPath, filePath)
257
258 // If the absolute path is the same as the original path, return it
259 if absPath == filePath {
260 return filePath
261 }
262
263 return "./" + absPath
264}
265
266func encapsulateText(htmlString string) string {
267 htmlString = removeHTMLComments(htmlString)
268 openOpen := regexp.MustCompile(`(<\w+[^>]*>)([^<]+)(<\w+[^>]*>)`)
269 closeOpen := regexp.MustCompile(`(</\w+[^>]*>)([^<]+)(<\w+[^>]*>)`)
270 closeClose := regexp.MustCompile(`(</\w+[^>]*>)([^<]+)(</\w+[^>]*>)`)
271 a := matchFactory(openOpen)
272 t := openOpen.ReplaceAllStringFunc(htmlString, a)
273 b := matchFactory(closeOpen)
274 u := closeOpen.ReplaceAllStringFunc(t, b)
275 c := matchFactory(closeClose)
276 v := closeClose.ReplaceAllStringFunc(u, c)
277 return v
278}
279
280func matchFactory(re *regexp.Regexp) func(string) string {
281 return func(match string) string {
282 submatches := re.FindStringSubmatch(match)
283 if len(submatches) != 4 {
284 return match
285 }
286
287 // Process submatches
288 if len(removeWhitespace(submatches[2])) > 0 {
289 return submatches[1] + "<notaspan>" + submatches[2] + "</notaspan>" + submatches[3]
290 } else {
291 return match
292 }
293 }
294}
295func removeWhitespace(htmlString string) string {
296 // Remove extra white space
297 reSpaces := regexp.MustCompile(`\s+`)
298 htmlString = reSpaces.ReplaceAllString(htmlString, " ")
299
300 // Trim leading and trailing white space
301 htmlString = strings.TrimSpace(htmlString)
302
303 return htmlString
304}
305
306func removeHTMLComments(htmlString string) string {
307 re := regexp.MustCompile(`<!--(.*?)-->`)
308 return re.ReplaceAllString(htmlString, "")
309}