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", "fake",
"gpui", "gpui",
"gpui-component", "gpui-component",
"gtk",
"rand 0.8.5", "rand 0.8.5",
"raw-window-handle", "raw-window-handle",
"regex", "regex",

View file

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

View file

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

View file

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

View file

@ -9,7 +9,11 @@ use gpui_component::{
webview::WebView, webview::WebView,
wry, ActiveTheme, wry, ActiveTheme,
}; };
use raw_window_handle::HasWindowHandle;
pub fn init(_: &mut App) {
#[cfg(target_os = "linux")]
gtk::init().unwrap();
}
pub struct WebViewStory { pub struct WebViewStory {
focus_handle: FocusHandle, 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> { fn new_view(window: &mut Window, cx: &mut App) -> Entity<impl Render + Focusable> {
Self::view(window, cx) 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 { impl WebViewStory {
@ -32,9 +43,36 @@ impl WebViewStory {
let focus_handle = cx.focus_handle(); let focus_handle = cx.focus_handle();
let webview = cx.new(|cx| { let webview = cx.new(|cx| {
let webview = wry::WebViewBuilder::new() let builder = wry::WebViewBuilder::new();
.build_as_child(&window.window_handle().expect("No window handle")) #[cfg(not(any(
.unwrap(); 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) WebView::new(webview, window, cx)
}); });