Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 41 additions & 2 deletions apps/desktop-gpui/src/app_windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ use crate::{
/// `fake_window.rs` position math.
pub const CONTROLS_WIDTH: f32 = 320.;
pub const CONTROLS_HEIGHT: f32 = 150.;
/// The bar alone, with its padding. A transparent window takes every click
/// inside its frame, so without an issue to show the window is only this
/// tall and the app under the empty space above the bar stays clickable.
pub const CONTROLS_BAR_HEIGHT: f32 = 64.;
const CONTROLS_FIT_TO_ISSUE: bool = cfg!(any(target_os = "macos", windows));
const CONTROLS_BOTTOM_OFFSET: f64 = 120.;
const TARGET_CONTROLS_OFFSET_Y: f64 = 48.;

Expand Down Expand Up @@ -6898,6 +6903,35 @@ fn controls_origin(config: &StartConfig) -> (f64, f64) {
}
}

/// Grow the bar's window upward to make room for an issue card above the
/// bar, or shrink it back to the bar alone, leaving the bar where it is.
/// `setFrame` re-enters gpui, so call this from a task.
pub(crate) fn fit_controls_to_issue(native: &platform::NativeWindow, issue: bool, scale: f32) {
let height = f64::from(if issue {
CONTROLS_HEIGHT
} else {
CONTROLS_BAR_HEIGHT
});
let (x, y, width, current) = platform::window_frame(native);
if width <= 0. {
return;
}
// AppKit frames have a bottom-left origin: a new height keeps the bottom.
#[cfg(target_os = "macos")]
{
let _ = (current, scale);
platform::set_window_frame(native, x, y, width, height);
}
// Windows frames are physical pixels from the top-left.
#[cfg(windows)]
{
let height = height * f64::from(scale);
platform::set_window_frame(native, x, y + current - height, width, height);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expanding controls near the top clips the issue card

Medium Severity

On Windows at 100% scaling, a compact controls window at y=16 has its bottom at y=80. When a storage warning or recording error appears, this resize moves its top to -70, placing much of the issue card off-screen. This position is reachable by dragging the bar or by the initial work-area clamp for a short recording target near the screen's top. Previously the entire 150-point window was clamped into the work area. Clamp the expanded frame to the current display's work area, allowing the bar to move when necessary to keep the warning visible.

Open in Capy

}
#[cfg(not(any(target_os = "macos", windows)))]
let _ = (native, x, y, current, height, scale);
}

/// Open the bar. Returns the native window number for capture exclusion.
fn open_controls(
config: &StartConfig,
Expand All @@ -6909,9 +6943,14 @@ fn open_controls(
return None;
}
let (x, y) = controls_origin(config);
let height = if CONTROLS_FIT_TO_ISSUE {
CONTROLS_BAR_HEIGHT
} else {
CONTROLS_HEIGHT
};
let bounds = Bounds {
origin: point(px(x as f32), px(y as f32)),
size: size(px(CONTROLS_WIDTH), px(CONTROLS_HEIGHT)),
origin: point(px(x as f32), px(y as f32 + CONTROLS_HEIGHT - height)),
size: size(px(CONTROLS_WIDTH), px(height)),
};
let display = config
.target
Expand Down
37 changes: 31 additions & 6 deletions apps/desktop-gpui/src/controls_window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ pub struct ControlsWindow {
_tick: gpui::Task<()>,
ticking_live: bool,
level_bar: Entity<ui::MicrophoneLevel>,
issue_room: bool,
}

#[derive(Clone, Copy)]
Expand Down Expand Up @@ -63,6 +64,7 @@ impl ControlsWindow {
crate::theme::bind_window(window, cx);
let theme = Theme::for_window(window, cx, false);
cx.observe_in(&session, window, |this, _, window, cx| {
this.fit_to_issue(window, cx);
let live = this.timer_is_live(cx);
if live != this.ticking_live {
this.ticking_live = live;
Expand Down Expand Up @@ -90,12 +92,40 @@ impl ControlsWindow {
_tick: gpui::Task::ready(()),
ticking_live: false,
level_bar,
issue_room: false,
};
this.ticking_live = this.timer_is_live(cx);
this._tick = Self::spawn_tick(window, cx);
// Once the platform window is finished: an issue may already be up.
cx.defer_in(window, |this, window, cx| this.fit_to_issue(window, cx));
this
}

fn issue(&self, cx: &gpui::App) -> Option<String> {
let session = self.session.read(cx);
session.error.clone().or_else(|| {
session
.storage_warning
.then(|| tr::controls::low_storage().to_owned())
})
}

fn fit_to_issue(&mut self, window: &mut Window, cx: &mut Context<Self>) {
let issue = self.issue(cx).is_some();
if issue == self.issue_room {
return;
}
self.issue_room = issue;
let Some(native) = crate::platform::native_window(window) else {
return;
};
let scale = window.scale_factor();
cx.spawn(async move |_, _| {
crate::app_windows::fit_controls_to_issue(&native, issue, scale);
})
.detach();
}

fn timer_is_live(&self, cx: &gpui::App) -> bool {
matches!(
self.session.read(cx).phase,
Expand Down Expand Up @@ -511,12 +541,7 @@ impl ControlsWindow {
impl Render for ControlsWindow {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
self.theme.refresh(window, cx, false);
let session = self.session.read(cx);
let issue = session.error.clone().or_else(|| {
session
.storage_warning
.then(|| tr::controls::low_storage().to_owned())
});
let issue = self.issue(cx);

div()
.size_full()
Expand Down
Loading