Skip to content

Layout

Ratatuiの座標系は左から右、上から下部に走行し、ターミナルの左上隅にOrigin (0, 0) があります。XおよびY座標はU16値で表され、通常、ほとんどの場所でその順序でリストされています。

x y (0,0) (columns) (rows)

レイアウトとウィジェットは、RatatuiのUIの基礎を形成します。レイアウトは、インターフェイスの構造を決定し、制約を使用して画面をさまざまなセクションに分割し、ウィジェットはこれらのセクションをコンテンツで埋めます。

ウィジェットを画面にレンダリングするときは、最初にウィジェットが表示される領域を定義する必要があります。この領域は、バッファーに特定の高さと幅がある長方形で表されます。この長方形を絶対的な位置とサイズとして指定することも、Layout structを使用して、 LengthMinMaxRatioPercentage などの制約に基づいてターミナルウィンドウを動的に分割することができます。

次の例では、「Hello World!」をレンダリングします。10回、内部にレンダリングする領域を手動で計算します。

for i in 0..10 {
let area = Rect::new(0, i, frame.area().width, 1);
frame.render_widget(Paragraph::new("Hello world!"), area);
}

レイアウト構造

レイアウト構造体を使用する簡単な例は、次のようになるかもしれません。

use ratatui::prelude::*;
let layout = Layout::default()
.direction(Direction::Vertical)
.constraints(vec![
Constraint::Percentage(50),
Constraint::Percentage(50),
])
.split(frame.area());

この例では、利用可能なスペースを垂直方向に2つの等しい部分に分割し、画面の高さの50%をそれぞれに割り当てることを示しています。Layout::split関数は、[Frame::area()]メソッドによって返されたターミナルウィンドウの合計サイズを引数として取得し、指定された制約に基づいて各長方形の適切なサイズと配置を計算します。

レイアウト (またはネストされたレイアウトのセット) を定義したら、そのようなレイアウトから派生した長方形の領域の1つを使用してウィジェットをレンダリングできます。これは、Frame::render_widgetまたはframe::render_stateful_widgetメソッドのいずれかを呼び出すことで実現できます。

frame.render_widget(
Paragraph::new("Top")
.block(Block::new().borders(Borders::ALL)),
layout[0]);
frame.render_widget(
Paragraph::new("Bottom")
.block(Block::new().borders(Borders::ALL)),
layout[1]);

これは次のように見えるかもしれません:

Top Bottom

この例では、2つの Paragraph ウィジェットが生成され、「top」と「bottom」という名前が生成されます。これらのウィジェットは、それぞれスプリットバッファーの最初と2番目の領域 ( layout[0] および layout[1] ) でレンダリングされます。レイアウトは、それぞれの制約で定義された長方形のインデックス付きリストを返すことに注意することが重要です。この場合、 layout[0] は画面の上半分を指し、 layout[1] は下半分を指します。

ネストレイアウト

理解すべき重要な概念の1つは、レイアウトをネストできることです。これは、外側のレイアウトの長方形内で別のレイアウトを定義できることを意味します。このネストされたレイアウトにより、ターミナルウィンドウでウィジェットのグリッドがどのようにサイズ化するかを制御しながら、複雑で柔軟なUI設計を構築できます。

ネストされたレイアウトの使用方法は次のとおりです。

let outer_layout = Layout::default()
.direction(Direction::Vertical)
.constraints(vec![
Constraint::Percentage(50),
Constraint::Percentage(50),
])
.split(f.size());
let inner_layout = Layout::default()
.direction(Direction::Horizontal)
.constraints(vec![
Constraint::Percentage(25),
Constraint::Percentage(75),
])
.split(outer_layout[1]);

この状況では、ターミナルウィンドウは最初に outer_layout によって2つの等しい部分に垂直に分割されます。次に、 inner_layoutouter_layout の2番目の長方形を水平方向に分割し、元の長方形の幅の25%と75%の2つの領域を作成します。

上記のレイアウトにテキストのいくつかの段落をレンダリングすると、以下が生成されます。

frame.render_widget(
Paragraph::new("outer 0")
.block(Block::new().borders(Borders::ALL)),
outer_layout[0]);
frame.render_widget(
Paragraph::new("inner 0")
.block(Block::new().borders(Borders::ALL)),
inner_layout[0]);
frame.render_widget(
Paragraph::new("inner 1")
.block(Block::new().borders(Borders::ALL)),
inner_layout[1]);
outer 0 inner 0 inner 1

これにより、ターミナルウィンドウをさまざまなサイズの複数のセクションに分割できるため、複雑で適応性のあるグラフィカルなインターフェイスを作成する柔軟性が得られます。

制約

Constraint sレイアウト内のコンポーネントのサイズと配置を決定します。Ratatuiフレームワークは、ユーザーインターフェイスのレイアウトを微調整するためのいくつかの制約タイプを提供します。

  • Constraint::Length(u16) : この制約は、長方形が取り上げるべき特定の数の行または列を指定します。これは絶対サイズによって決定され、ターミナル全体のウィンドウサイズに応答しないことに注意してください。
  • Constraint::Percentage(u16) : この制約は、親レイアウトまたはターミナルウィンドウ自体のサイズに関連するサイズを提供します。たとえば、 Constraint::Percentage(50) は、長方形が親のサイズの半分を占めるべきであることを意味します。
  • Constraint::Ratio(u16, u16) : 比率を使用すると、レイアウトを分割するためのさらに細かい粒度が提供されます。たとえば、 Constraint::Ratio(1, 3) は、親のサイズの1/3をこの制約に割り当てます。
  • Constraint::Min(u16) : コンポーネントのサイズに最小制限を浸します。 Min の制約が Percentage または Ratio で保証されている場合、コンポーネントは指定された最小サイズを下回ることはありません。
  • Constraint::Max(u16) : コンポーネントの最大サイズを制限します。 Min と同様に、 Percentage または Ratio と混合する場合、コンポーネントは指定された最大サイズを超えることはありません。

制約を混合してレイアウト内で一致させることができ、動的で調整可能なインターフェイスを作成できます。これらの制約は、アプリケーションのレイアウトを定義するときに使用できます。

let layout = Layout::default()
.direction(Direction::Horizontal)
.constraints([
Constraint::Length(10),
Constraint::Percentage(70),
Constraint::Min(5),
]
.into_iter())
.split(frame.area());

この例では、最初の Length 制約により、最初の長方形が10文字の幅を持たせます。次の長方形は、合計幅の70%になります。最終的な長方形は残りのスペースを占有しますが、5文字より小さくなることはありません。

制約を指定する順序は、画面スペースに適用される順序であることに注意してください。

デフォルトでは、分割方法は、レイアウトの最後の領域にエリア内の残りのスペースを割り当てます。これを回避するには、未使用の Min(0) 制約を最後の制約として追加します。

Ratatuiは、 Cassowary と呼ばれる制約ソルバーアルゴリズムを使用して、rectsの適切なサイズを決定します。場合によっては、すべての制約が達成できるわけではなく、ソルバーは制約を満たすことに近い任意のソリューションを返すことができます。特定の結果は、これが発生すると非決定的です。

他のレイアウトアプローチ

Taffy を使用して、flexbox / gridアルゴリズム (CSSに類似) を使用して長方をレイアウトするレイアウトを作成するためにいくつかのPOCがあります。これはうまく機能しますが、Ratatuiには組み込まれていません (まだ) 。詳細については、 taffy in ratatui を参照してください。

参照してください

  • [FAQ: バッファーでの範囲外の呼び出しのためにパニックを避けるにはどうすればよいですか?] FAQ-Avoid-Panics