lib/scrollable/container.controller.ex

defmodule FloUI.Scrollable.ScrollableContainerController do
  import Scenic.Primitives, only: [group: 3]
  import FloUI.Scrollable.ScrollBar, only: [scroll_bar: 3]
  alias Scenic.Graph
  alias Scenic.Primitive
  alias Scenic.Math.Vector2

  def on_scroll_position_change(
    %{
      assigns: %{
        scroll_position: scroll_position,
        scroll_bars_state: %{
          vertical: %{
            scrolling: vert_scrolling
          },
          horizontal: %{
            scrolling: horiz_scrolling
          }
        }
      }
    } = scene
  ) when vert_scrolling == :dragging or horiz_scrolling == :dragging do
    graph =
      scene.assigns.graph
      |> Graph.modify(
        :content,
        &Primitive.put_transform(&1, :translate, scroll_position)
      )

    Scenic.Scene.assign(scene, graph: graph)
  end

  def on_scroll_position_change(%{assigns: %{content: %{x: x, y: y}}} = scene) do
    IO.puts "update container position"
    graph =
      scene.assigns.graph
      |> Graph.modify(
        :content,
        &Primitive.put_transform(&1, :translate, Vector2.add(scene.assigns.scroll_position, {x, y}))
      )
      |> Graph.modify(
        :vertical_scroll_bar,
        &scroll_bar(
          &1,
          %{
            scroll_position: Vector2.sub(scene.assigns.scroll_position, {x, y})
          },
          []
        )
      )
      |> Graph.modify(
        :horizontal_scroll_bar,
        &scroll_bar(
          &1,
          %{
            scroll_position: Vector2.sub(scene.assigns.scroll_position, {x, y})
          },
          []
        )
      )

    Scenic.Scene.assign(scene, graph: graph)
  end

  def on_children_change(scene) do
    graph =
      scene.assigns.graph
      |> Graph.delete(:content_container)

    scene
    |> Scenic.Scene.assign(graph: graph)
    |> render_content
  end

  def render_content(
        %{
          assigns: %{
            children: children,
            graph: graph,
            frame: %{x: x, y: y, width: width, height: height}
          }
        } = scene
      ) do
    graph =
      graph
      |> group(
        fn g ->
          group(g, &render_children(&1, children), id: :content)
        end,
        scissor: {width, height},
        translate: {x, y},
        id: :content_container
      )

    Scenic.Scene.assign(scene, graph: graph)
  end

  def render_children(g, children) do
    Enum.reduce(children, g, fn child, g ->
      g |> child[:module].add_to_graph(child[:data], child[:opts])
    end)
  end
end