# Layout

#### Example: Defining Custom Layout

    xplr.config.layouts.builtin.default = {
      Horizontal = {
        config = {
          margin = 1,
          horizontal_margin = 1,
          vertical_margin = 1,
          constraints = {
            { Percentage = 50 },
            { Percentage = 50 },
          }
        },
        splits = {
          "Table",
          "HelpMenu",
        }
      }
    }

Result:

     ╭ /home ─────────────╮╭ Help [default] ────╮
     │   ╭─── path        ││.    show hidden    │
     │   ├▸[ð Desktop/]   ││/    search         │
     │   ├  ð Documents/  ││:    action         │
     │   ├  ð Downloads/  ││?    global help    │
     │   ├  ð GitHub/     ││G    go to bottom   │
     │   ├  ð Music/      ││V    select/unselect│
     │   ├  ð Pictures/   ││ctrl duplicate as   │
     │   ├  ð Public/     ││ctrl next visit     │
     ╰────────────────────╯╰────────────────────╯

A layout is a sum type can be one of the following:

- Nothing
- Table
- InputAndLogs
- Selection
- HelpMenu
- SortAndFilter
- Static
- Dynamic
- Horizontal
- Vertical
- CustomContent (deprecated, use `Static` or `Dynamic`)

### Nothing

This layout contains a blank panel.

Type: "Nothing"

### Table

This layout contains the table displaying the files and directories in the current directory.

### InputAndLogs

This layout contains the panel displaying the input prompt and logs.

Type: "InputAndLogs"

### Selection

This layout contains the panel displaying the selected paths.

Type: "Selection"

### HelpMenu

This layout contains the panel displaying the help menu for the current mode in real-time.

Type: "HelpMenu"

### SortAndFilter

This layout contains the panel displaying the pipeline of sorters and filters applied on the list of paths being displayed.

Type: "SortAndFilter"

### Static

This is a custom layout to render static content.

Type: { Static = Custom Panel }

### Dynamic

This is a custom layout to render dynamic content using a function defined in xplr.fn that takes Content Renderer Argument and returns Custom Panel.

Type: { Dynamic = "Content Renderer" }

### Horizontal

This is a special layout that splits the panel into two horizontal parts.

It contains the following information:

- config
- splits

Type: { Vertical = { config = Layout Config, splits = { Layout, ... } }

### Vertical

This is a special layout that splits the panel into two vertical parts.

It contains the following information:

- config
- splits

Type: { Vertical = { config = Layout Config, splits = { Layout, ... } }

## Layout Config

A layout config contains the following information:

- margin
- horizontal_margin
- vertical_margin
- constraints

### margin

Type: nullable integer

The width of the margin in all direction.

### horizontal_Margin

Type: nullable integer

The width of the horizontal margins. Overwrites the margin value.

### vertical_Margin

Type: nullable integer

The width of the vertical margins. Overwrites the margin value.

### constraints

Type: nullable list of Constraint

The constraints applied on the layout.

## Constraint

A constraint is a sum type can be one of the following:

- { Percentage = int }
- { Ratio = { int, int } }
- { Length = { int }
- { LengthLessThanScreenHeight = int }
- { LengthLessThanScreenWidth = int }
- { LengthLessThanLayoutHeight = int }
- { LengthLessThanLayoutWidth = int }
- { Max = int }
- { MaxLessThanScreenHeight = int }
- { MaxLessThanScreenWidth = int }
- { MaxLessThanLayoutHeight = int }
- { MaxLessThanLayoutWidth = int }
- { Min = int }
- { MinLessThanScreenHeight = int }
- { MinLessThanScreenWidth = int }
- { MinLessThanLayoutHeight = int }
- { MinLessThanLayoutWidth = int }

## splits

Type: list of Layout

The list of child layouts to fit into the parent layout.

## Custom Panel

Custom panel is a sum type can be one of the following:

- CustomParagraph
- CustomList
- CustomTable
- CustomLayout

### CustomParagraph

A paragraph to render. It contains the following fields:

- ui (nullable Panel UI Config): Optional UI config for the panel.
- body (string): The string to render.

#### Example: Render a custom static paragraph

    xplr.config.layouts.builtin.default = {
      Static = {
        CustomParagraph = {
          ui = { title = { format = " custom title " } },
          body = "custom body",
        },
      },
    }

Result:

    ╭ custom title ────────╮
    │custom body           │
    │                      │
    │                      │
    ╰──────────────────────╯

#### Example: Render a custom dynamic paragraph

    xplr.config.layouts.builtin.default = { Dynamic = "custom.render_layout" }

    xplr.fn.custom.render_layout = function(ctx)
      return {
        CustomParagraph = {
          ui = { title = { format = ctx.app.pwd } },
          body = xplr.util.to_yaml(ctx.app.focused_node),
        },
      }
    end

Result:

    ╭/home/sayanarijit───────────────────────────╮
    │mime_essence: inode/directory               │
    │relative_path: Desktop                      │
    │is_symlink: false                           │
    │is_readonly: false                          │
    │parent: /home/sayanarijit                   │
    │absolute_path: /home/sayanarijit/Desktop    │
    │is_broken: false                            │
    │created: 1668087850396758714                │
    │size: 4096                                  │
    │gid: 100                                    │
    ╰────────────────────────────────────────────╯

### CustomList

A list to render. It contains the following fields:

- ui (nullable Panel UI Config): Optional UI config for the panel.
- body (list of string): The list of strings to display.

#### Example: Render a custom static list

    xplr.config.layouts.builtin.default = {
      Static = {
        CustomList = {
          ui = { title = { format = " custom title " } },
          body = { "1", "2", "3" },
        },
      },
    }

Result:

    ╭ custom title ─────────────╮
    │1                          │
    │2                          │
    │3                          │
    │                           │
    ╰───────────────────────────╯

#### Example: Render a custom dynamic list

    xplr.config.layouts.builtin.default = { Dynamic = "custom.render_layout" }

    xplr.fn.custom.render_layout = function(ctx)
      return {
        CustomList = {
          ui = { title = { format = ctx.app.pwd } },
          body = {
            (ctx.app.focused_node or {}).relative_path or "",
            ctx.app.version,
            tostring(ctx.app.pid),
          },
        },
      }
    end

Result:

    ╭/home/sayanarijit──────────╮
    │Desktop                    │
    │0.21.2                     │
    │17336                      │
    │                           │
    │                           │
    ╰───────────────────────────╯

## CustomTable

A custom table to render. It contains the following fields:

- ui (nullable Panel UI Config): Optional UI config for the panel.
- widths (list of Constraint): Width of the columns.
- col_spacing (nullable int): Spacing between columns. Defaults to 1.
- body (list of list of string): The rows and columns to render.

#### Example: Render a custom static table

    xplr.config.layouts.builtin.default = {
      Static = {
        CustomTable = {
          ui = { title = { format = " custom title " } },
          widths = {
            { Percentage = 50 },
            { Percentage = 50 },
          },
          body = {
            { "a", "b" },
            { "c", "d" },
          },
        },
      },
    }

Result:

    ╭ custom title ────────────────────╮
    │a                 b               │
    │c                 d               │
    │                                  │
    │                                  │
    │                                  │
    ╰──────────────────────────────────╯

#### Example: Render a custom dynamic table

    xplr.config.layouts.builtin.default = {Dynamic = "custom.render_layout" }

    xplr.fn.custom.render_layout = function(ctx)
      return {
        CustomTable = {
          ui = { title = { format = ctx.app.pwd } },
          widths = {
            { Percentage = 50 },
            { Percentage = 50 },
          },
          body = {
            { "", "" },
            { "Layout height", tostring(ctx.layout_size.height) },
            { "Layout width", tostring(ctx.layout_size.width) },
            { "", "" },
            { "Screen height", tostring(ctx.screen_size.height) },
            { "Screen width", tostring(ctx.screen_size.width) },
          },
        },
      }
    end

Result:

    ╭/home/sayanarijit───────────────────────────╮
    │                                            │
    │Layout height          12                   │
    │Layout width           46                   │
    │                                            │
    │Screen height          12                   │
    │Screen width           46                   │
    │                                            │
    │                                            │
    │                                            │
    │                                            │
    ╰────────────────────────────────────────────╯

### CustomLayout

A whole custom layout to render. It doesn't make sense to use it as a Static layout, but can be very useful to render as a Dynamic layout for use cases where the structure of the layout needs to change without having to switch modes.

> WARNING: Rendering the same dynamic custom layout recursively will result in a ugly crash.

#### Example: Render a custom dynamic layout

    xplr.config.layouts.builtin.default = { Dynamic = "custom.render_layout" }

    xplr.fn.custom.render_layout = function(ctx)
      local inner = {
        config = {
          constraints = {
            { Percentage = 50 },
            { Percentage = 50 },
          },
        },
        splits = {
          { Static = { CustomParagraph = { body = "Try your luck..." } } },
          { Static = { CustomParagraph = { body = "Press ctrl-r" } } },
        },
      }

      local layout_type = "Vertical"
      if math.random(1, 2) == 1 then
        layout_type = "Horizontal"
      end

      return { CustomLayout = { [layout_type] = inner } }
    end

Result:

    ╭─────────────────────╮╭─────────────────────╮
    │Try your luck...     ││Press ctrl-r         │
    │                     ││                     │
    │                     ││                     │
    │                     ││                     │
    │                     ││                     │
    │                     ││                     │
    │                     ││                     │
    │                     ││                     │
    │                     ││                     │
    │                     ││                     │
    ╰─────────────────────╯╰─────────────────────╯

Or

    ╭────────────────────────────────────────────╮
    │Try your luck...                            │
    │                                            │
    │                                            │
    │                                            │
    ╰────────────────────────────────────────────╯
    ╭────────────────────────────────────────────╮
    │Press ctrl-r                                │
    │                                            │
    │                                            │
    │                                            │
    ╰────────────────────────────────────────────╯

## Panel UI Config

It contains the following optional fields:

- title ({ format = "string", style = Style }): the title of the panel.
- style (Style): The style of the panel body.
- borders (nullable list of Border): The shape of the borders.
- border_type (Border Type): The type of the borders.
- border_style (Style): The style of the borders.

## Content Renderer

It is a Lua function that receives a special argument as input and returns some output that can be rendered in the UI. It is used to render content body for the custom dynamic layouts.

## Content Renderer Argument

It contains the following information:

- layout_size
- screen_size
- app

### Size

It contains the following information:

- x
- y
- height
- width

Every field is of integer type.

### app

This is a lightweight version of the Lua Context. In this context, the heavyweight fields like directory_buffer are omitted for performance reasons.

Hence, only the following fields are available.

- version
- pwd
- initial_pwd
- vroot
- focused_node
- selection
- mode
- layout
- input_buffer
- pid
- session_path
- explorer_config

## Also See:

- xplr.config.layouts