T23: the owner's checks on macOS: on macOS 27 gpui's Selection material drew no blur, so blur/macos gives the window's effect view underWindowBackground, radius 30; a button at each end of the title bar hides and shows its column, kept in the settings; shot reads an effect view's blur radius; new comments cut to ten words
This commit is contained in:
12 files changed
+216
-55
No files matched your search
+13
-2
@@ -4,6 +4,8 @@ use gpui::{Window, WindowBackgroundAppearance};
|
||||
|
||||
use crate::settings::Settings;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
mod macos;
|
||||
#[cfg(target_os = "linux")]
|
||||
mod wayland;
|
||||
#[cfg(target_os = "linux")]
|
||||
@@ -32,10 +34,15 @@ pub struct Blur {
|
||||
impl Blur {
|
||||
/// The window as `WindowOptions` opened it from these settings.
|
||||
pub fn new(window: &Window, settings: &Settings) -> Self {
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
let shown = appearance(settings);
|
||||
#[cfg(target_os = "macos")]
|
||||
if shown == WindowBackgroundAppearance::Blurred {
|
||||
macos::blur(window);
|
||||
}
|
||||
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
|
||||
let _ = window;
|
||||
Self {
|
||||
shown: appearance(settings),
|
||||
shown,
|
||||
#[cfg(target_os = "linux")]
|
||||
wayland: wayland::Effect::on(window),
|
||||
#[cfg(target_os = "linux")]
|
||||
@@ -57,6 +64,10 @@ impl Blur {
|
||||
self.shown = wanted;
|
||||
window.set_background_appearance(wanted);
|
||||
log::info!("blur: the window is {wanted:?} on {}", std::env::consts::OS);
|
||||
#[cfg(target_os = "macos")]
|
||||
if wanted == WindowBackgroundAppearance::Blurred {
|
||||
macos::blur(window);
|
||||
}
|
||||
}
|
||||
|
||||
/// Keeps a Linux compositor's blur on the window's pane while Blurred; each frame, sending only a change.
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
use anyhow::bail;
|
||||
use gpui::Window;
|
||||
use objc::{
|
||||
class, msg_send,
|
||||
runtime::{BOOL, Object, YES},
|
||||
sel, sel_impl,
|
||||
};
|
||||
use raw_window_handle::{HasWindowHandle, RawWindowHandle};
|
||||
|
||||
/// `NSVisualEffectMaterialUnderWindowBackground`.
|
||||
const UNDER_WINDOW_BACKGROUND: isize = 21;
|
||||
|
||||
/// Gives gpui's effect view a blurring material; logs either way.
|
||||
pub fn blur(window: &Window) {
|
||||
match effect_view(window) {
|
||||
Ok(view) => {
|
||||
unsafe {
|
||||
let _: () = msg_send![view, setMaterial: UNDER_WINDOW_BACKGROUND];
|
||||
}
|
||||
log::info!(
|
||||
"blur: the window's NSVisualEffectView takes underWindowBackground; gpui's Selection material draws no blur on macOS 27"
|
||||
);
|
||||
}
|
||||
Err(error) => log::error!("blur: the window does not blur: {error:#}"),
|
||||
}
|
||||
}
|
||||
|
||||
/// The NSVisualEffectView gpui puts under the window's content for `Blurred`.
|
||||
fn effect_view(window: &Window) -> anyhow::Result<*mut Object> {
|
||||
let handle = HasWindowHandle::window_handle(window)
|
||||
.map_err(|error| anyhow::anyhow!("no native handle: {error:?}"))?;
|
||||
let RawWindowHandle::AppKit(appkit) = handle.as_raw() else {
|
||||
bail!("not an AppKit window");
|
||||
};
|
||||
unsafe {
|
||||
let view = appkit.ns_view.as_ptr() as *mut Object;
|
||||
let native: *mut Object = msg_send![view, window];
|
||||
let content: *mut Object = msg_send![native, contentView];
|
||||
let views: *mut Object = msg_send![content, subviews];
|
||||
let count: usize = msg_send![views, count];
|
||||
for ix in 0..count {
|
||||
let view: *mut Object = msg_send![views, objectAtIndex: ix];
|
||||
let effect: BOOL = msg_send![view, isKindOfClass: class!(NSVisualEffectView)];
|
||||
if effect == YES {
|
||||
return Ok(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
bail!("gpui put no NSVisualEffectView under the content")
|
||||
}
|
||||
+1
-1
@@ -186,7 +186,7 @@ impl Pty {
|
||||
|
||||
#[cfg(all(test, unix))]
|
||||
impl Pty {
|
||||
/// Whether the program reads keys one by one, as a line editor does.
|
||||
/// Whether the program reads raw keys, canonical mode off.
|
||||
pub(crate) fn reads_keys(&self) -> bool {
|
||||
use rustix_openpty::rustix::termios::{LocalModes, tcgetattr};
|
||||
let modes = tcgetattr(&self.master).expect("the pty's modes");
|
||||
|
||||
@@ -108,7 +108,7 @@ impl Run {
|
||||
}
|
||||
}
|
||||
|
||||
/// Types `line` at a prompt, once its line editor reads raw keys.
|
||||
/// Types `line` once the line editor reads raw keys.
|
||||
fn send(&mut self, line: &str) {
|
||||
self.until("line editor", |run| run.pty.reads_keys());
|
||||
self.pty.write(format!("{line}\r").into_bytes());
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ impl Pty {
|
||||
self.child.id()
|
||||
}
|
||||
|
||||
/// A copy of the master, for tests to read its modes.
|
||||
/// A copy of the master; tests read its modes.
|
||||
#[cfg(test)]
|
||||
pub fn master(&self) -> io::Result<File> {
|
||||
self.file.try_clone()
|
||||
|
||||
+60
-19
@@ -320,14 +320,55 @@ fn show_tab(value: &SharedString, cx: &mut App) {
|
||||
}
|
||||
}
|
||||
|
||||
/// A header button that hides or shows a column, saved.
|
||||
fn column_toggle(
|
||||
workspace: &Entity<Workspace>,
|
||||
(id, icon): (&'static str, IconName),
|
||||
tip: String,
|
||||
flip: fn(&mut Settings),
|
||||
) -> IconButton {
|
||||
let workspace = workspace.clone();
|
||||
IconButton::new(id, icon)
|
||||
.variant(ButtonVariant::Ghost)
|
||||
.size(ControlSize::Sm)
|
||||
.tooltip(tip)
|
||||
.on_click(move |_, _, cx| {
|
||||
cx.stop_propagation();
|
||||
workspace.update(cx, |this, cx| this.toggle(flip, cx));
|
||||
})
|
||||
}
|
||||
|
||||
impl Render for Workspace {
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
self.background.fit(window);
|
||||
let settings = SettingsStore::get(cx);
|
||||
let width = cx.theme().sidebar_width(false);
|
||||
let workspace = cx.entity();
|
||||
let tip =
|
||||
|shown: bool, what: &str| format!("{} {what}", if shown { "Hide" } else { "Show" });
|
||||
// It leads the first column or ends the last.
|
||||
let left = column_toggle(
|
||||
&workspace,
|
||||
("toggle-sessions", IconName::PanelLeft),
|
||||
tip(settings.show_sessions, "sessions"),
|
||||
|settings| settings.show_sessions = !settings.show_sessions,
|
||||
);
|
||||
let right = column_toggle(
|
||||
&workspace,
|
||||
("toggle-panel", IconName::PanelRight),
|
||||
tip(settings.show_panel, "Git and Files"),
|
||||
|settings| settings.show_panel = !settings.show_panel,
|
||||
);
|
||||
let mut shell = ColumnShell::new("workspace").glass();
|
||||
let shown = self
|
||||
.sessions
|
||||
.get(self.active)
|
||||
.map(|session| session.terminal.clone());
|
||||
let mut terminal = ShellColumn::new("terminal")
|
||||
.title(self.title().to_string())
|
||||
.children(shown);
|
||||
if settings.show_sessions {
|
||||
let (workspace, home) = (cx.entity(), dirs::home_dir());
|
||||
let home = dirs::home_dir();
|
||||
let rows = self.sessions.iter().enumerate().map(|(index, session)| {
|
||||
let active = index == self.active;
|
||||
sessions::row(&workspace, index, session, active, home.as_deref(), cx)
|
||||
@@ -342,25 +383,22 @@ impl Render for Workspace {
|
||||
workspace.update(cx, |this, cx| this.new_session(window, cx))
|
||||
});
|
||||
shell = shell.column(
|
||||
ShellColumn::new("sessions").width(width).action(new).child(
|
||||
div()
|
||||
.id("sessions")
|
||||
.size_full()
|
||||
.overflow_y_scroll()
|
||||
.p_2()
|
||||
.child(list),
|
||||
),
|
||||
ShellColumn::new("sessions")
|
||||
.width(width)
|
||||
.leading(left)
|
||||
.action(new)
|
||||
.child(
|
||||
div()
|
||||
.id("sessions")
|
||||
.size_full()
|
||||
.overflow_y_scroll()
|
||||
.p_2()
|
||||
.child(list),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
terminal = terminal.leading(left);
|
||||
}
|
||||
let shown = self
|
||||
.sessions
|
||||
.get(self.active)
|
||||
.map(|session| session.terminal.clone());
|
||||
shell = shell.column(
|
||||
ShellColumn::new("terminal")
|
||||
.title(self.title().to_string())
|
||||
.children(shown),
|
||||
);
|
||||
if settings.show_panel {
|
||||
let tab = settings.panel;
|
||||
let chosen = match tab {
|
||||
@@ -392,14 +430,17 @@ impl Render for Workspace {
|
||||
};
|
||||
// Git's list draws every row, so terminal output must not redraw it.
|
||||
let fill = || StyleRefinement::default().size_full();
|
||||
shell = shell.column(
|
||||
shell = shell.column(terminal).column(
|
||||
ShellColumn::new("panel")
|
||||
.width(width)
|
||||
.leading(switch)
|
||||
.action(right)
|
||||
.children(quiet.then(caption))
|
||||
.child(body(tab == PanelTab::Git).child(self.git.clone().cached(fill())))
|
||||
.child(body(tab == PanelTab::Files).child(self.files.clone())),
|
||||
);
|
||||
} else {
|
||||
shell = shell.column(terminal.action(right));
|
||||
}
|
||||
// The context sits above the focus root, so its keys work wherever focus is.
|
||||
div()
|
||||
|
||||
Reference in new issue
Block a user