Para desenvolvedores Ruby que desejam criar aplicações de linha de comando (CLI) mais interativas e visualmente organizadas, a gem curses é uma ferramenta poderosa. Ela fornece uma interface para a biblioteca Curses, permitindo o controle total sobre o terminal, desde o posicionamento do cursor e a manipulação de texto até o uso de cores e a criação de janelas.
Este artigo técnico serve como um tutorial introdutório à gem curses em Ruby. Abordaremos a instalação, os conceitos fundamentais e um guia passo a passo para construir uma aplicação de terminal simples.
O que é a Biblioteca Curses?
Originalmente desenvolvida para sistemas Unix, a biblioteca Curses oferece um conjunto de funções que permitem aos programadores escreverem aplicações baseadas em texto de forma independente do terminal. Em essência, ela abstrai as complexidades dos diferentes tipos de terminais, fornecendo uma API consistente para manipulação da tela.
A gem curses do Ruby encapsula essa funcionalidade, tornando-a acessível dentro do ecossistema Ruby. Com ela, é possível ir além do simples puts e gets e construir interfaces de usuário de texto (TUI - Text-based User Interface) sofisticadas.
Instalação
Antes de começarmos, é necessário garantir que a gem curses e suas dependências estejam instaladas. A instalação pode variar ligeiramente dependendo do sistema operacional.
Em sistemas baseados em Debian/Ubuntu:
É provável que você precise instalar a biblioteca de desenvolvimento ncurses:
sudo apt-get install libncurses5-dev libncursesw5-dev
Em sistemas baseados em Red Hat/Fedora:
sudo dnf install ncurses-devel
No macOS (usando Homebrew):
brew install ncurses
Com as dependências de sistema instaladas, você pode instalar a gem curses através do RubyGems:
gem install curses
Conceitos Fundamentais
Para trabalhar com a gem curses, é importante entender alguns conceitos essenciais:
- Tela Padrão (stdscr): Ao iniciar a Curses, uma janela principal que ocupa toda a tela do terminal é criada. Esta é conhecida como stdscr (standard screen).
- Janelas (Windows): Você pode criar múltiplas janelas (ou sub-janelas dentro de outras) para dividir a tela em diferentes seções. Cada janela se comporta como uma tela independente.
- Cursor: A Curses mantém o controle da posição do cursor em cada janela, permitindo que você mova-o para coordenadas específicas antes de escrever texto.
- Modos do Terminal: A Curses altera o modo do terminal para ter controle total. Por exemplo, ela pode desativar o eco de caracteres digitados e o buffer de linha, permitindo que sua aplicação capture cada pressionamento de tecla instantaneamente.
- Atualização da Tela: As alterações feitas em uma janela não são exibidas imediatamente no terminal. Você precisa chamar o método refresh na janela para que as atualizações se tornem visíveis.
Tutorial: Construindo uma Aplicação "Hello, Curses!"
Vamos criar uma aplicação simples que demonstra os conceitos básicos da gem curses. Nossa aplicação irá inicializar a tela, exibir uma mensagem de boas-vindas em uma janela centralizada e aguardar a entrada do usuário para encerrar.
Passo 1: Estrutura Básica do Programa
Todo programa que utiliza a gem curses segue uma estrutura básica de inicialização e finalização. É crucial garantir que o estado original do terminal seja restaurado ao final da execução, mesmo que ocorram erros.
Crie um arquivo chamado hello_curses.rb e adicione o seguinte código:
require 'curses' begin # --- Nosso código da aplicação Curses irá aqui --- ensure Curses.close_screen end
O bloco begin/ensure/end garante que Curses.close_screen seja chamado para restaurar o terminal ao seu estado normal antes de o programa terminar.
Passo 2: Inicializando a Tela
Dentro do bloco begin, precisamos inicializar a biblioteca Curses. Adicione as seguintes linhas:
require 'curses' begin Curses.init_screen Curses.cbreak # Desabilita o buffer de linha Curses.noecho # Não exibe os caracteres digitados # --- Restante do nosso código --- ensure Curses.close_screen end
- Curses.init_screen: Inicializa a biblioteca e prepara o terminal para o modo Curses.
- Curses.cbreak: Permite que os caracteres sejam lidos assim que são digitados, sem a necessidade de pressionar Enter.
- Curses.noecho: Impede que os caracteres digitados pelo usuário sejam exibidos na tela.
Passo 3: Criando uma Janela
Agora, vamos criar uma janela para exibir nossa mensagem. Queremos que esta janela seja centralizada na tela.
# ... (código de inicialização)
# Calcula as dimensões e a posição da janela
height = 10
width = 40
top = (Curses.lines - height) / 2
left = (Curses.cols - width) / 2
# Cria a nova janela
win = Curses::Window.new(height, width, top, left)
win.box('|', '-') # Desenha uma borda ao redor da janela
# ... (restante do código)- Curses.lines e Curses.cols retornam a altura e a largura do terminal, respectivamente.
- Curses::Window.new(height, width, top, left) cria uma nova janela com as dimensões e a posição especificadas.
- win.box('|', '-') desenha uma borda ao redor da janela usando os caracteres fornecidos.
Passo 4: Escrevendo na Janela
Com a janela criada, podemos posicionar o cursor e escrever nossa mensagem.
# ... (código da janela)
# Move o cursor para o centro da janela e adiciona o texto
win.setpos(height / 2, (width - "Hello, Curses!".length) / 2)
win.addstr("Hello, Curses!")
win.setpos(height / 2 + 1, (width - "Pressione qualquer tecla para sair".length) / 2)
win.addstr("Pressione qualquer tecla para sair")
# Atualiza a janela para exibir o conteúdo
win.refresh
# ... (restante do código)- win.setpos(y, x) move o cursor da janela para a coordenada y (linha) e x (coluna) especificada.
- win.addstr(string) escreve a string na posição atual do cursor.
- win.refresh atualiza a tela física com o conteúdo do buffer da janela.
Passo 5: Aguardando a Entrada do Usuário
Finalmente, vamos fazer a aplicação esperar por uma entrada do teclado antes de encerrar.
# ... (código de escrita na janela) # Aguarda o usuário pressionar uma tecla win.getch # ... (código de finalização)
- win.getch aguarda e retorna o código do caractere da tecla pressionada.
Código Completo
Juntando todas as partes, o arquivo hello_curses.rb final ficará assim:
require 'curses'
begin
Curses.init_screen
Curses.cbreak
Curses.noecho
# Calcula as dimensões e a posição da janela
height = 10
width = 40
top = (Curses.lines - height) / 2
left = (Curses.cols - width) / 2
# Cria a nova janela
win = Curses::Window.new(height, width, top, left)
win.box('|', '-') # Desenha uma borda ao redor da janela
# Move o cursor para o centro da janela e adiciona o texto
win.setpos(height / 2, (width - "Hello, Curses!".length) / 2)
win.addstr("Hello, Curses!")
win.setpos(height / 2 + 1, (width - "Pressione qualquer tecla para sair".length) / 2)
win.addstr("Pressione qualquer tecla para sair")
# Atualiza a janela para exibir o conteúdo
win.refresh
# Aguarda o usuário pressionar uma tecla
win.getch
ensure
Curses.close_screen
endPara executar, simplesmente rode o script Ruby no seu terminal:
ruby hello_curses.rb
Você deverá ver uma janela centralizada com a mensagem de boas-vindas. Pressionar qualquer tecla encerrará a aplicação e restaurará o seu terminal.
Próximos Passos
Este tutorial arranhou apenas a superfície do que é possível com a gem curses. A partir daqui, você pode explorar funcionalidades mais avançadas, como:
- Cores: Utilizar Curses.start_color e Curses.init_pair para adicionar cores ao texto e ao fundo.
- Atributos de Texto: Aplicar atributos como negrito, sublinhado e reverso.
- Entrada Não-Bloqueante: Configurar getch para não esperar pela entrada, permitindo a criação de loops de jogo e animações.
- Múltiplas Janelas: Gerenciar diferentes partes da sua interface em janelas separadas e atualizá-las independentemente.
- Sub-janelas (subwin): Criar janelas contidas dentro de outras janelas.
A gem curses é uma ferramenta fantástica para criar aplicações de linha de comando ricas e interativas em Ruby. Com os fundamentos apresentados neste artigo, você está pronto para começar a explorar e construir suas próprias interfaces de usuário baseadas em texto.