Skip to content

UI - Main screen

Main の画面を編集ポップアップの背後にレンダリングする必要があるため、最初に描画してから、ポップアップに関する追加のロジックがあります。

私たちのレイアウト

Frame を手に入れたので、実際にウィジェットを描画し始めることができます。レイアウトを作成することから始めます。

let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Length(3),
Constraint::Min(1),
Constraint::Length(3),
])
.split(frame.area());

変数 chunks には、スペースの左上隅とサイズを含む Rect オブジェクトの長さ3配列が含まれています。ウィジェットを準備した後、後でこれらを使用します。

タイトル

タイトルは、どのアプリケーションにとっても重要な要素です。タイトルは、ユーザーが何ができるか、どこにいるかを理解する上で役立ちます。タイトルを作成するには、Paragraph ウィジェット (テキストのみを表示するために使用される) を使用し、境界線を有効にした Block を指定して、Paragraph の周囲に境界線を表示することを指示します。BlockParagraph の詳細については、ブロック レシピパラグラフ レシピ を参照してください。

let title_block = Block::default()
.borders(Borders::ALL)
.style(Style::default());
let title = Paragraph::new(Text::styled(
"Create New Json",
Style::default().fg(Color::Green),
))
.block(title_block);
frame.render_widget(title, chunks[0]);

このコードでは、まず最初に、すべての境界線が有効で、デフォルトのスタイルが設定された Block を作成します。次に、緑色のスタイルが設定された「Create New Json」というテキストを含む段落ウィジェットを作成します。段落の作成の詳細については、段落レシピ を、テキストのスタイル設定についてはテキストのスタイル設定レシピ を参照してください。最後に、Framerender_widget を呼び出し、レンダリングするウィジェットと、ウィジェットを配置する場所とサイズを表す Rect を指定します (すべてのウィジェットはこのように描画されます)。

既存のペアのリスト

また、ユーザーがすでに入力したキーと値のペアも表示できるようにしたいと考えています。 そのためには、別のウィジェットである List を使用します。リストは名前の通り、各 ListItem ごとに新しいテキスト行を作成し、状態を渡すことができるため、追加作業をほとんど行わずにリスト上の項目の選択を実装できます。選択は実装しません。ユーザーがすでに入力したものを表示できるようにするだけです。

let mut list_items = Vec::<ListItem>::new();
for key in app.pairs.keys() {
list_items.push(ListItem::new(Line::from(Span::styled(
format!("{: <25} : {}", key, app.pairs.get(key).unwrap()),
Style::default().fg(Color::Yellow),
))));
}
let list = List::new(list_items);
frame.render_widget(list, chunks[1]);

ライン、スパン、スタイルの詳細については、 Displaying Text recipes を参照してください

この関数の一部では、 ListItem のベクトルを作成し、スタイルとフォーマットされたキー価値ペアを入力します。最後に、 List ウィジェットを作成し、レンダリングします。

下のナビゲーションバー

アプリケーションを初めて使用するユーザーが、どのキーを押せるかについてのヒントを見るのに役立ちます。そのために、2 つのバーと別のレイアウトを実装します。これらの 2 つのバーには、1) 現在の画面 (MainEditing、および Exiting) と 2) 使用可能なキーバインドに関する情報が含まれます。

ここでは、SpanVec を作成します。これは、後で Paragraph によって 1 行に変換されます。(SpanLine とは異なります。Span は、スタイルが適用された Text のセクションを示し、改行で終了しません)

let current_navigation_text = vec![
// The first half of the text
match app.current_screen {
CurrentScreen::Main => Span::styled("Normal Mode", Style::default().fg(Color::Green)),
CurrentScreen::Editing => {
Span::styled("Editing Mode", Style::default().fg(Color::Yellow))
}
CurrentScreen::Exiting => Span::styled("Exiting", Style::default().fg(Color::LightRed)),
}
.to_owned(),
// A white divider bar to separate the two sections
Span::styled(" | ", Style::default().fg(Color::White)),
// The final section of the text, with hints on what the user is editing
{
if let Some(editing) = &app.currently_editing {
match editing {
CurrentlyEditing::Key => {
Span::styled("Editing Json Key", Style::default().fg(Color::Green))
}
CurrentlyEditing::Value => {
Span::styled("Editing Json Value", Style::default().fg(Color::LightGreen))
}
}
} else {
Span::styled("Not Editing Anything", Style::default().fg(Color::DarkGray))
}
},
];
let mode_footer = Paragraph::new(Line::from(current_navigation_text))
.block(Block::default().borders(Borders::ALL));

次に、利用可能なキーを備えたナビゲーションバーでヒントを作成します。これには、異なるスタイルのテキストのセクションがいくつかありません。したがって、コードが少なくなります。

let current_keys_hint = {
match app.current_screen {
CurrentScreen::Main => Span::styled(
"(q) to quit / (e) to make new pair",
Style::default().fg(Color::Red),
),
CurrentScreen::Editing => Span::styled(
"(ESC) to cancel/(Tab) to switch boxes/enter to complete",
Style::default().fg(Color::Red),
),
CurrentScreen::Exiting => Span::styled(
"(q) to quit / (e) to make new pair",
Style::default().fg(Color::Red),
),
}
};
let key_notes_footer =
Paragraph::new(Line::from(current_keys_hint)).block(Block::default().borders(Borders::ALL));

最後に、最初のネストされたレイアウトを作成します。 Layout.split 関数には Rect が必要であり、 Frame ではなく、以前のレイアウトからチャンクの1つを新しいレイアウトのスペースとして渡すことができます。上記のグラフィックからほとんどのセクションを覚えている場合:

This always section should be 3 lines tall Constraint::Length 3

split のパラメーターとして渡すことにより、このスペースに新しいレイアウトを作成します ( chunks[2] ) 。

let footer_chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
.split(chunks[2]);

このコードに相当する視覚的なものは次のとおりです。

Length 50% Length 50% Constraint::Length 3

そして今、私たちは適切なスペースでフッターの段落をレンダリングすることができます。

frame.render_widget(mode_footer, footer_chunks[0]);
frame.render_widget(key_notes_footer, footer_chunks[1]);