開発

[binllion] スクロール機能を実装する

カーソルの表示と移動ができるようになったので次はスクロールの機能をつけたいと思います。

スクロールはratatuiのParagraphにあるそのものズバリなscrollメソッドを使って行います。

これらの処理を行うために、スクロール処理を扱う新たなScroll構造体を追加しました。

メインパネルとASCIIコードを表示するサブパネルのスクロール量を配列に格納して、Paragraphで利用する形としました。

これにいくつかのヘルパー関数を作成してスクロール量の計算をしていきます。

// スクロール量
pub(crate) struct Scroll {
    scroll_y: [u16; 2], // main:0, sub0:1
}

impl Scroll {
    fn new() -> Self {
        let scroll_y = [0; 2];
        Self { scroll_y }
    }

    pub(crate) fn scroll_y(&self) -> &[u16; 2] {
        &self.scroll_y
    }
    pub(crate) fn scroll_y_mut(&mut self) -> &mut [u16; 2] {
        &mut self.scroll_y
    }
    // スクロール上限計算
    pub(crate) fn calc_border(bottom: u16) -> u16 {
        const SCROLL_Y_BORDER: u16 = 3;
        let border = bottom.saturating_sub(SCROLL_Y_BORDER);
        border
    }
    // スクロール計算
    pub(crate) fn calc_scroll(y: u16, border: u16) -> u16 {
        let scroll_y;
        if y > border {
            scroll_y = y - border;
        } else {
            scroll_y = 0;
        }
        scroll_y
    }
}
Code language: PHP (php)

アプリとしては縦方向のみのスクロールを想定しています。

Y軸の画面上の下限を取得してそのいくつか手前(上限 – SCROLL_Y_BORDER)にカーソルが来た段階でスクロールが始まるように処理をしています。

描画処理関連のコードが大きくなってきたこととmessageが可変である必要が出てきたので新たにrender_prep()を追加してそちらで前処理をおこなう事とします。

スコープを限定した可変参照

ここで少し所有権に関してテクニカルな書き方をしています。

Rustの所有権について以前書きましたが可変参照は1つしか持てません。

そのため、6行目のmessage.cursor_mut()と15行目のmessage.scroll_mut().scroll_y_mut()はmessageのメンバーを変更するので同時に使用することができません。

ただし、同一スコープでなければ記述は可能です。

同じスコープで同時に可変参照を持たないようにpos_y変数の値の取得を限定したスコープ内で先におこない、message.scroll_mut().scroll_y_mut()とコンフリクトしないようにしています。

現状はこのままのコードでも良いですが、message.scroll_mut().scroll_y_mut()の処理も限定したスコープ内で実行するようにしたほうが良いかもしれません。

// ratatuiレンダリング準備
pub(crate) fn render_prep(message: &mut Message) -> io::Result<()> {
.....
    // カーソルY座標の算出
    let pos_y = {
        let cursor = message.cursor_mut();
        cursor.calc_position();
        cursor.position().y
    };
    // 画面の下限
    let main_bottom = main_layout[0].bottom();
    // メインパネルのスクロール開始ラインの算出
    let main_border = Scroll::calc_border(main_bottom);
    // メインパネルのスクロール量計算
    let scroll_y = message.scroll_mut().scroll_y_mut();
    scroll_y[0] = Scroll::calc_scroll(pos_y, main_border);
.....
Code language: JavaScript (javascript)

その他

イベントループの中に新しく作ったrender_prep()を追加しました。

    // イベントループ
    while event_handler.is_looping() {
        // 描画処理準備
        let _ = render_prep(&mut terminal, &mut message);
        // 描画処理
        let _ = render_main(&mut terminal, &message);
        // イベント処理
        event_handler.run(&mut message);
    }
Code language: JavaScript (javascript)

render_main()では下記のようにスクロール処理をしています。

    let main_contents = Paragraph::new(Text::from(main_panel_data))
        .scroll((message.scroll().scroll_y()[0], 0))
        .block(block.clone());
Code language: JavaScript (javascript)

GitHubにコードをアップロードしています。0.1.0バージョン

コードのコメントに書かれているfirst_stepなどをcargoコマンドに渡すと実行できます。

# Example
$ cargo run --example first_stepCode language: Bash (bash)
管理人

Recent Posts

情報セキュリティマネジメント試験取得への道

スキルアップを図るべく情報セキ…

2か月 ago

ファイナンシャルプランナー3級試験取得への道

スキルアップを図るべくファイナ…

2か月 ago

[rust] New Type Patternを使ってみる

DDDの考えを取り入れることで…

5か月 ago

RustでDDDの要素を取り入れてみる

前回SOLID原則というものを…

5か月 ago

期間限定!書籍無料キャンペーン2025

「mdBookではじめるKin…

5か月 ago