component.ml 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. open Base
  2. module Path = Draw_tree.Path
  3. module Update = Draw_tree.Update
  4. module Sink_id : sig
  5. include Identifiable.S
  6. val gen : unit -> t
  7. val anticipate : int -> t
  8. end = struct
  9. include Int
  10. let to_string n = Printf.sprintf "<sink:%d>" n
  11. let next = ref 0
  12. let gen () =
  13. let v = !next in
  14. Int.incr next ; v
  15. let anticipate n = n + !next
  16. end
  17. (*** components ***)
  18. type component =
  19. | Tree of component list * (Draw_tree.t list -> Draw_tree.t)
  20. | Dynamic of component Behavior.t
  21. | With_fold of Source.value
  22. * (Event.t -> Source.value -> Source.value)
  23. * (Source.t -> component)
  24. (* sinks and connections *)
  25. type sink =
  26. { behavior : component Behavior.t
  27. ; sub_sinks : Sink_id.t list
  28. }
  29. type sinks = (Sink_id.t, Path.t * sink, Sink_id.comparator_witness) Map.t
  30. type connections = (Source.t, Sink_id.t list, Source.comparator_witness) Map.t
  31. type handlers = Event.handler Event.map
  32. type state =
  33. { sources : Source.State.t
  34. ; sinks : sinks
  35. ; conns : connections
  36. ; handlers : handlers
  37. }
  38. let empty_sinks = Map.empty (module Sink_id)
  39. let empty_conns = Source.empty_map
  40. let merge_sinks s1 s2 = Map.merge_skewed s1 s2 ~combine:(fun ~key:_ _ x -> x)
  41. let merge_conns c1 c2 = Map.merge_skewed c1 c2 ~combine:(fun ~key:_ -> (@))
  42. let empty_state sources =
  43. { sources
  44. ; sinks = empty_sinks
  45. ; conns = empty_conns
  46. ; handlers = Event.empty_map }
  47. let connect_behavior bhv sink_id conns =
  48. Sequence.fold (Behavior.dependencies bhv)
  49. ~init:conns
  50. ~f:(fun con src -> Map.add_multi con ~key:src ~data:sink_id)
  51. let remove_sink_by_id st sink_id =
  52. let not_this_sink id = not (Sink_id.equal id sink_id) in
  53. { st
  54. with sinks = Map.remove st.sinks sink_id
  55. ; conns = Map.map st.conns ~f:(List.filter ~f:not_this_sink) }
  56. let rec recursive_subsink_ids sink st =
  57. List.concat_map sink.sub_sinks
  58. ~f:(fun sink_id ->
  59. match Map.find st.sinks sink_id with
  60. | None -> []
  61. | Some((_, sub_sink)) ->
  62. sink_id::(recursive_subsink_ids sub_sink st))
  63. (* mounting and other important logic *)
  64. let mount component sources : Source.State.t * sinks * connections * Draw_tree.t =
  65. let sources = ref sources in
  66. let sinks = ref empty_sinks in
  67. let conns = ref empty_conns in
  68. let rec inst = function
  69. | Tree(coms, make_tree) ->
  70. let new_sinks, subtrees =
  71. List.fold_map coms ~init:[]
  72. ~f:(fun new_sinks com ->
  73. let (new_sinks', tree) = inst com in
  74. (new_sinks' @ new_sinks, tree))
  75. in
  76. (new_sinks, make_tree subtrees)
  77. | Dynamic(b) ->
  78. let sink_id = Sink_id.gen () in
  79. (* create the tree that will be initially visible *)
  80. let init_com = Behavior.sample b !sources in
  81. let sub_sinks, init_tree = inst init_com in
  82. (* create a new sink *)
  83. let sink = { behavior = b ; sub_sinks } in
  84. let path = Draw_tree.path init_tree in
  85. (* associate the behavior to the sink *)
  86. let () = sinks := Map.add_exn !sinks ~key:sink_id ~data:(path, sink) in
  87. let () = conns := connect_behavior b sink_id !conns in
  88. ([sink_id], init_tree)
  89. | With_fold(init, f, make_component) ->
  90. (* create source with initial value *)
  91. let src = Source.create () in
  92. let () = sources := Source.State.set src init !sources in
  93. (* create child component *)
  94. let com = make_component src in
  95. let new_sinks, tree = inst com in
  96. ignore (f) [@ocaml.warning "-5"];
  97. (new_sinks, tree)
  98. in
  99. let _, tree = inst component in
  100. !sources, !sinks, !conns, tree
  101. let mount component st : state * Draw_tree.t =
  102. let sources, sinks, conns, tree = mount component st.sources in
  103. ({ st with sources
  104. ; sinks = merge_sinks st.sinks sinks
  105. ; conns = merge_conns st.conns conns },
  106. tree)
  107. let update_sink sink st =
  108. (* remove old sinks *)
  109. let st = List.fold (recursive_subsink_ids sink st)
  110. ~init:st ~f:remove_sink_by_id in
  111. (* update component and re-mount *)
  112. let component' = Behavior.sample sink.behavior st.sources in
  113. let st, tree = mount component' st in
  114. (st, [ Update.Unmount
  115. ; Update.Mount(tree) ])
  116. let source_changed src value st : state * Draw_tree.updates =
  117. let st = { st with sources = Source.State.set src value st.sources } in
  118. (* find affected sinks *)
  119. let sink_ids = Map.find_multi st.conns src in
  120. let sinks = Sequence.(filter_map (of_list sink_ids) ~f:(Map.find st.sinks)) in
  121. (* apply updates *)
  122. let st, upds =
  123. Sequence.fold sinks ~init:(st, [])
  124. ~f:(fun (st, upds) (path, sink) ->
  125. let (st, new_upds) = update_sink sink st in
  126. let new_upds = List.map new_upds ~f:(fun u -> (path, u)) in
  127. (st, List.append new_upds upds))
  128. in
  129. (st, Sequence.of_list upds)
  130. let init sources component : state * Draw_tree.t =
  131. let st, tree = mount component (empty_state sources) in
  132. let () = Draw_tree.refresh_paths tree in
  133. (st, tree)
  134. (*** component combinators ***)
  135. let arm dir coms =
  136. Tree(coms, Draw_tree.arm dir)
  137. let figure b =
  138. let f fig = Tree([], fun _ -> Draw_tree.figure fig) in
  139. Dynamic(Behavior.map b ~f)
  140. let rect b =
  141. figure Behavior.(b >>| fun (dim, c) -> Draw_tree.Rect(dim, c))
  142. let cfigure fig =
  143. Tree([], fun _ -> Draw_tree.figure fig)
  144. let crect dim c =
  145. cfigure (Draw_tree.Rect(dim, c))
  146. let fold ~init ~f com =
  147. With_fold(init, f, fun src -> com (Behavior.of_source src))