feat: add configurable panel background image
This commit is contained in:
+46
-24
@@ -6,10 +6,10 @@ use std::sync::Arc;
|
||||
use gpui::prelude::FluentBuilder as _;
|
||||
use gpui::{
|
||||
AnyElement, AnyView, App, AppContext as _, Bounds, Context, Div, Empty, Entity,
|
||||
GlobalElementId, InspectorElementId, InteractiveElement as _, IntoElement, LayoutId,
|
||||
MouseButton, MouseMoveEvent, MouseUpEvent, ParentElement as _, Pixels, Render, ScrollHandle,
|
||||
SharedString, Stateful, StatefulInteractiveElement as _, Style, StyleRefinement, Styled as _,
|
||||
WeakEntity, Window, actions, div, px,
|
||||
GlobalElementId, ImageSource, InspectorElementId, InteractiveElement as _, IntoElement,
|
||||
LayoutId, MouseButton, MouseMoveEvent, MouseUpEvent, ParentElement as _, Pixels, Render,
|
||||
ScrollHandle, SharedString, Stateful, StatefulInteractiveElement as _, Style, StyleRefinement,
|
||||
Styled as _, WeakEntity, Window, actions, div, px,
|
||||
};
|
||||
pub use gpui_base::dock::{DockArea, DockLayout, DockPlacement};
|
||||
use gpui_base::dock::{
|
||||
@@ -24,7 +24,7 @@ use crate::resizable::{resize_handle, resize_handle_appearance};
|
||||
use crate::tab::Tab;
|
||||
use crate::tab::tab_bar::TabBar;
|
||||
use crate::title_bar::{TRAFFIC_LIGHT_PADDING, title_bar_drag_handlers, window_controls};
|
||||
use crate::{IconName, Selectable, Sizable, StyledExt, h_flex, v_flex};
|
||||
use crate::{IconName, Selectable, Sizable, StyledExt, background, h_flex, v_flex};
|
||||
|
||||
mod panel;
|
||||
pub use panel::*;
|
||||
@@ -431,6 +431,11 @@ impl TabGroupSkin {
|
||||
let tabs_count = group.panels().len();
|
||||
let is_title_bar = self.is_title_bar_group(group, cx);
|
||||
|
||||
let wallpaper = group
|
||||
.active_panel()
|
||||
.and_then(|panel| panel_background(panel, cx))
|
||||
.is_some();
|
||||
|
||||
let displayed = group.active_panel().map(|panel| panel.panel_id(cx));
|
||||
let displayed_ix = displayed.and_then(|displayed| {
|
||||
group
|
||||
@@ -467,7 +472,7 @@ impl TabGroupSkin {
|
||||
let bar = TabBar::new("tab-bar")
|
||||
.track_scroll(&self.scroll_handle)
|
||||
.h(TABBAR_HEIGHT)
|
||||
.bg(cx.theme().bg)
|
||||
.when(!wallpaper, |this| this.bg(cx.theme().bg))
|
||||
.when(needs_traffic_light_padding, |this| {
|
||||
this.pl(px(TRAFFIC_LIGHT_PADDING))
|
||||
})
|
||||
@@ -569,7 +574,7 @@ impl TabGroupSkin {
|
||||
h_flex()
|
||||
.h(TABBAR_HEIGHT)
|
||||
.w_full()
|
||||
.bg(cx.theme().bg)
|
||||
.when(!wallpaper, |this| this.bg(cx.theme().bg))
|
||||
.child(bar.flex_1())
|
||||
.child(window_controls())
|
||||
.into_any_element()
|
||||
@@ -647,23 +652,31 @@ impl TabGroupSkin {
|
||||
}
|
||||
|
||||
impl TabGroupRenderer for TabGroupSkin {
|
||||
fn frame(&self, group: &TabGroupContext, _: &mut Window, _cx: &mut App) -> Stateful<Div> {
|
||||
div().id("tab-panel").when(!group.is_collapsed(), |this| {
|
||||
this.on_action({
|
||||
let group = TabGroupContext::clone(group);
|
||||
move |_: &ToggleZoom, window, cx| group.toggle_zoom(window, cx)
|
||||
fn frame(&self, group: &TabGroupContext, _: &mut Window, cx: &mut App) -> Stateful<Div> {
|
||||
let wallpaper = group
|
||||
.active_panel()
|
||||
.and_then(|panel| panel_background(panel, cx));
|
||||
|
||||
div()
|
||||
.id("tab-panel")
|
||||
.relative()
|
||||
.when_some(wallpaper, |this, source| this.child(background(source, cx)))
|
||||
.when(!group.is_collapsed(), |this| {
|
||||
this.on_action({
|
||||
let group = TabGroupContext::clone(group);
|
||||
move |_: &ToggleZoom, window, cx| group.toggle_zoom(window, cx)
|
||||
})
|
||||
.on_action({
|
||||
let group = TabGroupContext::clone(group);
|
||||
move |_: &ClosePanel, window, cx| {
|
||||
let Some(panel) = group.active_panel() else {
|
||||
return;
|
||||
};
|
||||
let panel = panel.panel_id(cx);
|
||||
group.close(panel, window, cx);
|
||||
}
|
||||
})
|
||||
})
|
||||
.on_action({
|
||||
let group = TabGroupContext::clone(group);
|
||||
move |_: &ClosePanel, window, cx| {
|
||||
let Some(panel) = group.active_panel() else {
|
||||
return;
|
||||
};
|
||||
let panel = panel.panel_id(cx);
|
||||
group.close(panel, window, cx);
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn render_tab_bar(
|
||||
@@ -710,6 +723,11 @@ impl TabGroupRenderer for TabGroupSkin {
|
||||
return Empty.into_any_element();
|
||||
}
|
||||
|
||||
let wallpaper = group
|
||||
.active_panel()
|
||||
.and_then(|active| panel_background(active, cx))
|
||||
.is_some();
|
||||
|
||||
v_flex()
|
||||
.id("tab-content")
|
||||
.group("")
|
||||
@@ -718,7 +736,7 @@ impl TabGroupRenderer for TabGroupSkin {
|
||||
.child(
|
||||
div()
|
||||
.size_full()
|
||||
.bg(cx.theme().bg)
|
||||
.when(!wallpaper, |this| this.bg(cx.theme().bg))
|
||||
.overflow_hidden()
|
||||
.child(panel.cached(StyleRefinement::default().v_flex().size_full())),
|
||||
)
|
||||
@@ -781,3 +799,7 @@ fn panel_title(panel: &Arc<dyn gpui_base::dock::PanelView>, cx: &App) -> AnyElem
|
||||
None => SharedString::from(panel.panel_name(cx)).into_any_element(),
|
||||
}
|
||||
}
|
||||
|
||||
fn panel_background(panel: &Arc<dyn gpui_base::dock::PanelView>, cx: &App) -> Option<ImageSource> {
|
||||
PanelHandle::of(panel).and_then(|handle| handle.panel().background(cx))
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@ use std::any::Any;
|
||||
use std::sync::Arc;
|
||||
|
||||
use gpui::{
|
||||
AnyElement, AnyView, App, Element, Entity, EventEmitter, FocusHandle, Focusable, Render,
|
||||
SharedString, Window,
|
||||
AnyElement, AnyView, App, Element, Entity, EventEmitter, FocusHandle, Focusable, ImageSource,
|
||||
Render, SharedString, Window,
|
||||
};
|
||||
use gpui_base::dock::{PanelId, PanelState};
|
||||
|
||||
@@ -46,6 +46,11 @@ pub trait Panel: EventEmitter<PanelEvent> + Render + Focusable {
|
||||
fn toolbar_buttons(&self, _window: &Window, _cx: &App) -> Vec<Button> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
/// An optional image drawn behind the panel and its tab bar.
|
||||
fn background(&self, _cx: &App) -> Option<ImageSource> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait PanelView: 'static + Send + Sync {
|
||||
@@ -54,6 +59,7 @@ pub(crate) trait PanelView: 'static + Send + Sync {
|
||||
fn closable(&self, cx: &App) -> bool;
|
||||
fn zoomable(&self, cx: &App) -> bool;
|
||||
fn visible(&self, cx: &App) -> bool;
|
||||
fn background(&self, cx: &App) -> Option<ImageSource>;
|
||||
fn set_active(&self, active: bool, window: &mut Window, cx: &mut App);
|
||||
fn set_zoomed(&self, zoomed: bool, cx: &mut App);
|
||||
fn view(&self) -> AnyView;
|
||||
@@ -81,6 +87,10 @@ impl<T: Panel> PanelView for Entity<T> {
|
||||
self.read(cx).visible(cx)
|
||||
}
|
||||
|
||||
fn background(&self, cx: &App) -> Option<ImageSource> {
|
||||
self.read(cx).background(cx)
|
||||
}
|
||||
|
||||
fn set_active(&self, active: bool, window: &mut Window, cx: &mut App) {
|
||||
self.update(cx, |this, cx| {
|
||||
this.set_active(active, window, cx);
|
||||
|
||||
+17
-1
@@ -1,4 +1,7 @@
|
||||
use gpui::{App, Div, Pixels, Refineable, StyleRefinement, Styled, div, px};
|
||||
use gpui::{
|
||||
App, Div, ImageSource, ObjectFit, ParentElement, Pixels, Refineable, StyleRefinement, Styled,
|
||||
StyledImage, div, img, px,
|
||||
};
|
||||
pub use gpui_base::component_traits::{Disableable, Selectable};
|
||||
use theme::{ActiveTheme, TextStyle, Typeset as _, surface_radius, surface_shadows};
|
||||
|
||||
@@ -17,6 +20,19 @@ pub fn divider(cx: &App) -> Div {
|
||||
div().w_full().h_px().bg(cx.theme().border_faint)
|
||||
}
|
||||
|
||||
/// Returns a full-bleed image with a wash for readability, to show behind panel content.
|
||||
pub fn background<I>(image: I, cx: &App) -> Div
|
||||
where
|
||||
I: Into<ImageSource>,
|
||||
{
|
||||
let wash = if cx.theme().is_dark() { 0.5 } else { 0.2 };
|
||||
div()
|
||||
.absolute()
|
||||
.inset_0()
|
||||
.child(img(image).size_full().object_fit(ObjectFit::Cover))
|
||||
.child(div().absolute().inset_0().bg(cx.theme().bg.opacity(wash)))
|
||||
}
|
||||
|
||||
macro_rules! font_weight {
|
||||
($fn:ident, $const:ident) => {
|
||||
/// [docs](https://tailwindcss.com/docs/font-weight)
|
||||
|
||||
Reference in New Issue
Block a user