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
相关产品推荐
相关产品推荐

