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