vdom.go 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. package vdom
  2. import "kumachan/standalone/ctn"
  3. type Node struct {
  4. Tag string
  5. Props
  6. Content
  7. }
  8. type Props struct {
  9. Attrs *Attrs
  10. Styles *Styles
  11. Events *Events
  12. }
  13. type Attrs struct { Data ctn.Map[string,string] }
  14. type Styles struct { Data ctn.Map[string,string] }
  15. type Events struct { Data ctn.Map[string,*EventHandler] }
  16. type EventHandler struct { Handler interface{} }
  17. type Content interface { impl(Content) }
  18. func(*Text) impl(Content) {}
  19. func(*Children) impl(Content) {
  20. }
  21. type Text string
  22. type Children ([] *Node)
  23. type DeltaNotifier struct {
  24. ApplyStyle func(id string, key string, value string)
  25. EraseStyle func(id string, key string)
  26. SetAttr func(id string, key string, value string)
  27. RemoveAttr func(id string, key string)
  28. AttachEvent func(id string, name string, handler *EventHandler)
  29. ModifyEvent func(id string, name string)
  30. DetachEvent func(id string, name string, handler *EventHandler)
  31. SetText func(id string, content string)
  32. AppendNode func(parent string, id string, tag string)
  33. RemoveNode func(parent string, id string)
  34. UpdateNode func(old_id string, new_id string)
  35. ReplaceNode func(parent string, old_id string, new_id string, tag string)
  36. SwapNode func(parent string, a string, b string)
  37. MoveNode func(parent string, id string, pivot string)
  38. }
  39. func Diff(ctx *DeltaNotifier, parent *Node, old *Node, new *Node) {
  40. assert(ctx != nil)
  41. assert(old != nil || new != nil)
  42. if old == new { return }
  43. var parent_id = get_addr(parent)
  44. var old_id = get_addr(old)
  45. var new_id = get_addr(new)
  46. if old == nil {
  47. ctx.AppendNode(parent_id, new_id, new.Tag)
  48. } else if new == nil {
  49. detach_all_events(ctx, old)
  50. ctx.RemoveNode(parent_id, old_id)
  51. } else {
  52. if (old.Tag == new.Tag) {
  53. ctx.UpdateNode(old_id, new_id)
  54. } else {
  55. ctx.ReplaceNode(parent_id, old_id, new_id, new.Tag)
  56. old = nil
  57. }
  58. }
  59. if new != nil {
  60. var id = new_id
  61. var node = new
  62. var new_styles = new.Styles
  63. if old != nil {
  64. var old_styles = old.Styles
  65. if new_styles == old_styles {
  66. goto skip_styles
  67. }
  68. old_styles.Data.ForEach(func(key string, _ string) {
  69. if !(new_styles.Data.Has(key)) {
  70. ctx.EraseStyle(id, key)
  71. }
  72. })
  73. }
  74. new_styles.Data.ForEach(func(key string, val string) {
  75. if old != nil {
  76. var old_val, exists = old.Styles.Data.Lookup(key)
  77. if exists {
  78. if exists {
  79. if old_val == val {
  80. goto skip_this_style
  81. }
  82. }
  83. }
  84. }
  85. ctx.ApplyStyle(id, key, val)
  86. skip_this_style:
  87. })
  88. skip_styles:
  89. var new_attrs = new.Attrs
  90. if old != nil {
  91. var old_attrs = old.Attrs
  92. if new_attrs == old_attrs {
  93. goto skip_attrs
  94. }
  95. old_attrs.Data.ForEach(func(name string, _ string) {
  96. if !(new_attrs.Data.Has(name)) {
  97. ctx.RemoveAttr(id, name)
  98. }
  99. })
  100. }
  101. new_attrs.Data.ForEach(func(name string, new_val string) {
  102. if old != nil {
  103. var old_val, exists = old.Attrs.Data.Lookup(name)
  104. if exists {
  105. if old_val == new_val {
  106. goto skip_this_attr
  107. }
  108. }
  109. }
  110. ctx.SetAttr(id, name, new_val)
  111. skip_this_attr:
  112. })
  113. skip_attrs:
  114. var new_events = new.Events
  115. if old != nil {
  116. var old_events = old.Events
  117. if old_events == new_events {
  118. goto skip_events
  119. }
  120. old_events.Data.ForEach(func(name string, old_handler *EventHandler) {
  121. var new_handler, name_in_new = new_events.Data.Lookup(name)
  122. if name_in_new {
  123. if new_handler == old_handler {
  124. goto skip_this_event
  125. } else {
  126. ctx.DetachEvent(id, name, old_handler)
  127. ctx.AttachEvent(id, name, new_handler)
  128. }
  129. } else {
  130. ctx.DetachEvent(id, name, old_handler)
  131. }
  132. skip_this_event:
  133. })
  134. }
  135. new_events.Data.ForEach(func(new_name string, handler *EventHandler) {
  136. if !(old != nil && old.Events.Data.Has(new_name)) {
  137. var new_handler = handler
  138. ctx.AttachEvent(id, new_name, new_handler)
  139. }
  140. })
  141. skip_events:
  142. if old != nil && old.Content == new.Content {
  143. goto skip_content
  144. }
  145. switch new_content := new.Content.(type) {
  146. case *Text:
  147. if old != nil {
  148. var old_content, is_text = old.Content.(*Text)
  149. if is_text && (*old_content == *new_content) {
  150. goto skip_text
  151. }
  152. }
  153. ctx.SetText(id, string(*new_content))
  154. skip_text:;
  155. case *Children:
  156. var new_children = *new_content
  157. var diff_children = func(old_children Children, new_children Children) {
  158. var build_index = func(nodes Children) (map[*Node] int, func(*Node) bool) {
  159. var index = make(map[*Node] int)
  160. for i, node := range nodes {
  161. index[node] = i
  162. }
  163. var has = func(node *Node) bool {
  164. var _, exists = index[node]
  165. return exists
  166. }
  167. return index, has
  168. }
  169. var old_index, old_has = build_index(old_children)
  170. var _, new_has = build_index(new_children)
  171. var old_skip = make(map[int] bool)
  172. var i = 0
  173. var j = 0
  174. for (i < len(old_children)) || (j < len(new_children)) {
  175. if old_skip[i] {
  176. i += 1
  177. continue
  178. }
  179. var old_child *Node = nil
  180. var new_child *Node = nil
  181. if i < len(old_children) {
  182. old_child = old_children[i]
  183. }
  184. if j < len(new_children) {
  185. new_child = new_children[j]
  186. }
  187. if new_child != old_child && old_has(new_child) {
  188. var node_id = get_addr(node)
  189. var old_child_id = get_addr(old_child)
  190. var new_child_id = get_addr(new_child)
  191. old_skip[old_index[new_child]] = true
  192. ctx.MoveNode(node_id, new_child_id, old_child_id)
  193. if !(new_has(old_child)) {
  194. Diff(ctx, node, old_child, nil) // remove
  195. i += 1
  196. }
  197. j += 1
  198. continue
  199. } else if new_child != old_child && new_has(old_child) {
  200. var node_id = get_addr(node)
  201. var old_child_id = get_addr(old_child)
  202. var new_child_id = get_addr(new_child)
  203. Diff(ctx, node, nil, new_child) // append
  204. ctx.MoveNode(node_id, new_child_id, old_child_id)
  205. j += 1
  206. continue
  207. } else {
  208. Diff(ctx, node, old_child, new_child)
  209. }
  210. i += 1
  211. j += 1
  212. }
  213. }
  214. if old != nil {
  215. switch old_content := old.Content.(type) {
  216. case *Text:
  217. ctx.SetText(id, string(""))
  218. diff_children(Children([] *Node {}), new_children)
  219. case *Children:
  220. var old_children = *old_content
  221. diff_children(old_children, new_children)
  222. }
  223. } else {
  224. diff_children(Children([] *Node {}), new_children)
  225. }
  226. default:
  227. panic("impossible branch")
  228. }
  229. skip_content:
  230. }
  231. }
  232. func detach_all_events(ctx *DeltaNotifier, node *Node) {
  233. var node_id = get_addr(node)
  234. node.Events.Data.ForEach(func(name string, handler *EventHandler) {
  235. ctx.DetachEvent(node_id, name, handler)
  236. })
  237. var children, has_children = node.Content.(*Children)
  238. if has_children {
  239. for _, child := range *children {
  240. detach_all_events(ctx, child)
  241. }
  242. }
  243. }
  244. var emptyAttrs = &Attrs { ctn.MakeMap[string,string](ctn.StringCompare) }
  245. var emptyStyles = &Styles { ctn.MakeMap[string,string](ctn.StringCompare) }
  246. var emptyEvents = &Events { ctn.MakeMap[string,*EventHandler](ctn.StringCompare) }
  247. var emptyContent = &Children {}
  248. func EmptyAttrs() *Attrs { return emptyAttrs }
  249. func EmptyStyles() *Styles { return emptyStyles }
  250. func EmptyEvents() *Events { return emptyEvents }
  251. func EmptyContent() Content { return emptyContent }
  252. func (attrs *Attrs) MergedWith(another *Attrs) *Attrs {
  253. const class = "class"
  254. var draft = attrs.Data
  255. another.Data.ForEach(func(key string, another_value string) {
  256. var value = (func() string {
  257. if key == class {
  258. var this_value, exists = draft.Lookup(class)
  259. if exists {
  260. var merged_value = (this_value + " " + another_value)
  261. return merged_value
  262. } else {
  263. return another_value
  264. }
  265. } else {
  266. return another_value
  267. }
  268. })()
  269. draft = draft.Inserted(key, value)
  270. })
  271. return &Attrs { draft }
  272. }
  273. func (styles *Styles) MergedWith(another *Styles) *Styles {
  274. return &Styles { styles.Data.MergedWith(another.Data) }
  275. }
  276. func (events *Events) MergedWith(another *Events) *Events {
  277. return &Events { events.Data.MergedWith(another.Data) }
  278. }