WidgetTextWidth

Source Code

# frozen_string_literal: true

#--
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
# SPDX-License-Identifier: MIT-0
#++

$LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
require "ratatui_ruby"

class WidgetTextWidth
  def initialize
    @text_samples = [
      { label: "ASCII", text: "Hello, World!", desc: "Simple English text" },
      { label: "CJK", text: "δ½ ε₯½δΈ–η•Œ", desc: "Chinese (full-width characters)" },
      { label: "Emoji", text: "Hello πŸ‘ World 🌍", desc: "Mixed text with emoji (2 cells each)" },
      { label: "Mixed", text: "Hi δ½ ε₯½ πŸ‘", desc: "ASCII + CJK + emoji" },
      { label: "Empty", text: "", desc: "Empty string" },
    ]
    @selected_index = 0
  end

  def run
    RatatuiRuby.run do |tui|
      @tui = tui
      loop do
        render
        break if handle_input == :quit
      end
    end
  end

  private def render
    @tui.draw do |frame|
      # Layout: main content above, controls below
      areas = @tui.layout_split(
        frame.area,
        direction: :vertical,
        constraints: [@tui.constraint_fill(1), @tui.constraint_length(7)]
      )

      # Main content area with sample text
      render_content(frame, areas[0])

      # Controls footer
      render_controls(frame, areas[1])
    end
  end

  private def render_content(frame, area)
    sample = @text_samples[@selected_index]
    measured_width = @tui.text_width(sample[:text])

    # v0.7.0: Text::Span#width and Text::Line#width instance methods for rich text measurement
    styled_span = @tui.text_span(content: sample[:text], style: @tui.style(fg: :cyan))
    span_width = styled_span.width

    styled_line = @tui.text_line(spans: [styled_span])
    line_width = styled_line.width

    # Build content text with newlines
    content = []
    content << "Sample: #{sample[:text]}"
    content << ""
    content << "Display Width (text_width): #{measured_width} cells"
    content << "Display Width (span.width): #{span_width} cells"
    content << "Display Width (line.width): #{line_width} cells"
    content << "Character Count: #{sample[:text].length}"
    content << ""
    content << sample[:desc]
    text = content.join("\n")

    widget = @tui.paragraph(
      text:,
      block: @tui.block(
        title: "Text Width Calculator",
        borders: [:all],
        border_style: { fg: "cyan" }
      ),
      alignment: :left
    )

    frame.render_widget(widget, area)
  end

  private def render_controls(frame, area)
    info = "Sample #{@selected_index + 1}/#{@text_samples.length}: #{@text_samples[@selected_index][:label]}"
    controls = "↑/↓ Select   q Quit"
    text = "#{info}\n#{controls}"

    widget = @tui.paragraph(
      text:,
      block: @tui.block(borders: [:top], border_style: { fg: "gray" }),
      alignment: :center
    )

    frame.render_widget(widget, area)
  end

  private def handle_input
    event = @tui.poll_event
    case event
    in { type: :key, code: "q" }
      :quit
    in { type: :key, code: "up" }
      @selected_index = (@selected_index - 1) % @text_samples.length
      nil
    in { type: :key, code: "down" }
      @selected_index = (@selected_index + 1) % @text_samples.length
      nil
    else
      nil
    end
  end
end

WidgetTextWidth.new.run if __FILE__ == $PROGRAM_NAME