example: Fix to hide WebView when switch tab. (#764)

fixes #763
it also prevent crashing on linux due to gtk not initialized

---------

Co-authored-by: Jason Lee <huacnlee@gmail.com>
This commit is contained in:
Jakku Sakura 2025-04-02 10:50:20 +08:00 committed by GitHub
parent 99e7f8ee70
commit 4f0617e96f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 92 additions and 13 deletions

1
Cargo.lock generated
View file

@ -6257,6 +6257,7 @@ dependencies = [
"fake",
"gpui",
"gpui-component",
"gtk",
"rand 0.8.5",
"raw-window-handle",
"regex",

View file

@ -20,5 +20,8 @@ serde = "1"
serde_json = "1"
unindent = "0.2.3"
[target.'cfg(target_os = "linux")'.dependencies]
gtk = { version = "0.18" }
[lints]
workspace = true

View file

@ -209,6 +209,7 @@ pub fn init(cx: &mut App) {
input_story::init(cx);
dropdown_story::init(cx);
popup_story::init(cx);
webview_story::init(cx);
let http_client = std::sync::Arc::new(
reqwest_client::ReqwestClient::user_agent("gpui-component/story").unwrap(),
@ -226,9 +227,11 @@ pub fn init(cx: &mut App) {
};
let view = cx.new(|cx| {
let (title, description, closable, zoomable, story) = story_state.to_story(window, cx);
let mut container =
StoryContainer::new(window, cx).story(story, story_state.story_klass);
let (title, description, closable, zoomable, story, on_active) =
story_state.to_story(window, cx);
let mut container = StoryContainer::new(window, cx)
.story(story, story_state.story_klass)
.on_active(on_active);
cx.on_focus_in(
&container.focus_handle,
@ -279,6 +282,7 @@ pub struct StoryContainer {
story_klass: Option<SharedString>,
closable: bool,
zoomable: Option<PanelControl>,
on_active: Option<fn(AnyView, bool, &mut Window, &mut App)>,
}
#[derive(Debug)]
@ -305,6 +309,22 @@ pub trait Story: Focusable + Render {
None
}
fn new_view(window: &mut Window, cx: &mut App) -> Entity<impl Render + Focusable>;
fn on_active(&mut self, active: bool, window: &mut Window, cx: &mut App) {
let _ = active;
let _ = window;
let _ = cx;
}
fn on_active_any(view: AnyView, active: bool, window: &mut Window, cx: &mut App)
where
Self: 'static,
{
if let Some(story) = view.downcast::<Self>().ok() {
cx.update_entity(&story, |story, cx| {
story.on_active(active, window, cx);
});
}
}
}
impl EventEmitter<ContainerEvent> for StoryContainer {}
@ -324,6 +344,7 @@ impl StoryContainer {
story_klass: None,
closable: true,
zoomable: Some(PanelControl::default()),
on_active: None,
}
}
@ -335,7 +356,9 @@ impl StoryContainer {
let focus_handle = story.focus_handle(cx);
let view = cx.new(|cx| {
let mut story = Self::new(window, cx).story(story.into(), story_klass);
let mut story = Self::new(window, cx)
.story(story.into(), story_klass)
.on_active(S::on_active_any);
story.focus_handle = focus_handle;
story.closable = S::closable();
story.zoomable = S::zoomable();
@ -363,6 +386,10 @@ impl StoryContainer {
self.story_klass = Some(story_klass.into());
self
}
pub fn on_active(mut self, on_active: fn(AnyView, bool, &mut Window, &mut App)) -> Self {
self.on_active = Some(on_active);
self
}
fn on_action_panel_info(
&mut self,
@ -420,6 +447,7 @@ impl StoryState {
bool,
Option<PanelControl>,
AnyView,
fn(AnyView, bool, &mut Window, &mut App),
) {
macro_rules! story {
($klass:tt) => {
@ -429,6 +457,7 @@ impl StoryState {
$klass::closable(),
$klass::zoomable(),
$klass::view(window, cx).into(),
$klass::on_active_any,
)
};
}
@ -500,8 +529,13 @@ impl Panel for StoryContainer {
println!("panel: {} zoomed: {}", self.name, zoomed);
}
fn set_active(&mut self, active: bool, _window: &mut Window, _cx: &mut App) {
fn set_active(&mut self, active: bool, _window: &mut Window, cx: &mut App) {
println!("panel: {} active: {}", self.name, active);
if let Some(on_active) = self.on_active {
if let Some(story) = self.story.clone() {
on_active(story, active, _window, cx);
}
}
}
fn popup_menu(&self, menu: PopupMenu, _window: &Window, _cx: &App) -> PopupMenu {

View file

@ -4,15 +4,18 @@ use gpui_component::{
button::{Button, ButtonVariants as _},
dock::{DockArea, DockAreaState, DockEvent, DockItem, DockPlacement},
popup_menu::PopupMenuExt,
IconName, Root, Sizable, Theme, TitleBar,
IconName, Root, Sizable,
};
#[cfg(not(target_os = "linux"))]
use gpui_component::{Theme, TitleBar};
use serde::Deserialize;
use std::{sync::Arc, time::Duration};
use story::{
AccordionStory, AppState, AppTitleBar, Assets, ButtonStory, CalendarStory, DropdownStory,
FormStory, IconStory, ImageStory, InputStory, ListStory, ModalStory, Open, PopupStory,
ProgressStory, Quit, ResizableStory, ScrollableStory, SidebarStory, StoryContainer,
SwitchStory, TableStory, TextStory, TooltipStory,
SwitchStory, TableStory, TextStory, TooltipStory, WebViewStory,
};
#[derive(Clone, PartialEq, Eq, Deserialize)]
@ -365,7 +368,7 @@ impl StoryWorkspace {
Arc::new(StoryContainer::panel::<AccordionStory>(window, cx)),
Arc::new(StoryContainer::panel::<SidebarStory>(window, cx)),
Arc::new(StoryContainer::panel::<FormStory>(window, cx)),
// Arc::new(StoryContainer::panel::<WebViewStory>(window, cx)),
Arc::new(StoryContainer::panel::<WebViewStory>(window, cx)),
],
None,
&dock_area,
@ -452,7 +455,7 @@ impl StoryWorkspace {
14 => Arc::new(StoryContainer::panel::<ResizableStory>(window, cx)),
15 => Arc::new(StoryContainer::panel::<ScrollableStory>(window, cx)),
16 => Arc::new(StoryContainer::panel::<AccordionStory>(window, cx)),
// 17 => Arc::new(StoryContainer::panel::<WebViewStory>(window, cx)),
17 => Arc::new(StoryContainer::panel::<WebViewStory>(window, cx)),
_ => Arc::new(StoryContainer::panel::<ButtonStory>(window, cx)),
};

View file

@ -9,7 +9,11 @@ use gpui_component::{
webview::WebView,
wry, ActiveTheme,
};
use raw_window_handle::HasWindowHandle;
pub fn init(_: &mut App) {
#[cfg(target_os = "linux")]
gtk::init().unwrap();
}
pub struct WebViewStory {
focus_handle: FocusHandle,
@ -25,6 +29,13 @@ impl super::Story for WebViewStory {
fn new_view(window: &mut Window, cx: &mut App) -> Entity<impl Render + Focusable> {
Self::view(window, cx)
}
fn on_active(&mut self, active: bool, _window: &mut Window, cx: &mut App) {
if active {
self.webview.update(cx, |webview, _| webview.show());
} else {
self.webview.update(cx, |webview, _| webview.hide());
}
}
}
impl WebViewStory {
@ -32,9 +43,36 @@ impl WebViewStory {
let focus_handle = cx.focus_handle();
let webview = cx.new(|cx| {
let webview = wry::WebViewBuilder::new()
.build_as_child(&window.window_handle().expect("No window handle"))
.unwrap();
let builder = wry::WebViewBuilder::new();
#[cfg(not(any(
target_os = "windows",
target_os = "macos",
target_os = "ios",
target_os = "android"
)))]
let webview = {
use gtk::prelude::*;
use wry::WebViewBuilderExtUnix;
// borrowed from https://github.com/tauri-apps/wry/blob/dev/examples/gtk_multiwebview.rs
// doesn't work yet
// TODO: How to initialize this fixed?
let fixed = gtk::Fixed::builder().build();
fixed.show_all();
builder.build_gtk(&fixed).unwrap()
};
#[cfg(any(
target_os = "windows",
target_os = "macos",
target_os = "ios",
target_os = "android"
))]
let webview = {
use raw_window_handle::HasWindowHandle;
let window_handle = window.window_handle().expect("No window handle");
builder.build_as_child(&window_handle).unwrap()
};
WebView::new(webview, window, cx)
});