GPUI의 뷰, 상태, 레이아웃, 이벤트, 접근성 및 시작 예제를 알아봅니다.
GPUI는 브라우저를 임베드하거나 플랫폼 네이티브 위젯을 사용하는 대신 GPU로 자체 인터페이스를 그리는 Rust UI 프레임워크입니다. macOS에서는 Metal, Windows에서는 DirectX 11, Linux에서는 wgpu를 사용하며, Linux 창 관리는 X11 또는 Wayland가 담당합니다.
GPUI 앱은 창을 열고 여기에 루트 뷰를 제공합니다. 뷰는 Render를 구현하는 GPUI 관리 Rust 구조체이며, render 메서드는 요소 트리로 UI를 구성합니다. React 또는 React와 유사한 UI 프레임워크를 알고 있다면 뷰와 상태 모델이 익숙하게 느껴질 수 있습니다.
아래 예제에서 뷰는 자신의 카운트를 표시하고 클릭하면 이를 증가시킵니다.
impl Render for Counter {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
div()
.child(format!("Count: {}", self.count))
.child(
div()
.on_click(cx.listener(|this, _, _, cx| {
this.count += 1;
cx.notify();
}))
.child("Add"),
)
}
}
핸들러는 뷰의 상태를 변경한 다음 새 렌더링을 요청하기 위해 cx.notify()를 호출합니다. 필드만 변경해서는 새 렌더링이 요청되지 않습니다. 키보드 상호작용과 테스트를 포함한 완전한 앱은 전체 카운터 및 테스트 예제에서 확인하세요.
Tailwind에 익숙하다면 GPUI의 스타일링 API도 친숙하게 느껴질 것입니다. 문법은 HTML 요소에 CSS 유틸리티 클래스를 조합하는 방식과 비슷해 보이는 Rust 메서드 호출입니다.
예를 들어, 다음은 간격이 있는 가운데 정렬 열을 표현합니다.
div()
.flex()
.flex_col()
.gap_3()
.justify_center()
.items_center()
다음과 같습니다.
.stack {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 12px;
}
메서드 이름은 유사하지만, 사용 가능한 속성과 동작은 GPUI가 정의합니다. 아직 완전한 CSS 호환성을 제공하지는 않습니다. GPUI는 주로 Zed 또는 Delta를 구축하는 데 필요한 기능과 커뮤니티가 주도하는 기타 기능을 구현합니다. 하지만 웹에서 하듯 실제 애플리케이션을 스타일링할 수 있을 만큼 충분한 CSS 유사 기능을 지원합니다.
예를 들어, GPUI는 그리드 레이아웃을 제공합니다.
div()
.grid()
.grid_cols(5)
.child(header.col_span_full())
.child(sidebar.col_span(1))
.child(content.col_span(3))
이 그리드 레이아웃 예제는 넓은 레이아웃에 5열 그리드를 사용하고, 컨테이너가 좁아지면 쌓인 플렉스 레이아웃으로 전환합니다. 이 전환은 CSS의 대응 기능에서 자연스럽게 영감을 얻은 GPUI의 container_query로 작성됩니다.
마우스 핸들러는 Rust 코드를 직접 실행하고, 키보드 단축키는 GPUI 액션을 사용합니다. 브라우저와 Rust 사이에 RPC 경계가 없으므로 핸들러가 애플리케이션의 Rust 코드를 호출할 수 있습니다.
키보드 단축키는 액션, 뷰의 핸들러, 앱 설정의 키 바인딩을 사용합니다.
actions!(counter, [Increment]);
struct Counter {
count: i32,
}
impl Counter {
fn increment(&mut self, _: &Increment, _: &mut Window, cx: &mut Context<Self>) {
self.count += 1;
cx.notify();
}
}
impl Render for Counter {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
div()
.key_context("Counter")
.on_action(cx.listener(Self::increment))
}
}
fn bind_keys(cx: &mut App) {
cx.bind_keys([KeyBinding::new("up", Increment, Some("Counter"))]);
}
key_context는 바인딩이 적용되는 범위를 지정합니다. 자세한 내용은 키 디스패치 가이드를 참조하세요. 디스크 또는 네트워크 I/O처럼 느린 작업은 UI 스레드에서 처리하지 마세요.
웹과 마찬가지로 GPUI에서 사용자 지정 컨트롤의 접근성을 보장하는 책임은 여러분에게 있습니다. GPUI는 AccessKit을 사용해 접근성 트리를 노출합니다. 접근성 가이드를 참조하세요.
GPUI는 UI 프레임워크를 제공하지만, 앱 아키텍처의 나머지 부분까지 제공하지는 않습니다. 브라우저 DOM을 제공하지 않으며, 컨트롤을 네이티브 플랫폼 위젯으로 자동 변환하지도 않습니다.
프로젝트 대시보드를 만들어 보세요. 스크롤 가능한 목록에 프로젝트를 표시하고, 텍스트 필드로 필터링하며, 각 행의 세부 정보를 열거나 표시하는 방법을 연습할 수 있습니다. 대규모 애플리케이션 없이도 입력, 상태, 이벤트 처리, 목록 렌더링을 함께 다룰 수 있습니다.
실행 가능한 시작점은 헬로 월드, 입력, 전체 GPUI 예제 디렉터리를 참조하세요.