Overlay Example Demonstrates the Overlay widget for layering widgets with depth.
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" HEADLINES = [ "Scientists Discover New Species of Deep-Sea Octopus Near Hawaii", "Global Climate Summit Reaches Historic Agreement on Emissions", "Tech Giant Announces Breakthrough in Quantum Computing Research", "Local Community Garden Initiative Expands to Ten More Cities", "Astronomers Detect Unusual Radio Signals from Distant Galaxy", "New Study Links Mediterranean Diet to Improved Heart Health", "Electric Vehicle Sales Surge as Battery Technology Improves", "Ancient Manuscripts Reveal Previously Unknown Trading Routes", "Renewable Energy Now Powers 40% of National Grid", "Robotics Team Develops AI System for Disaster Response", "Archaeological Dig Uncovers Evidence of Early Human Settlement", "Major Airline Commits to Carbon-Neutral Flights by 2035", "Breakthrough Treatment Shows Promise for Rare Genetic Disease", "City Council Approves Expanded Public Transportation Network", "Marine Biologists Track Migration Patterns of Endangered Whales", "New App Helps Farmers Optimize Water Usage During Drought", "International Space Station Extends Mission Timeline to 2030", "Local Schools Implement Innovative STEM Education Program", "Wildlife Conservation Efforts Lead to Species Population Recovery", "Research Team Creates Biodegradable Alternative to Plastic Packaging", "Historic Theater Restoration Project Nears Completion", "Cybersecurity Experts Warn of Emerging Online Threats", "Community Food Bank Serves Record Number of Families This Year", "Innovative Urban Planning Reduces Traffic Congestion by 30%", ].freeze # Overlay Example # Demonstrates the Overlay widget for layering widgets with depth. class WidgetOverlay def initialize @layer_count = 2 # Start with 2 layers visible @swapped = false @clear = true end def run RatatuiRuby.run do |tui| @tui = tui loop do tui.draw do |frame| render(frame) end break if handle_input == :quit sleep 0.05 end end end private def render(frame) area = frame.area # Split into main area and control panel layout = @tui.layout_split( area, direction: :vertical, constraints: [ @tui.constraint_fill(1), @tui.constraint_length(5), ] ) main_area = layout[0] control_area = layout[1] # Render background layer - RSS reader frame.render_widget(background_layer, main_area) # Render upper layers based on layer_count and swap state if @swapped render_beta_layer(frame, main_area) if @layer_count >= 2 render_notification_layer(frame, main_area) if @layer_count >= 1 else render_notification_layer(frame, main_area) if @layer_count >= 1 render_beta_layer(frame, main_area) if @layer_count >= 2 end # Render control panel frame.render_widget(control_panel, control_area) end def background_layer @background_layer ||= @tui.list( items: HEADLINES, block: @tui.block( title: "RSS Reader", borders: [:all] ) ) end def render_notification_layer(frame, area) # Position modal: 20% from top, 60% height, 15% from left, 70% width vertical_sections = @tui.layout_split( area, direction: :vertical, constraints: [ @tui.constraint_fill(2), @tui.constraint_fill(5), @tui.constraint_fill(3), ] ) horizontal_sections = @tui.layout_split( vertical_sections[1], direction: :horizontal, constraints: [ @tui.constraint_fill(1), @tui.constraint_fill(5), @tui.constraint_fill(1), ] ) modal_rect = horizontal_sections[1] frame.render_widget(@tui.clear, modal_rect) if @clear # Render the modal content frame.render_widget( @tui.paragraph( text: "Your feeds have been updated", wrap: true, alignment: :center, block: @tui.block( title: "Notification", borders: [:all], border_style: @tui.style(fg: :black), style: @tui.style(bg: :red, fg: :black) ) ), modal_rect ) end def render_beta_layer(frame, area) # Position modal: 30% from top, 40% height, 25% from left, 50% width vertical_sections = @tui.layout_split( area, direction: :vertical, constraints: [ @tui.constraint_fill(3), @tui.constraint_fill(4), @tui.constraint_fill(2), ] ) horizontal_sections = @tui.layout_split( vertical_sections[1], direction: :horizontal, constraints: [ @tui.constraint_fill(2), @tui.constraint_fill(3), @tui.constraint_fill(2), ] ) modal_rect = horizontal_sections[1] frame.render_widget(@tui.clear, modal_rect) if @clear # Render the modal content frame.render_widget( beta_paragraph, modal_rect ) end def beta_paragraph @beta_paragraph ||= @tui.paragraph( text: "Thank you for being a beta tester. To give feedback, shout very loudly and we will hear you. Be careful not to scare the llamas.", wrap: true, alignment: :left, block: @tui.block( title: "Beta Program", borders: [:all], border_style: @tui.style(fg: :black), style: @tui.style(bg: :blue, fg: :black) ) ) end def control_panel bold_underline = @tui.style(modifiers: [:bold, :underlined]) first_controls = [ @tui.text_span(content: "0", style: bold_underline), @tui.text_span(content: "/"), @tui.text_span(content: "1", style: bold_underline), @tui.text_span(content: "/"), @tui.text_span(content: "2", style: bold_underline), @tui.text_span(content: ": Change number of overlays | "), @tui.text_span(content: "space", style: bold_underline), @tui.text_span(content: ": Swap overlay order"), ] second_controls = [ @tui.text_span(content: "c", style: bold_underline), @tui.text_span(content: ": Toggle clear (currently #{@clear ? 'on' : 'off'})"), ] third_controls = [ @tui.text_span(content: "q", style: bold_underline), @tui.text_span(content: ": Quit"), ] first = @tui.text_line(spans: first_controls) second = @tui.text_line(spans: second_controls) third = @tui.text_line(spans: third_controls) @tui.paragraph( text: [first, second, third], alignment: :center, block: @tui.block( title: "Controls", borders: [:all] ) ) end def handle_input case @tui.poll_event in { type: :key, code: "q" } | { type: :key, code: "c", modifiers: ["ctrl"] } :quit in { type: :key, code: "0" } @layer_count = 0 in { type: :key, code: "1" } @layer_count = 1 in { type: :key, code: "2" } @layer_count = 2 in { type: :key, code: " " } @swapped = !@swapped in { type: :key, code: "c" } @clear = !@clear else nil end end end WidgetOverlay.new.run if __FILE__ == $PROGRAM_NAME