Detailed changes
@@ -13,8 +13,8 @@ use call::{report_call_event_for_room, ActiveCall};
pub use collab_panel::CollabPanel;
pub use collab_titlebar_item::CollabTitlebarItem;
use gpui::{
- actions, point, AppContext, DevicePixels, Pixels, PlatformDisplay, Size, Task, WindowContext,
- WindowKind, WindowOptions,
+ actions, point, AppContext, DevicePixels, Pixels, PlatformDisplay, Size, Task,
+ WindowBackgroundAppearance, WindowContext, WindowKind, WindowOptions,
};
use panel_settings::MessageEditorSettings;
pub use panel_settings::{
@@ -121,5 +121,6 @@ fn notification_window_options(
is_movable: false,
display_id: Some(screen.id()),
fullscreen: false,
+ window_background: WindowBackgroundAppearance::default(),
}
}
@@ -48,6 +48,7 @@ fn main() {
display_id: Some(screen.id()),
titlebar: None,
+ window_background: WindowBackgroundAppearance::default(),
focus: false,
show: true,
kind: WindowKind::PopUp,
@@ -190,6 +190,7 @@ pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle {
fn activate(&self);
fn is_active(&self) -> bool;
fn set_title(&mut self, title: &str);
+ fn set_background_appearance(&mut self, background_appearance: WindowBackgroundAppearance);
fn set_edited(&mut self, edited: bool);
fn show_character_palette(&self);
fn minimize(&self);
@@ -533,6 +534,9 @@ pub struct WindowOptions {
/// The display to create the window on, if this is None,
/// the window will be created on the main display
pub display_id: Option<DisplayId>,
+
+ /// The appearance of the window background.
+ pub window_background: WindowBackgroundAppearance,
}
/// The variables that can be configured when creating a new window
@@ -555,6 +559,8 @@ pub(crate) struct WindowParams {
pub show: bool,
pub display_id: Option<DisplayId>,
+
+ pub window_background: WindowBackgroundAppearance,
}
impl Default for WindowOptions {
@@ -572,6 +578,7 @@ impl Default for WindowOptions {
is_movable: true,
display_id: None,
fullscreen: false,
+ window_background: WindowBackgroundAppearance::default(),
}
}
}
@@ -633,6 +640,27 @@ impl Default for WindowAppearance {
}
}
+/// The appearance of the background of the window itself, when there is
+/// no content or the content is transparent.
+#[derive(Copy, Clone, Debug, Default, PartialEq)]
+pub enum WindowBackgroundAppearance {
+ /// Opaque.
+ ///
+ /// This lets the window manager know that content behind this
+ /// window does not need to be drawn.
+ ///
+ /// Actual color depends on the system and themes should define a fully
+ /// opaque background color instead.
+ #[default]
+ Opaque,
+ /// Plain alpha transparency.
+ Transparent,
+ /// Transparency, but the contents behind the window are blurred.
+ ///
+ /// Not always supported.
+ Blurred,
+}
+
/// The options that can be configured for a file dialog prompt
#[derive(Copy, Clone, Debug)]
pub struct PathPromptOptions {
@@ -23,7 +23,7 @@ use crate::platform::{PlatformAtlas, PlatformInputHandler, PlatformWindow};
use crate::scene::Scene;
use crate::{
px, size, Bounds, DevicePixels, Modifiers, Pixels, PlatformDisplay, PlatformInput, Point,
- PromptLevel, Size, WindowAppearance, WindowParams,
+ PromptLevel, Size, WindowAppearance, WindowBackgroundAppearance, WindowParams,
};
#[derive(Default)]
@@ -355,6 +355,10 @@ impl PlatformWindow for WaylandWindow {
self.0.toplevel.set_title(title.to_string());
}
+ fn set_background_appearance(&mut self, _background_appearance: WindowBackgroundAppearance) {
+ // todo(linux)
+ }
+
fn set_edited(&mut self, edited: bool) {
// todo(linux)
}
@@ -4,7 +4,7 @@
use crate::{
platform::blade::BladeRenderer, size, Bounds, DevicePixels, Modifiers, Pixels, PlatformAtlas,
PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point, PromptLevel,
- Scene, Size, WindowAppearance, WindowOptions, WindowParams,
+ Scene, Size, WindowAppearance, WindowBackgroundAppearance, WindowOptions, WindowParams,
};
use blade_graphics as gpu;
use parking_lot::Mutex;
@@ -423,6 +423,10 @@ impl PlatformWindow for X11Window {
// todo(linux)
fn set_edited(&mut self, edited: bool) {}
+ fn set_background_appearance(&mut self, _background_appearance: WindowBackgroundAppearance) {
+ // todo(linux)
+ }
+
// todo(linux), this corresponds to `orderFrontCharacterPalette` on macOS,
// but it looks like the equivalent for Linux is GTK specific:
//
@@ -73,7 +73,7 @@ impl MetalRenderer {
let layer = metal::MetalLayer::new();
layer.set_device(&device);
layer.set_pixel_format(MTLPixelFormat::RGBA8Unorm);
- layer.set_opaque(true);
+ layer.set_opaque(false);
layer.set_maximum_drawable_count(3);
unsafe {
let _: () = msg_send![&*layer, setAllowsNextDrawableTimeout: NO];
@@ -4,12 +4,12 @@ use crate::{
DisplayLink, ExternalPaths, FileDropEvent, ForegroundExecutor, KeyDownEvent, Keystroke,
Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformWindow, Point, PromptLevel,
- Size, Timer, WindowAppearance, WindowKind, WindowParams,
+ Size, Timer, WindowAppearance, WindowBackgroundAppearance, WindowKind, WindowParams,
};
use block::ConcreteBlock;
use cocoa::{
appkit::{
- CGPoint, NSApplication, NSBackingStoreBuffered, NSEventModifierFlags,
+ CGPoint, NSApplication, NSBackingStoreBuffered, NSColor, NSEvent, NSEventModifierFlags,
NSFilenamesPboardType, NSPasteboard, NSScreen, NSView, NSViewHeightSizable,
NSViewWidthSizable, NSWindow, NSWindowButton, NSWindowCollectionBehavior,
NSWindowOcclusionState, NSWindowStyleMask, NSWindowTitleVisibility,
@@ -83,6 +83,17 @@ const NSDragOperationNone: NSDragOperation = 0;
#[allow(non_upper_case_globals)]
const NSDragOperationCopy: NSDragOperation = 1;
+#[link(name = "CoreGraphics", kind = "framework")]
+extern "C" {
+ // Widely used private APIs; Apple uses them for their Terminal.app.
+ fn CGSMainConnectionID() -> id;
+ fn CGSSetWindowBackgroundBlurRadius(
+ connection_id: id,
+ window_id: NSInteger,
+ radius: i64,
+ ) -> i32;
+}
+
#[ctor]
unsafe fn build_classes() {
WINDOW_CLASS = build_window_class("GPUIWindow", class!(NSWindow));
@@ -509,6 +520,7 @@ impl MacWindow {
pub fn open(
handle: AnyWindowHandle,
WindowParams {
+ window_background,
bounds,
titlebar,
kind,
@@ -606,7 +618,7 @@ impl MacWindow {
)
};
- let window = Self(Arc::new(Mutex::new(MacWindowState {
+ let mut window = Self(Arc::new(Mutex::new(MacWindowState {
handle,
executor,
native_window,
@@ -685,6 +697,8 @@ impl MacWindow {
native_window.setContentView_(native_view.autorelease());
native_window.makeFirstResponder_(native_view);
+ window.set_background_appearance(window_background);
+
match kind {
WindowKind::Normal => {
native_window.setLevel_(NSNormalWindowLevel);
@@ -967,6 +981,31 @@ impl PlatformWindow for MacWindow {
}
}
+ fn set_background_appearance(&mut self, background_appearance: WindowBackgroundAppearance) {
+ let this = self.0.as_ref().lock();
+ let blur_radius = if background_appearance == WindowBackgroundAppearance::Blurred {
+ 80
+ } else {
+ 0
+ };
+ let opaque = if background_appearance == WindowBackgroundAppearance::Opaque {
+ YES
+ } else {
+ NO
+ };
+ unsafe {
+ this.native_window.setOpaque_(opaque);
+ let clear_color = if opaque == YES {
+ NSColor::colorWithSRGBRed_green_blue_alpha_(nil, 0f64, 0f64, 0f64, 1f64)
+ } else {
+ NSColor::clearColor(nil)
+ };
+ this.native_window.setBackgroundColor_(clear_color);
+ let window_number = this.native_window.windowNumber();
+ CGSSetWindowBackgroundBlurRadius(CGSMainConnectionID(), window_number, blur_radius);
+ }
+ }
+
fn set_edited(&mut self, edited: bool) {
unsafe {
let window = self.0.lock().native_window;
@@ -2,7 +2,7 @@ use crate::{
AnyWindowHandle, AtlasKey, AtlasTextureId, AtlasTile, Bounds, DevicePixels,
DispatchEventResult, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput,
PlatformInputHandler, PlatformWindow, Point, Size, TestPlatform, TileId, WindowAppearance,
- WindowParams,
+ WindowBackgroundAppearance, WindowParams,
};
use collections::HashMap;
use parking_lot::Mutex;
@@ -190,6 +190,10 @@ impl PlatformWindow for TestWindow {
self.0.lock().title = Some(title.to_owned());
}
+ fn set_background_appearance(&mut self, _background: WindowBackgroundAppearance) {
+ unimplemented!()
+ }
+
fn set_edited(&mut self, edited: bool) {
self.0.lock().edited = edited;
}
@@ -1468,6 +1468,10 @@ impl PlatformWindow for WindowsWindow {
.ok();
}
+ fn set_background_appearance(&mut self, _background_appearance: WindowBackgroundAppearance) {
+ // todo(windows)
+ }
+
// todo(windows)
fn set_edited(&mut self, _edited: bool) {}
@@ -7,8 +7,8 @@ use crate::{
Modifiers, ModifiersChangedEvent, MouseButton, MouseMoveEvent, MouseUpEvent, Pixels,
PlatformAtlas, PlatformDisplay, PlatformInput, PlatformWindow, Point, PromptLevel, Render,
ScaledPixels, SharedString, Size, SubscriberSet, Subscription, TaffyLayoutEngine, Task,
- TextStyle, TextStyleRefinement, View, VisualContext, WeakView, WindowAppearance, WindowOptions,
- WindowParams, WindowTextSystem,
+ TextStyle, TextStyleRefinement, View, VisualContext, WeakView, WindowAppearance,
+ WindowBackgroundAppearance, WindowOptions, WindowParams, WindowTextSystem,
};
use anyhow::{anyhow, Context as _, Result};
use collections::FxHashSet;
@@ -398,6 +398,7 @@ impl Window {
is_movable,
display_id,
fullscreen,
+ window_background,
} = options;
let bounds = bounds.unwrap_or_else(|| default_bounds(display_id, cx));
@@ -411,6 +412,7 @@ impl Window {
focus,
show,
display_id,
+ window_background,
},
);
let display_id = platform_window.display().id();
@@ -872,7 +874,7 @@ impl<'a> WindowContext<'a> {
self.window.platform_window.bounds()
}
- /// Retusn whether or not the window is currently fullscreen
+ /// Returns whether or not the window is currently fullscreen
pub fn is_fullscreen(&self) -> bool {
self.window.platform_window.is_fullscreen()
}
@@ -911,6 +913,13 @@ impl<'a> WindowContext<'a> {
self.window.platform_window.set_title(title);
}
+ /// Sets the window background appearance.
+ pub fn set_background_appearance(&mut self, background_appearance: WindowBackgroundAppearance) {
+ self.window
+ .platform_window
+ .set_background_appearance(background_appearance);
+ }
+
/// Mark the window as dirty at the platform level.
pub fn set_window_edited(&mut self, edited: bool) {
self.window.platform_window.set_edited(edited);
@@ -1,5 +1,7 @@
use std::sync::Arc;
+use gpui::WindowBackgroundAppearance;
+
use crate::prelude::*;
use crate::{
@@ -15,6 +17,7 @@ fn zed_pro_daylight() -> Theme {
name: "Zed Pro Daylight".into(),
appearance: Appearance::Light,
styles: ThemeStyles {
+ window_background_appearance: WindowBackgroundAppearance::Opaque,
system: SystemColors::default(),
colors: ThemeColors::light(),
status: StatusColors::light(),
@@ -45,6 +48,7 @@ pub(crate) fn zed_pro_moonlight() -> Theme {
name: "Zed Pro Moonlight".into(),
appearance: Appearance::Dark,
styles: ThemeStyles {
+ window_background_appearance: WindowBackgroundAppearance::Opaque,
system: SystemColors::default(),
colors: ThemeColors::dark(),
status: StatusColors::dark(),
@@ -1,6 +1,6 @@
use std::sync::Arc;
-use gpui::{hsla, FontStyle, FontWeight, HighlightStyle};
+use gpui::{hsla, FontStyle, FontWeight, HighlightStyle, WindowBackgroundAppearance};
use crate::{
default_color_scales, Appearance, PlayerColors, StatusColors, SyntaxTheme, SystemColors, Theme,
@@ -39,8 +39,8 @@ pub(crate) fn one_dark() -> Theme {
id: "one_dark".to_string(),
name: "One Dark".into(),
appearance: Appearance::Dark,
-
styles: ThemeStyles {
+ window_background_appearance: WindowBackgroundAppearance::Opaque,
system: SystemColors::default(),
colors: ThemeColors {
border: hsla(225. / 360., 13. / 100., 12. / 100., 1.),
@@ -122,6 +122,13 @@ impl ThemeRegistry {
AppearanceContent::Light => SyntaxTheme::light(),
AppearanceContent::Dark => SyntaxTheme::dark(),
};
+
+ let window_background_appearance = user_theme
+ .style
+ .window_background_appearance
+ .map(Into::into)
+ .unwrap_or_default();
+
if !user_theme.style.syntax.is_empty() {
syntax_colors.highlights = user_theme
.style
@@ -153,6 +160,7 @@ impl ThemeRegistry {
},
styles: ThemeStyles {
system: SystemColors::default(),
+ window_background_appearance,
colors: theme_colors,
status: status_colors,
player: player_colors,
@@ -1,5 +1,5 @@
use anyhow::Result;
-use gpui::{FontStyle, FontWeight, HighlightStyle, Hsla};
+use gpui::{FontStyle, FontWeight, HighlightStyle, Hsla, WindowBackgroundAppearance};
use indexmap::IndexMap;
use palette::FromColor;
use schemars::gen::SchemaGenerator;
@@ -33,6 +33,25 @@ pub enum AppearanceContent {
Dark,
}
+/// The background appearance of the window.
+#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum WindowBackgroundContent {
+ Opaque,
+ Transparent,
+ Blurred,
+}
+
+impl From<WindowBackgroundContent> for WindowBackgroundAppearance {
+ fn from(value: WindowBackgroundContent) -> Self {
+ match value {
+ WindowBackgroundContent::Opaque => WindowBackgroundAppearance::Opaque,
+ WindowBackgroundContent::Transparent => WindowBackgroundAppearance::Transparent,
+ WindowBackgroundContent::Blurred => WindowBackgroundAppearance::Blurred,
+ }
+ }
+}
+
/// The content of a serialized theme family.
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct ThemeFamilyContent {
@@ -53,6 +72,9 @@ pub struct ThemeContent {
#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
#[serde(default)]
pub struct ThemeStyleContent {
+ #[serde(default, rename = "background.appearance")]
+ pub window_background_appearance: Option<WindowBackgroundContent>,
+
#[serde(flatten, default)]
pub colors: ThemeColorsContent,
@@ -244,6 +244,12 @@ impl ThemeSettings {
if let Some(theme_overrides) = &self.theme_overrides {
let mut base_theme = (*self.active_theme).clone();
+ if let Some(window_background_appearance) = theme_overrides.window_background_appearance
+ {
+ base_theme.styles.window_background_appearance =
+ window_background_appearance.into();
+ }
+
base_theme
.styles
.colors
@@ -1,4 +1,4 @@
-use gpui::Hsla;
+use gpui::{Hsla, WindowBackgroundAppearance};
use refineable::Refineable;
use std::sync::Arc;
@@ -235,6 +235,8 @@ pub struct ThemeColors {
#[derive(Refineable, Clone)]
pub struct ThemeStyles {
+ /// The background appearance of the window.
+ pub window_background_appearance: WindowBackgroundAppearance,
pub system: SystemColors,
/// An array of colors used for theme elements that iterate through a series of colors.
///
@@ -27,7 +27,9 @@ pub use schema::*;
pub use settings::*;
pub use styles::*;
-use gpui::{AppContext, AssetSource, Hsla, SharedString, WindowAppearance};
+use gpui::{
+ AppContext, AssetSource, Hsla, SharedString, WindowAppearance, WindowBackgroundAppearance,
+};
use serde::Deserialize;
#[derive(Debug, PartialEq, Clone, Copy, Deserialize)]
@@ -158,6 +160,12 @@ impl Theme {
pub fn appearance(&self) -> Appearance {
self.appearance
}
+
+ /// Returns the [`WindowBackgroundAppearance`] for the theme.
+ #[inline(always)]
+ pub fn window_background_appearance(&self) -> WindowBackgroundAppearance {
+ self.styles.window_background_appearance
+ }
}
pub fn color_alpha(color: Hsla, alpha: f32) -> Hsla {
@@ -56,6 +56,7 @@ impl VsCodeThemeConverter {
name: self.theme_metadata.name,
appearance,
style: ThemeStyleContent {
+ window_background_appearance: Some(theme::WindowBackgroundContent::Opaque),
colors: theme_colors,
status: status_colors,
players: Vec::new(),
@@ -208,12 +208,21 @@ fn main() {
watch_file_types(fs.clone(), cx);
languages.set_theme(cx.theme().clone());
+
cx.observe_global::<SettingsStore>({
let languages = languages.clone();
let http = http.clone();
let client = client.clone();
move |cx| {
+ for &mut window in cx.windows().iter_mut() {
+ let background_appearance = cx.theme().window_background_appearance();
+ window
+ .update(cx, |_, cx| {
+ cx.set_background_appearance(background_appearance)
+ })
+ .ok();
+ }
languages.set_theme(cx.theme().clone());
let new_host = &client::ClientSettings::get_global(cx).server_url;
if &http.base_url() != new_host {
@@ -34,6 +34,7 @@ use task::{
oneshot_source::OneshotSource,
static_source::{StaticSource, TrackedFile},
};
+use theme::ActiveTheme;
use terminal_view::terminal_panel::{self, TerminalPanel};
use util::{
@@ -104,6 +105,7 @@ pub fn build_window_options(display_uuid: Option<Uuid>, cx: &mut AppContext) ->
is_movable: true,
display_id: display.map(|display| display.id()),
fullscreen: false,
+ window_background: cx.theme().window_background_appearance(),
}
}