3
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ZedエディタのGPUIを使ったUIアプリを作成

3
Posted at

ZedはGPUを活用したRust製の高性能エディタで、3Dゲームのような手法で画面を描画しているのが特徴です。

GPUIと呼ばれるUIフレームワークがUIの描画を担っており、そのコア機能がgpuiクレートとして利用できるようになっています。

  • gpui 0.2.2

そこで、このgpuiクレートを使って単純なUIアプリを作ってみました。

はじめに

GPUIを利用するには、gpuiクレートをcargo addするだけです。

cargo add例
$ cargo add gpui
Cargo.toml
[dependencies]
gpui = "0.2.2"

ただし、ビルド時にシェーダーコンパイル1等が必要になるため、そのための環境が必要です。

例えば、macOS環境ではXcodeとMetalToolchain、Windows環境ではVisual StudioのビルドツールとWindows SDKが必要でした。

ちなみに、Zedソースコード内のクレートを用いる方法も可能でしたが2、場合によっては手間が増えそうなのでご注意ください。

テキスト表示

GPUIでウインドウを表示する処理は例えばこのようになります。

ここでは、描画領域の上部にセンタリングしたテキスト(文字サイズを大きくしている)を表示しています。

src/main.rs
use gpui::{AppContext, Application, ParentElement, Render, Styled, WindowOptions, div, rems};
use std::env;

struct SimpleView(String);

impl Render for SimpleView {
    fn render(
        &mut self,
        _window: &mut gpui::Window,
        _ctx: &mut gpui::prelude::Context<Self>,
    ) -> impl gpui::prelude::IntoElement {
        div()
            .size_full()
            .bg(gpui::white())
            .text_center()
            .text_size(rems(7.))
            .child(self.0.clone())
    }
}

fn main() {
    let text = env::args()
        .skip(1)
        .next()
        .unwrap_or("GPUI Test View".into());

    Application::new().run(|app| {
        app.open_window(WindowOptions::default(), |_, app| {
            app.new(|_| SimpleView(text))
        })
        .unwrap();

        app.activate(true);
    });
}

Renderトレイトのrenderメソッドを実装してUIを描画します。

div()で作成したDivのメソッドチェーンを用いてUIを組み立てるのが基本的な作法のようです。

CSSに相当する機能がStyledトレイトで定義されており、Divはこのトレイトを実装しています。

例えば、CSSのdisplay相当のレイアウト関連メソッドには次のようなものがあります。

  • block()
  • flex()
  • grid()
  • hidden()3

また、Styledトレイトはマクロで大量のメソッドを定義しており4、例えばこのようなものが用意されています。

メソッド 概要
p_xxx p_10 top, bottom, left, rightのパディング指定
pt_xxx pt_5 topのパディング指定
m_xxx m_1 top, bottom, left, rightのマージン指定
mb_xxx mb_2 bottomのマージン指定
border_xxx border_3 top, bottom, left, rightのボーダー指定
border_l_xxx border_l_4 leftのボーダー指定
size_xxx size_full 要素のサイズ指定。size_fullの場合は100%になる

ちなみに、上記コードでsize_fullしていないと、背景色(white)の適用範囲がテキストの表示エリアだけに限定されてしまい、その他の部分は環境依存で黒や透明になりました。

実行結果はこのようになります。(macOS環境の場合)

実行例
$ cargo run "Zed Editor GPUIを使ったアプリケーションのテスト表示"

ウインドウ表示例(macOS環境)

1.png

マウスイベント処理

マウスのダウンイベントを使って数値をアップ・ダウンするような処理はこのようになります。

on_mouse_downで対象とするマウスボタンの種類とイベントハンドラを登録します。
状態を更新した際はContextのnotifyを呼び出す事で再描画が行われます。

src/main2.rs
use gpui::prelude::*;
use gpui::{
    Application, Bounds, MouseButton, MouseDownEvent, Render, Window, WindowBounds, WindowOptions,
    div, px, rgb, size, white,
};

struct Counter {
    count: isize,
}

impl Counter {
    fn up(&mut self, _event: &MouseDownEvent, _window: &mut Window, ctx: &mut Context<Self>) {
        self.count += 1;
        ctx.notify();
    }

    fn down(&mut self, _event: &MouseDownEvent, _window: &mut Window, ctx: &mut Context<Self>) {
        self.count -= 1;
        ctx.notify();
    }
}

impl Render for Counter {
    fn render(&mut self, _window: &mut Window, ctx: &mut Context<Self>) -> impl IntoElement {
        div()
            .size_full()
            .flex()
            .flex_col()
            .p_10()
            .bg(white())
            .justify_center()
            .child("Counter:")
            .child(
                div()
                    .bg(rgb(0xAAEEAA))
                    .text_center()
                    .text_3xl()
                    .child(format!("{}", self.count))
                    .on_mouse_down(MouseButton::Left, ctx.listener(Self::up))
                    .on_mouse_down(MouseButton::Right, ctx.listener(Self::down)),
            )
    }
}

fn main() {
    Application::new().run(|app| {
        // ウインドウサイズ
        let bounds = Bounds::centered(None, size(px(500.), px(300.)), app);

        let opts = WindowOptions {
            window_bounds: Some(WindowBounds::Windowed(bounds)),
            ..Default::default()
        };

        app.open_window(opts, |_, app| app.new(|_| Counter { count: 1 }))
            .unwrap();

        app.activate(true);
    });
}

flex()だけだと横方向へ要素を配置するため、flex_col()で縦方向へ配置するようにしています。

こちらの実行結果はこのようになります。

ウインドウ表示例(macOS環境)

  1. macOS環境ではMetal、Windows環境ではHLSLファイルのシェーダーコンパイルを行うようになっている

  2. 例えば、gpui = { git = "https://github.com/zed-industries/zed", version = "0.2.2", tag = "v1.13.1" }のように依存関係を設定。なお、こちらの方法だとgpui単体では扱えず、gpui_platformも必要でした

  3. 非表示。noneに相当

  4. prefixとsuffixを組み合わせたりしてメソッドを生成している。Zedソースコードの crates/gpui_macros/src/styles.rs 参照

3
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?