diff --git a/Cargo.lock b/Cargo.lock index b5d2e315..1f5057d3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6257,6 +6257,7 @@ dependencies = [ "fake", "gpui", "gpui-component", + "gtk", "rand 0.8.5", "raw-window-handle", "regex", diff --git a/crates/story/Cargo.toml b/crates/story/Cargo.toml index edc481ef..f2205900 100644 --- a/crates/story/Cargo.toml +++ b/crates/story/Cargo.toml @@ -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 diff --git a/crates/story/src/lib.rs b/crates/story/src/lib.rs index dc676517..81a120eb 100644 --- a/crates/story/src/lib.rs +++ b/crates/story/src/lib.rs @@ -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, closable: bool, zoomable: Option, + on_active: Option, } #[derive(Debug)] @@ -305,6 +309,22 @@ pub trait Story: Focusable + Render { None } fn new_view(window: &mut Window, cx: &mut App) -> Entity; + + 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::().ok() { + cx.update_entity(&story, |story, cx| { + story.on_active(active, window, cx); + }); + } + } } impl EventEmitter 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, 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 { diff --git a/crates/story/src/main.rs b/crates/story/src/main.rs index 026f02de..2cc0d6a9 100644 --- a/crates/story/src/main.rs +++ b/crates/story/src/main.rs @@ -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::(window, cx)), Arc::new(StoryContainer::panel::(window, cx)), Arc::new(StoryContainer::panel::(window, cx)), - // Arc::new(StoryContainer::panel::(window, cx)), + Arc::new(StoryContainer::panel::(window, cx)), ], None, &dock_area, @@ -452,7 +455,7 @@ impl StoryWorkspace { 14 => Arc::new(StoryContainer::panel::(window, cx)), 15 => Arc::new(StoryContainer::panel::(window, cx)), 16 => Arc::new(StoryContainer::panel::(window, cx)), - // 17 => Arc::new(StoryContainer::panel::(window, cx)), + 17 => Arc::new(StoryContainer::panel::(window, cx)), _ => Arc::new(StoryContainer::panel::(window, cx)), }; diff --git a/crates/story/src/webview_story.rs b/crates/story/src/webview_story.rs index 6a7d5a78..a2a7cf92 100644 --- a/crates/story/src/webview_story.rs +++ b/crates/story/src/webview_story.rs @@ -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 { 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) });