You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MacOS下GPUI与Sysbar互斥问题:如何实现二者共存?

在MacOS上让Rust的GPUI与Sysbar共存的解决方案

问题背景

在MacOS上使用Rust的gpui库创建UI应用,同时想用sysbar库添加状态栏项,依赖配置如下:

[dependencies]
gpui = "0.2.2"
sysbar = "0.3.0"

实现代码:

use gpui::{
    div, prelude::*, px, rgb, size, App, Application, Bounds, Context, SharedString, Window,
    WindowBounds, WindowOptions,
};

use sysbar;

struct HelloWorld {
    text: SharedString,
}
 
impl Render for HelloWorld {
    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
        div()
            .flex()
            .flex_col()
            .gap_3()
            .bg(rgb(0x505050))
            .size(px(500.0))
            .justify_center()
            .items_center()
            .shadow_lg()
            .border_1()
            .border_color(rgb(0x0000ff))
            .text_xl()
            .text_color(rgb(0xffffff))
            .child(format!("Hello, {}!", &self.text))
            .child(
                div()
                    .flex()
                    .gap_2()
                    .child(div().size_8().bg(gpui::red()))
                    .child(div().size_8().bg(gpui::green()))
                    .child(div().size_8().bg(gpui::blue()))
                    .child(div().size_8().bg(gpui::yellow()))
                    .child(div().size_8().bg(gpui::black()))
                    .child(div().size_8().bg(gpui::white())),
            )
    }
}

fn gpui_test() {
    Application::new().run(|cx: &mut App| {
        let bounds = Bounds::centered(None, size(px(500.), px(500.0)), cx);
        cx.open_window(
            WindowOptions {
                window_bounds: Some(WindowBounds::Windowed(bounds)),
                ..Default::default()
            },
            |_, cx| {
                cx.new(|_| HelloWorld {
                    text: "World".into(),
                })
            },
        )
        .unwrap();
        cx.activate(true);
    });
}

fn create_status_bar() {
    let mut bar = sysbar::Sysbar::new("Foo");

    bar.add_item(
        "Say 'bar'",
        Box::new(move || {
            println!("bar");
        }),
    );

    bar.add_quit_item("Quit");

    bar.display();
}

fn main() {
    //test 1. 
    gpui_test();
    create_status_bar();

    //test 2.
    create_status_bar();
    gpui_test();
}

测试发现:先调用gpui_test()再调用create_status_bar()时,仅显示GPUI窗口,状态栏不显示;调换调用顺序则仅显示状态栏,GPUI窗口不显示。本质原因是gpui和sysbar都使用了阻塞式消息循环,调用后会占据当前线程并一直运行,导致后续代码无法执行。

解决方案

核心思路是将其中一个库的消息循环放到独立线程中运行,避免阻塞主线程。由于MacOS的窗口UI通常要求在主线程处理,建议将Sysbar的逻辑放到后台线程,主线程保留给GPUI运行。

修改后的代码

use gpui::{
    div, prelude::*, px, rgb, size, App, Application, Bounds, Context, SharedString, Window,
    WindowBounds, WindowOptions,
};

use sysbar;
use std::thread;

struct HelloWorld {
    text: SharedString,
}
 
impl Render for HelloWorld {
    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
        div()
            .flex()
            .flex_col()
            .gap_3()
            .bg(rgb(0x505050))
            .size(px(500.0))
            .justify_center()
            .items_center()
            .shadow_lg()
            .border_1()
            .border_color(rgb(0x0000ff))
            .text_xl()
            .text_color(rgb(0xffffff))
            .child(format!("Hello, {}!", &self.text))
            .child(
                div()
                    .flex()
                    .gap_2()
                    .child(div().size_8().bg(gpui::red()))
                    .child(div().size_8().bg(gpui::green()))
                    .child(div().size_8().bg(gpui::blue()))
                    .child(div().size_8().bg(gpui::yellow()))
                    .child(div().size_8().bg(gpui::black()))
                    .child(div().size_8().bg(gpui::white())),
            )
    }
}

fn gpui_test() {
    Application::new().run(|cx: &mut App| {
        let bounds = Bounds::centered(None, size(px(500.), px(500.0)), cx);
        cx.open_window(
            WindowOptions {
                window_bounds: Some(WindowBounds::Windowed(bounds)),
                ..Default::default()
            },
            |_, cx| {
                cx.new(|_| HelloWorld {
                    text: "World".into(),
                })
            },
        )
        .unwrap();
        cx.activate(true);
    });
}

fn create_status_bar() {
    let mut bar = sysbar::Sysbar::new("Foo");

    bar.add_item(
        "Say 'bar'",
        Box::new(move || {
            println!("bar");
        }),
    );

    bar.add_quit_item("Quit");

    bar.display();
}

fn main() {
    // 将状态栏逻辑放到后台线程运行
    thread::spawn(create_status_bar);
    // 主线程运行GPUI窗口应用
    gpui_test();
}

补充说明

如果需要实现状态栏与GPUI窗口的交互(比如点击状态栏项更新窗口内容),可以使用Rust的线程安全通信机制,比如std::sync::mpsc通道,结合GPUI提供的cx.spawn或cx.update等跨线程更新API来传递事件和数据。

内容的提问来源于stack exchange,提问作者long

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.02 07:07:31