Cursesで枠を作る

作成日:2016/02/15
最終更新日:2020/07/05

Cursesで枠を作る

作成日:2016/02/15
最終更新日:2020/07/05

概要

Rubyでテキストエディタを作り始めます。 cursesを利用し画面を区切ります。

画面構成

cursesを用い画面を区切ります。 今回はEmacs風に以下のように画面を区切ることにします。

コマンド1. コマンド実行イメージ
+--------------------------------------------------+
|Header                                            |
+--------------------------------------------------+
|Body                                              |
|                                                  |
|                                                  |
|                                                  |
|                                                  |
|                                                  |
+--------------------------------------------------+
|Label                                             |
+--------------------------------------------------+
|Footer                                            |
+--------------------------------------------------+

HeaderとLabelの領域は色を反転させます。 また、EmacsではBodyとLabelの領域の組を複数持てますが、 当面は単一の構成で作成します。

Cursesモジュールの取り込み

以下のようにincludeすることでCurses.を省略できるようになります。

ソース1. モジュールの取り込み
require "curses"
include Curses

ViewWindowクラス

そのまま、cursesを利用しても良いのですが扱いやすくするために ViewWindowクラスを定義します。

windowの親子構造の管理やパラメータの管理を目的とします。 windowを生成するための情報はハッシュで与えることにします。

ソース2. ViewWindowクラス
class ViewWindow
  attr_accessor :parent, :param, :window
  def initialize(parent, param)
    @parent = parent
    if (parent.nil?)
      @parent_window = stdscr
    else
      @parent_window = @parent.window
    end
    @param = param;
    @param[:size] = param[:size]
    @param[:size][:width] = param[:size][:width]
    @param[:size][:height] = param[:size][:height]
    @window = @parent_window.subwin(param[:size][:height], 
                                    param[:size][:width], 
                                    param[:pos][:y], 
                                    param[:pos][:x])
    if (param[:box])
      @window.box(param[:box][:virtical_char], 
                  param[:box][:horizontal_char], 
                  param[:box][:joint_char]);
    end
    if (param[:attr])
      @window.attrset(param[:attr])
    end
  end
  def addstr(str)
    @window.addstr(str.ljust(@param[:size][:width]))
  end
  def setpos(y, x)
    @window.setpos(y, x)
  end
  def refresh
    @window.refresh
  end
end

色の反転

文字の状態を変化させるにはWindowクラスのメソッドattrset()を 呼び値を設定します。 描画色を反転するにはCursesモジュールに組み込まれた定数A_REVERSEを 指定します。

Windowの生成

cursesを初期化し、各種Windowを生成します。 Windowのオブジェクトはwindowハッシュで管理します。

ソース3. Windowの生成
init_screen
window = {}

begin
  # Main Windowの作成
  param = {size: {width: cols, height: lines},
           pos: {x: 0, y: 0}}
  window[:main] = ViewWindow.new(nil, param)
  # Header Windowの作成
  param = {size: {width: window[:main].param[:size][:width], height: 1},
           pos: {x: 0, y: 0},
           attr: (::A_REVERSE)}
  window[:header] = ViewWindow.new(window[:main], param)
  # Footer Windowの作成
  param = {size: {width: window[:main].param[:size][:width], height: 1},
           pos: {x: 0, y: window[:main].param[:size][:height] - 1}}
  window[:footer] = ViewWindow.new(window[:main], param)
  # Label Windowの作成
  param = {size: {width: window[:main].param[:size][:width], height: 1},
           pos: {x: 0, y: window[:main].param[:size][:height] - 2},
           attr: (::A_REVERSE)}
  window[:label] = ViewWindow.new(window[:main], param)
  # Body Windowの作成
  param = {size: {width: window[:main].param[:size][:width], 
                  height: window[:main].param[:size][:height] - 3},
           pos: {x: 0, y: 1}}
  window[:body] = ViewWindow.new(window[:main], param)
  # Header Windowに表示する文字列の設定
  str = "Menu"
  window[:header].addstr(str.ljust(window[:main].param[:size][:width]))
  window[:header].refresh
  # Label Windowに表示する文字列の設定
  str = "Label"
  window[:label].addstr(str.ljust(window[:main].param[:size][:width]))
  window[:label].refresh
  # カーソル位置の設定
  setpos(window[:body].param[:pos][:y],
         window[:body].param[:pos][:x])
  getch
ensure
  close_screen
end

つづく