Packages
Terminal emulation and driver infrastructure for Raxol. ANSI parsing, screen buffers, command processing, cursor management, input handling, session management, and termbox2 NIF integration.
Current section
Files
Jump to
Current section
Files
lib/raxol/terminal/buffer/cell.ex
defmodule Raxol.Terminal.Buffer.Cell do
@moduledoc """
Manages terminal cell operations and attributes.
"""
defstruct [
:char,
:foreground,
:background,
:attributes,
:hyperlink,
:width,
# Virtual field for backward compatibility
:fg,
# Virtual field for backward compatibility
:bg
]
@type t :: %__MODULE__{
char: String.t(),
foreground: integer() | atom() | String.t(),
background: integer() | atom() | String.t(),
attributes: map(),
hyperlink: String.t() | nil,
width: integer()
}
@doc """
Creates a new cell with default settings.
"""
def new(opts \\ [])
def new(opts) when is_list(opts) do
case opts do
[] -> %__MODULE__{}
_ -> new_from_keyword(opts)
end
end
def new(arg) when is_binary(arg) do
%__MODULE__{char: arg, fg: nil, bg: nil}
end
def new(opts) when is_map(opts) do
new_from_map(opts)
end
def new(_), do: %__MODULE__{}
@doc """
Creates a new cell with the specified character and style.
"""
def new(char, style) when is_binary(char) and is_map(style) do
# Log.info("DEBUG: Cell.new/2 - creating cell with char: #{inspect(char)}, style: #{inspect(style)}")
foreground = extract_foreground(style)
background = extract_background(style)
cell = %__MODULE__{
char: char,
foreground: foreground,
background: background,
attributes: extract_attributes(style),
hyperlink: extract_hyperlink(style),
width: extract_width(style),
# Virtual field for backward compatibility
fg: foreground,
# Virtual field for backward compatibility
bg: background
}
# Log.info("DEBUG: Cell.new/2 - created cell: #{inspect(cell)}")
cell
end
@doc """
Gets the cell's character.
"""
def get_char(%__MODULE__{} = cell) do
cell.char
end
@doc """
Sets the cell's character.
"""
def set_char(%__MODULE__{} = cell, char) when is_binary(char) do
%{cell | char: char}
end
@doc """
Gets the cell's foreground color.
"""
def get_foreground(%__MODULE__{} = cell) do
cell.foreground
end
@doc """
Sets the cell's foreground color.
"""
def set_foreground(%__MODULE__{} = cell, color) when is_integer(color) do
%{cell | foreground: color}
end
@doc """
Gets the cell's background color.
"""
def get_background(%__MODULE__{} = cell) do
cell.background
end
@doc """
Sets the cell's background color.
"""
def set_background(%__MODULE__{} = cell, color) when is_integer(color) do
%{cell | background: color}
end
@doc """
Gets the cell's attributes.
"""
def get_attributes(%__MODULE__{} = cell) do
cell.attributes
end
@doc """
Sets the cell's attributes.
"""
def set_attributes(%__MODULE__{} = cell, attributes)
when is_map(attributes) do
%{cell | attributes: attributes}
end
@doc """
Gets the cell's hyperlink.
"""
def get_hyperlink(%__MODULE__{} = cell) do
cell.hyperlink
end
@doc """
Sets the cell's hyperlink.
"""
def set_hyperlink(%__MODULE__{} = cell, hyperlink)
when is_binary(hyperlink) or is_nil(hyperlink) do
%{cell | hyperlink: hyperlink}
end
@doc """
Gets the cell's width.
"""
def get_width(%__MODULE__{} = cell) do
cell.width
end
@doc """
Sets the cell's width.
"""
def set_width(%__MODULE__{} = cell, width)
when is_integer(width) and width > 0 do
%{cell | width: width}
end
# Backward compatibility functions for old :fg and :bg field names
@doc """
Gets the cell's foreground color (backward compatibility).
"""
def fg(%__MODULE__{} = cell) do
cell.foreground
end
@doc """
Sets the cell's foreground color (backward compatibility).
"""
def fg(%__MODULE__{} = cell, color) do
%{cell | foreground: color}
end
@doc """
Gets the cell's background color (backward compatibility).
"""
def bg(%__MODULE__{} = cell) do
cell.background
end
@doc """
Sets the cell's background color (backward compatibility).
"""
def bg(%__MODULE__{} = cell, color) do
%{cell | background: color}
end
@doc """
Checks if the cell is empty.
"""
def empty?(%__MODULE__{} = cell) do
cell.char == " "
end
@doc """
Resets a cell to its default state.
"""
def reset(%__MODULE__{} = _cell) do
%__MODULE__{}
end
@doc """
Copies attributes from one cell to another.
"""
def copy_attributes(%__MODULE__{} = source, %__MODULE__{} = target) do
%{
target
| foreground: source.foreground,
background: source.background,
attributes: source.attributes,
hyperlink: source.hyperlink
}
end
@doc """
Validates a cell's data.
Returns true if the cell is valid, false otherwise.
"""
def valid?(%__MODULE__{} = cell) do
valid_char?(cell.char) and
valid_color?(cell.foreground) and
valid_color?(cell.background) and
valid_attributes?(cell.attributes)
end
def valid?(_), do: false
defp valid_char?(char) when is_binary(char) do
String.length(char) == 1
end
defp valid_char?(_), do: false
defp valid_color?(color) when is_atom(color) do
color in [
:default,
:black,
:red,
:green,
:yellow,
:blue,
:magenta,
:cyan,
:white
]
end
defp valid_color?(color) when is_binary(color) do
String.match?(color, ~r/^#[0-9A-Fa-f]{6}$/)
end
defp valid_color?(color) when is_integer(color) do
color >= 0 and color <= 255
end
defp valid_color?(_), do: false
defp valid_attributes?(attrs) when is_map(attrs) do
valid_attribute_keys = [
:bold,
:italic,
:underline,
:strikethrough,
:blink,
:reverse,
:faint,
:conceal,
:fraktur,
:double_underline,
:framed,
:encircled,
:overlined
]
Enum.all?(attrs, fn {key, value} ->
key in valid_attribute_keys and is_boolean(value)
end)
end
defp valid_attributes?(_), do: false
defp extract_foreground(style) do
case Map.get(style, :foreground) do
nil ->
# Default white
7
fg ->
fg
end
end
defp extract_background(style) do
case Map.get(style, :background) do
nil ->
# Default black
0
bg ->
bg
end
end
defp extract_attributes(style) do
case Map.get(style, :attributes) do
nil ->
# Extract attributes from TextFormatting struct fields
%{
bold: Map.get(style, :bold, false),
italic: Map.get(style, :italic, false),
underline: Map.get(style, :underline, false),
strikethrough: Map.get(style, :strikethrough, false),
blink: Map.get(style, :blink, false),
reverse: Map.get(style, :reverse, false),
faint: Map.get(style, :faint, false),
conceal: Map.get(style, :conceal, false),
fraktur: Map.get(style, :fraktur, false),
double_underline: Map.get(style, :double_underline, false),
framed: Map.get(style, :framed, false),
encircled: Map.get(style, :encircled, false),
overlined: Map.get(style, :overlined, false)
}
attrs ->
attrs
end
end
defp extract_hyperlink(style) do
case Map.get(style, :hyperlink) do
nil ->
case style do
%{hyperlink: link} -> link
_ -> nil
end
link ->
link
end
end
defp extract_width(style) do
case Map.get(style, :width) do
nil ->
case style do
%{width: w} when not is_nil(w) -> w
_ -> 1
end
w ->
w
end
end
defp new_from_map(opts) do
foreground = Map.get(opts, :foreground, 7)
background = Map.get(opts, :background, 0)
%__MODULE__{
char: Map.get(opts, :char, " "),
foreground: foreground,
background: background,
attributes: Map.get(opts, :attributes, %{}),
hyperlink: Map.get(opts, :hyperlink, nil),
width: Map.get(opts, :width, 1),
# Virtual field for backward compatibility
fg: foreground,
# Virtual field for backward compatibility
bg: background
}
end
defp new_from_keyword(opts) do
foreground = Keyword.get(opts, :foreground, 7)
background = Keyword.get(opts, :background, 0)
%__MODULE__{
char: Keyword.get(opts, :char, " "),
foreground: foreground,
background: background,
attributes: Keyword.get(opts, :attributes, %{}),
hyperlink: Keyword.get(opts, :hyperlink, nil),
width: Keyword.get(opts, :width, 1),
# Virtual field for backward compatibility
fg: foreground,
# Virtual field for backward compatibility
bg: background
}
end
end