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
1 change: 1 addition & 0 deletions apps/desktop-gpui/assets/icons/lock-open.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions apps/desktop-gpui/locales/de.toml
Original file line number Diff line number Diff line change
Expand Up @@ -1482,6 +1482,16 @@ reset_title = "Auswahl zurücksetzen"
reset_body = "Den Bereich löschen, um einen neuen aufzuziehen."
fill_title = "Bildschirm füllen"
fill_body = "Den Bereich auf den ganzen Bildschirm vergrößern und dabei das Seitenverhältnis beibehalten."
fit_window_hint = "Oder klicken Sie auf ein Fenster, um den Bereich daran anzupassen"
drag_to_move = "Zum Verschieben ziehen"
locked = "Gesperrt"
lock_title = "Bereich sperren"
lock_body = "Diesen Bereich für das nächste Mal behalten und vor versehentlichem Ziehen schützen."
lock_needs_area = "Ziehen Sie zuerst einen Bereich auf und sperren Sie ihn dann."
unlock_title = "Bereich entsperren"
unlock_body = "Den Bereich wieder verschieben, in der Größe ändern oder neu aufziehen lassen."
locked_title = "Bereich gesperrt"
locked_body = "Klicken, um ihn zu entsperren, damit er wieder verschoben, in der Größe geändert oder neu aufgezogen werden kann."

[overlay.camera_only]
description = "Nur mit Kamera und Mikrofon aufnehmen"
Expand Down
10 changes: 10 additions & 0 deletions apps/desktop-gpui/locales/en.toml
Original file line number Diff line number Diff line change
Expand Up @@ -1484,6 +1484,16 @@ reset_title = "Reset selection"
reset_body = "Clear the area so you can draw a new one."
fill_title = "Fill display"
fill_body = "Grow the area to cover the display, keeping its ratio."
fit_window_hint = "Or click a window to fit the area to it"
drag_to_move = { text = "Drag to move", context = "Pill in the middle of the selected area while the pointer is over it." }
locked = { text = "Locked", context = "Badge on a locked area, about 60px wide; clicking it unlocks the area." }
lock_title = "Lock area"
lock_body = "Keep this area for next time, and stop a stray drag from changing it."
lock_needs_area = "Draw an area first, then lock it."
unlock_title = "Unlock area"
unlock_body = "Let the area be moved, resized or drawn again."
locked_title = "Area locked"
locked_body = "Click to unlock it, so it can be moved, resized or drawn again."

[overlay.camera_only]
description = "Record using only your camera and microphone"
Expand Down
10 changes: 10 additions & 0 deletions apps/desktop-gpui/locales/es.toml
Original file line number Diff line number Diff line change
Expand Up @@ -1482,6 +1482,16 @@ reset_title = "Restablecer la selección"
reset_body = "Borra el área para dibujar una nueva."
fill_title = "Llenar la pantalla"
fill_body = "Amplía el área hasta cubrir la pantalla manteniendo su proporción."
fit_window_hint = "O haz clic en una ventana para ajustar el área a ella"
drag_to_move = "Arrastra para mover"
locked = "Bloqueada"
lock_title = "Bloquear el área"
lock_body = "Guarda esta área para la próxima vez y evita que un arrastre accidental la cambie."
lock_needs_area = "Primero dibuja un área y luego bloquéala."
unlock_title = "Desbloquear el área"
unlock_body = "Permite volver a mover, redimensionar o dibujar el área."
locked_title = "Área bloqueada"
locked_body = "Haz clic para desbloquearla y poder moverla, redimensionarla o dibujarla de nuevo."

[overlay.camera_only]
description = "Graba solo con tu cámara y tu micrófono"
Expand Down
10 changes: 10 additions & 0 deletions apps/desktop-gpui/locales/fr.toml
Original file line number Diff line number Diff line change
Expand Up @@ -1482,6 +1482,16 @@ reset_title = "Réinitialiser la sélection"
reset_body = "Effacer la zone pour en dessiner une nouvelle."
fill_title = "Remplir l'écran"
fill_body = "Agrandir la zone jusqu'à couvrir l'écran en conservant son format."
fit_window_hint = "Ou cliquez sur une fenêtre pour y ajuster la zone"
drag_to_move = "Faites glisser pour déplacer"
locked = "Verrouillée"
lock_title = "Verrouiller la zone"
lock_body = "Conserver cette zone pour la prochaine fois et éviter qu'un glissement accidentel ne la modifie."
lock_needs_area = "Dessinez d'abord une zone, puis verrouillez-la."
unlock_title = "Déverrouiller la zone"
unlock_body = "Permettre de déplacer, redimensionner ou redessiner la zone."
locked_title = "Zone verrouillée"
locked_body = "Cliquez pour la déverrouiller afin de pouvoir la déplacer, la redimensionner ou la redessiner."

[overlay.camera_only]
description = "Enregistrez uniquement avec votre caméra et votre micro"
Expand Down
10 changes: 10 additions & 0 deletions apps/desktop-gpui/locales/hi.toml
Original file line number Diff line number Diff line change
Expand Up @@ -1482,6 +1482,16 @@ reset_title = "चयन रीसेट करें"
reset_body = "क्षेत्र हटाएँ ताकि आप नया बना सकें।"
fill_title = "पूरा डिस्प्ले भरें"
fill_body = "अनुपात बनाए रखते हुए क्षेत्र को पूरे डिस्प्ले तक बढ़ाएँ।"
fit_window_hint = "या क्षेत्र को किसी विंडो के बराबर करने के लिए उस विंडो पर क्लिक करें"
drag_to_move = "खिसकाने के लिए खींचें"
locked = "लॉक है"
lock_title = "क्षेत्र लॉक करें"
lock_body = "इस क्षेत्र को अगली बार के लिए रखें, और गलती से खींचने पर इसे बदलने से रोकें।"
lock_needs_area = "पहले एक क्षेत्र बनाएँ, फिर उसे लॉक करें।"
unlock_title = "क्षेत्र अनलॉक करें"
unlock_body = "क्षेत्र को फिर से खिसकाने, उसका साइज़ बदलने या नया बनाने दें।"
locked_title = "क्षेत्र लॉक है"
locked_body = "अनलॉक करने के लिए क्लिक करें, ताकि इसे खिसकाया, इसका साइज़ बदला या फिर से बनाया जा सके।"

[overlay.camera_only]
description = "सिर्फ़ अपने कैमरा और माइक्रोफ़ोन से रिकॉर्ड करें"
Expand Down
10 changes: 10 additions & 0 deletions apps/desktop-gpui/locales/ja.toml
Original file line number Diff line number Diff line change
Expand Up @@ -1482,6 +1482,16 @@ reset_title = "選択範囲をリセット"
reset_body = "範囲を消去して、新しく描き直せるようにします。"
fill_title = "ディスプレイ全体に広げる"
fill_body = "比率を保ったまま、範囲をディスプレイ全体に広げます。"
fit_window_hint = "またはウィンドウをクリックして、範囲をそのウィンドウに合わせます"
drag_to_move = "ドラッグして移動"
locked = "ロック中"
lock_title = "範囲をロック"
lock_body = "この範囲を次回のために保持し、誤ってドラッグしても変わらないようにします。"
lock_needs_area = "先に範囲を描いてから、ロックしてください。"
unlock_title = "範囲のロックを解除"
unlock_body = "範囲を再び移動、サイズ変更、描き直しできるようにします。"
locked_title = "範囲はロックされています"
locked_body = "クリックしてロックを解除すると、移動、サイズ変更、描き直しができます。"

[overlay.camera_only]
description = "カメラとマイクだけで録画します"
Expand Down
10 changes: 10 additions & 0 deletions apps/desktop-gpui/locales/zh-CN.toml
Original file line number Diff line number Diff line change
Expand Up @@ -1482,6 +1482,16 @@ reset_title = "重置选区"
reset_body = "清除该区域,以便重新绘制。"
fill_title = "填满屏幕"
fill_body = "保持比例,将区域扩大到整个屏幕。"
fit_window_hint = "或点击一个窗口,让区域与其贴合"
drag_to_move = "拖动以移动"
locked = "已锁定"
lock_title = "锁定区域"
lock_body = "保留此区域供下次使用,并防止误拖动将其改变。"
lock_needs_area = "请先绘制一个区域,然后再锁定。"
unlock_title = "解锁区域"
unlock_body = "允许再次移动、调整大小或重新绘制该区域。"
locked_title = "区域已锁定"
locked_body = "点击即可解锁,以便移动、调整大小或重新绘制。"

[overlay.camera_only]
description = "仅使用摄像头和麦克风录制"
Expand Down
26 changes: 25 additions & 1 deletion apps/desktop-gpui/src/app_windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -582,7 +582,7 @@ pub fn init(main: WindowHandle<MainWindow>, session: Entity<RecordingSession>, c
cx.spawn(async move |cx| {
while escape.recv_async().await.is_ok() {
cx.update(|cx| {
if TargetSelect::global(cx).read(cx).mode.is_some() {
if TargetSelect::global(cx).read(cx).mode.is_some() && !cancel_area_drags(cx) {
dismiss_target_overlays(cx);
}
});
Expand Down Expand Up @@ -2553,6 +2553,30 @@ fn start_recording_from_overlay_now(target: ScreenCaptureTarget, cx: &mut App) {
}
}

/// Escape mid-drag puts the area back rather than closing the picker. True
/// when an overlay had a drag to cancel.
fn cancel_area_drags(cx: &mut App) -> bool {
let overlays = cx.global::<AppWindows>().overlays.clone();
let mut cancelled = false;
for (_, overlay) in overlays {
cancelled |= overlay
.update(cx, |view, _, cx| view.cancel_area_drag(cx))
.unwrap_or(false);
}
cancelled
}

pub fn release_other_area_locks(display: &DisplayId, cx: &mut App) {
let overlays = cx.global::<AppWindows>().overlays.clone();
for (id, overlay) in overlays {
if &id != display {
overlay
.update(cx, |view, _, cx| view.release_area_lock(cx))
.ok();
}
}
}

/// Seed an area selection on the overlay for a display (harness path -- the
/// real one is a mouse drag, and unprivileged synthetic drags are dropped).
pub fn seed_area_selection(display: Option<DisplayId>, crop: AreaRect, cx: &mut App) -> bool {
Expand Down
1 change: 1 addition & 0 deletions apps/desktop-gpui/src/assets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ const ICONS: &[(&str, &[u8])] = assets!("icons":
// (gpui's `svg()` keeps only the alpha).
"circle-check.svg",
"lock.svg",
"lock-open.svg",
// The screenshot editor's annotation toolbar, layers panel and header
// (`AnnotationTools.tsx`, `LayersPanel.tsx`, `Header.tsx`). Lucide 24x24
// originals, matching the `~icons/lucide/*` imports over there.
Expand Down
108 changes: 75 additions & 33 deletions apps/desktop-gpui/src/camera_window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,22 @@ mod inline_preview_tests {
);
}

#[test]
fn a_shrunk_preview_fits_its_toolbar_inside_it() {
// Not shrunk: the size-driven scale and every button.
assert_eq!(toolbar_fit(0.85, None), (0.85, 5));
// Wide enough: unchanged.
assert_eq!(toolbar_fit(0.7, Some(300.)), (0.7, 5));
for width in [81., 100., 120., 144., 160., 200.] {
let (scale, buttons) = toolbar_fit(1., Some(width));
let drawn = (42. * buttons as f32 + 4.) * scale + 2.;
assert!(drawn <= width - 8. + 0.01, "{width}: {drawn}");
assert!(scale >= 0.4);
}
// A portrait sliver keeps close, size and shape.
assert_eq!(toolbar_fit(1., Some(81.)).1, 3);
}

#[test]
fn camera_only_wide_preview_preserves_aspect_and_toolbar_space() {
let state = CameraWindowState {
Expand All @@ -157,6 +173,25 @@ fn normalized_size(state: &CameraWindowState) -> f32 {
(clamp_size(state.size) - CAMERA_MIN_SIZE) / (CAMERA_MAX_SIZE - CAMERA_MIN_SIZE)
}

/// The fit uses each button's footprint (a 22px icon in 8px padding, 4px
/// apart, in a 4px-padded strip with a 1px border). The preview-only blur,
/// then mirror, buttons go first, before the rest get too small to hit.
fn toolbar_fit(base_scale: f32, width: Option<f32>) -> (f32, usize) {
const SMALLEST_COMFORTABLE: f32 = 0.6;
const SMALLEST: f32 = 0.4;
let Some(width) = width else {
return (base_scale, 5);
};
let fit = |buttons: usize| (width - 10.) / (42. * buttons as f32 + 4.);
for buttons in [5, 4] {
let scale = base_scale.min(fit(buttons));
if scale >= SMALLEST_COMFORTABLE {
return (scale, buttons);
}
}
(base_scale.min(fit(3)).max(SMALLEST), 3)
}

/// The visible bubble's corner radius. The shipped Tauri experience is the
/// legacy webview preview (`enable_native_camera_preview` defaults to false,
/// re-forced by the `native_camera_preview_default_rollback_v1` migration,
Expand Down Expand Up @@ -2202,8 +2237,11 @@ impl CameraWindow {
.or(self.retained_aspect)
}

fn toolbar_scale(&self) -> f32 {
0.7 + normalized_size(&self.state) * 0.3
fn toolbar_fit(&self) -> (f32, usize) {
toolbar_fit(
0.7 + normalized_size(&self.state) * 0.3,
self.picker_size.map(|(width, _)| width),
)
}

/// Pushes the chrome inputs the preview renders with (its own notify is
Expand Down Expand Up @@ -2454,7 +2492,7 @@ impl CameraWindow {

fn render_toolbar(&self, cx: &mut Context<Self>) -> impl IntoElement {
let theme = self.theme;
let scale = self.toolbar_scale();
let (scale, buttons) = self.toolbar_fit();
#[cfg(target_os = "macos")]
let (mirror_failed, blur_failed) = (self.effect_failures.mirror, self.effect_failures.blur);
#[cfg(not(target_os = "macos"))]
Expand Down Expand Up @@ -2533,36 +2571,40 @@ impl CameraWindow {
// Preview-only, like both Tauri paths (the WGSL UV flip /
// the legacy `scaleX(-1)`); the recorded camera track is
// never mirrored by either app.
.child(self.toolbar_button(
"mirror",
"icons/arrows.svg",
self.state.mirrored && !mirror_failed,
scale,
mirror_failed.then_some("!"),
cx,
|this, window, cx| {
this.mutate_state(window, cx, |state| {
state.mirrored = !state.mirrored;
})
},
))
.child(self.toolbar_button(
"blur",
"icons/person-standing.svg",
self.state.background_blur != BlurMode::Off && !blur_failed,
scale,
if blur_failed {
Some("!")
} else {
self.state.background_blur.label()
},
cx,
|this, window, cx| {
this.mutate_state(window, cx, |state| {
state.background_blur = state.background_blur.cycle();
})
},
)),
.when(buttons >= 4, |this| {
this.child(self.toolbar_button(
"mirror",
"icons/arrows.svg",
self.state.mirrored && !mirror_failed,
scale,
mirror_failed.then_some("!"),
cx,
|this, window, cx| {
this.mutate_state(window, cx, |state| {
state.mirrored = !state.mirrored;
})
},
))
})
.when(buttons >= 5, |this| {
this.child(self.toolbar_button(
"blur",
"icons/person-standing.svg",
self.state.background_blur != BlurMode::Off && !blur_failed,
scale,
if blur_failed {
Some("!")
} else {
self.state.background_blur.label()
},
cx,
|this, window, cx| {
this.mutate_state(window, cx, |state| {
state.background_blur = state.background_blur.cycle();
})
},
))
}),
)
}

Expand Down
30 changes: 29 additions & 1 deletion apps/desktop-gpui/src/i18n/dialogs_surface_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,31 @@ fn dialog_surfaces_match_the_literals_they_replaced() {
tr::overlay::area::fill_body(),
"Grow the area to cover the display, keeping its ratio.",
),
(
tr::overlay::area::fit_window_hint(),
"Or click a window to fit the area to it",
),
(tr::overlay::area::drag_to_move(), "Drag to move"),
(tr::overlay::area::locked(), "Locked"),
(tr::overlay::area::lock_title(), "Lock area"),
(
tr::overlay::area::lock_body(),
"Keep this area for next time, and stop a stray drag from changing it.",
),
(
tr::overlay::area::lock_needs_area(),
"Draw an area first, then lock it.",
),
(tr::overlay::area::unlock_title(), "Unlock area"),
(
tr::overlay::area::unlock_body(),
"Let the area be moved, resized or drawn again.",
),
(tr::overlay::area::locked_title(), "Area locked"),
(
tr::overlay::area::locked_body(),
"Click to unlock it, so it can be moved, resized or drawn again.",
),
(tr::overlay::window::size_title(), "Change window size"),
(
tr::overlay::window::size_body(),
Expand All @@ -374,7 +399,10 @@ fn dialog_surfaces_match_the_literals_they_replaced() {
"Wayland doesn't let apps resize other apps' windows.",
),
(tr::overlay::window::focus_title(), "Focus mode"),
(tr::overlay::window::focus_on(), "Recording only this window"),
(
tr::overlay::window::focus_on(),
"Recording only this window",
),
(tr::overlay::window::focus_off(), "Record only this window"),
(tr::overlay::window::focus_enable(), "Enable Focus mode"),
(tr::overlay::window::focus_disable(), "Turn off Focus mode"),
Expand Down
Loading
Loading