mirror of
https://github.com/l0ng-ai/tty7.git
synced 2026-09-21 08:02:21 +00:00
Merge pull request #864 from hhdebb/fix/hide-the-title-bar-in-fullscreen
fix(window): drop the dead window buttons from the title bar in fullscreen
This commit is contained in:
+125
-10
@@ -1167,6 +1167,31 @@ fn clear_window_override_values(config: &mut Config, backdrop_is_local: bool) {
|
||||
}
|
||||
}
|
||||
|
||||
/// The id the fullscreen hint is pushed under, so that entering again replaces
|
||||
/// it and leaving takes it away.
|
||||
struct FullscreenHint;
|
||||
|
||||
/// Whether the title bar carries minimize, maximize and close right now.
|
||||
///
|
||||
/// Not in fullscreen. A fullscreen window has no caption: Windows clears
|
||||
/// `WS_CAPTION` and answers `HTCLIENT` along the whole top edge, so the three
|
||||
/// buttons would draw, light up under the pointer and do nothing when clicked.
|
||||
/// The row they sit at the end of stays, because it is also the tab strip.
|
||||
///
|
||||
/// Never on macOS, which draws no buttons of its own: those are the system's
|
||||
/// traffic lights, and the system hides them itself.
|
||||
pub(crate) fn window_controls_drawn(fullscreen: bool) -> bool {
|
||||
!cfg!(target_os = "macos") && !fullscreen
|
||||
}
|
||||
|
||||
/// How much of the title bar's trailing end the window buttons take.
|
||||
pub(crate) fn window_controls_w(fullscreen: bool) -> f32 {
|
||||
match window_controls_drawn(fullscreen) {
|
||||
true => WINDOW_CONTROLS_W,
|
||||
false => 0.,
|
||||
}
|
||||
}
|
||||
|
||||
impl Tty7App {
|
||||
pub fn for_workspace(
|
||||
id: Option<WorkspaceId>,
|
||||
@@ -3530,6 +3555,46 @@ impl Tty7App {
|
||||
remember_leaf_in(&mut self.tabs, leaf);
|
||||
}
|
||||
|
||||
/// Toggle fullscreen, and say how to leave it on the way in.
|
||||
///
|
||||
/// Only on the way in, and only from the action: entering is an instant in
|
||||
/// which the window buttons disappear, and a window that starts fullscreen
|
||||
/// because the setting says so is not a surprise anybody needs explaining.
|
||||
/// The chord comes from the keymap rather than from a string, because it is
|
||||
/// `F11` on Windows and Linux, `Cmd+Enter` on macOS, and either of them may
|
||||
/// have been rebound.
|
||||
///
|
||||
/// The hint carries an id of its own, which is what keeps a held-down
|
||||
/// `F11` to one notice rather than a column of identical ones: pushing
|
||||
/// under an id already on screen replaces that one. Leaving through the
|
||||
/// action takes it back too, so a quick in-and-out does not leave the way
|
||||
/// out on screen after it has been taken. Leaving some other way — a
|
||||
/// window manager with a chord of its own — just lets it time out, which
|
||||
/// is a second or two of a stale notice and not worth watching every
|
||||
/// frame for.
|
||||
fn toggle_fullscreen(&self, window: &mut Window, cx: &mut App) {
|
||||
let entering = !window.is_fullscreen();
|
||||
window.toggle_fullscreen();
|
||||
window.remove_notification::<FullscreenHint>(cx);
|
||||
// Nothing disappeared where there were no buttons to begin with, so
|
||||
// there is nothing to explain.
|
||||
if !entering || !window_controls_drawn(false) {
|
||||
return;
|
||||
}
|
||||
let hint = match crate::ui::home::key_hint("ToggleFullscreen", cx) {
|
||||
Some(chord) => t_fmt(L10nKey::AppFullscreenEntered, &[("key", &chord)]),
|
||||
// Rebound to nothing at all: still worth saying the buttons are gone,
|
||||
// just without naming a key that would not work.
|
||||
None => t(L10nKey::AppFullscreenEnteredNoKey).to_string(),
|
||||
};
|
||||
window.push_notification(
|
||||
gpui_component::notification::Notification::new()
|
||||
.id::<FullscreenHint>()
|
||||
.message(hint),
|
||||
cx,
|
||||
);
|
||||
}
|
||||
|
||||
fn focus_leaf(&self, leaf: &PaneSlot, window: &mut Window, cx: &mut App) {
|
||||
let handle = leaf.focus_handle(cx);
|
||||
window.focus(&handle, cx);
|
||||
@@ -5362,7 +5427,7 @@ impl Tty7App {
|
||||
NextTab => self.cycle_tab(true, window, cx),
|
||||
PrevTab => self.cycle_tab(false, window, cx),
|
||||
ToggleMaximizePane => self.toggle_maximize(window, cx),
|
||||
ToggleFullscreen => window.toggle_fullscreen(),
|
||||
ToggleFullscreen => self.toggle_fullscreen(window, cx),
|
||||
ToggleTabSidebar => self.toggle_tab_sidebar(cx),
|
||||
ToggleLeftPanel => self.toggle_left_panel(cx),
|
||||
ToggleRightPanel => self.toggle_right_panel(cx),
|
||||
@@ -7629,11 +7694,41 @@ impl Render for Tty7App {
|
||||
}
|
||||
};
|
||||
|
||||
let title_bar = TitleBar::new()
|
||||
.h(px(TITLE_BAR_HEIGHT))
|
||||
.bg(cx.theme().transparent)
|
||||
.border_color(cx.theme().transparent)
|
||||
.child(strip);
|
||||
// No window buttons in fullscreen, where they cannot work: the window
|
||||
// has no caption for the platform to hit-test, so they would draw,
|
||||
// light up under the pointer and do nothing when clicked. `TitleBar`
|
||||
// always draws them, so the strip goes into a plain row of the same
|
||||
// geometry instead — the row itself stays, since it holds the tabs,
|
||||
// the chrome tiles and the docked document's header.
|
||||
let title_bar =
|
||||
if window_controls_drawn(window.is_fullscreen()) || cfg!(target_os = "macos") {
|
||||
TitleBar::new()
|
||||
.h(px(TITLE_BAR_HEIGHT))
|
||||
.bg(cx.theme().transparent)
|
||||
.border_color(cx.theme().transparent)
|
||||
.child(strip)
|
||||
.into_any_element()
|
||||
} else {
|
||||
div()
|
||||
.flex_shrink_0()
|
||||
.flex()
|
||||
.flex_row()
|
||||
.items_center()
|
||||
.h(px(TITLE_BAR_HEIGHT))
|
||||
.pl(px(TITLE_BAR_LEAD))
|
||||
.border_b_1()
|
||||
.border_color(cx.theme().transparent)
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.flex_row()
|
||||
.items_center()
|
||||
.h_full()
|
||||
.flex_1()
|
||||
.child(strip),
|
||||
)
|
||||
.into_any_element()
|
||||
};
|
||||
let body_area = div()
|
||||
.flex_1()
|
||||
.relative()
|
||||
@@ -7835,7 +7930,9 @@ impl Render for Tty7App {
|
||||
.right(px(panel_px))
|
||||
.w(px(document_px))
|
||||
.when(panel_px <= 0., |d| {
|
||||
d.pr(px(crate::ui::tab_strip::trailing_chrome_w()))
|
||||
d.pr(px(crate::ui::tab_strip::trailing_chrome_w(
|
||||
window.is_fullscreen(),
|
||||
)))
|
||||
})
|
||||
.child(header),
|
||||
)
|
||||
@@ -8032,9 +8129,9 @@ impl Render for Tty7App {
|
||||
.on_action(cx.listener(|this, _: &ToggleMaximizePane, window, cx| {
|
||||
this.toggle_maximize(window, cx)
|
||||
}))
|
||||
.on_action(
|
||||
cx.listener(|_, _: &ToggleFullscreen, window, _cx| window.toggle_fullscreen()),
|
||||
)
|
||||
.on_action(cx.listener(|this, _: &ToggleFullscreen, window, cx| {
|
||||
this.toggle_fullscreen(window, cx)
|
||||
}))
|
||||
.on_action(cx.listener(|this, _: &ToggleTabSidebar, _window, cx| {
|
||||
this.toggle_tab_sidebar(cx)
|
||||
}))
|
||||
@@ -9421,6 +9518,24 @@ mod tests {
|
||||
assert_eq!(band.size.height, px(TITLE_BAR_HEIGHT));
|
||||
}
|
||||
|
||||
/// Fullscreen takes the window buttons and nothing else: the strip keeps
|
||||
/// its row, and the room reserved for the buttons at its end comes back.
|
||||
/// macOS never had any to take.
|
||||
#[test]
|
||||
fn fullscreen_drops_the_window_buttons_but_not_their_row() {
|
||||
assert!(!super::window_controls_drawn(true));
|
||||
assert_eq!(super::window_controls_w(true), 0.);
|
||||
assert_eq!(
|
||||
super::window_controls_drawn(false),
|
||||
!cfg!(target_os = "macos")
|
||||
);
|
||||
assert_eq!(super::window_controls_w(false), super::WINDOW_CONTROLS_W);
|
||||
assert_eq!(
|
||||
crate::ui::tab_strip::trailing_chrome_w(true),
|
||||
crate::ui::tab_strip::trailing_chrome_tiles_w()
|
||||
);
|
||||
}
|
||||
|
||||
/// A surface narrower than its own shadow is only reachable mid-resize, but
|
||||
/// a negative width would make `Bounds::contains` answer for a rectangle
|
||||
/// that is inside out.
|
||||
|
||||
@@ -1597,6 +1597,10 @@ pub fn translate_en(key: L10nKey) -> &'static str {
|
||||
L10nKey::AppReopenTabFailed => "Could not reopen the tab: no terminal started",
|
||||
L10nKey::AppOpenTerminalFailed => "Could not open a terminal: {error}",
|
||||
L10nKey::AppTabsNotRestored => "{count} tabs from last time could not be reopened",
|
||||
L10nKey::AppFullscreenEntered => "Fullscreen — press {key} to leave",
|
||||
L10nKey::AppFullscreenEnteredNoKey => {
|
||||
"Fullscreen — the window buttons are hidden until you leave"
|
||||
}
|
||||
L10nKey::LaunchWorkspacesLeftRunning => {
|
||||
"Only this window was restored — {count} workspaces are still running in the background. Reopen them from the sidebar."
|
||||
}
|
||||
|
||||
@@ -1662,6 +1662,10 @@ pub fn translate_ja(key: L10nKey) -> Option<&'static str> {
|
||||
L10nKey::AppReopenTabFailed => "タブを開き直せませんでした: ターミナルが起動しませんでした",
|
||||
L10nKey::AppOpenTerminalFailed => "ターミナルを開けませんでした: {error}",
|
||||
L10nKey::AppTabsNotRestored => "前回のタブ {count} 個を開き直せませんでした",
|
||||
L10nKey::AppFullscreenEntered => "全画面表示 — 解除するには {key}",
|
||||
L10nKey::AppFullscreenEnteredNoKey => {
|
||||
"全画面表示 — 解除するまでウィンドウボタンは非表示です"
|
||||
}
|
||||
L10nKey::LaunchWorkspacesLeftRunning => {
|
||||
"このウィンドウだけを復元しました — あと {count} 個のワークスペースがバックグラウンドで実行中です。サイドバーから開き直せます。"
|
||||
}
|
||||
|
||||
@@ -1290,6 +1290,8 @@ l10n_keys! {
|
||||
AppReopenTabFailed,
|
||||
AppOpenTerminalFailed,
|
||||
AppTabsNotRestored,
|
||||
AppFullscreenEntered,
|
||||
AppFullscreenEnteredNoKey,
|
||||
LaunchWorkspacesLeftRunning,
|
||||
AppSshConnectionFailed,
|
||||
AppSshReconnectFailed,
|
||||
|
||||
@@ -1507,6 +1507,8 @@ pub fn translate_zh(key: L10nKey) -> Option<&'static str> {
|
||||
L10nKey::AppReopenTabFailed => "无法重新打开标签页:没有启动终端",
|
||||
L10nKey::AppOpenTerminalFailed => "无法打开终端:{error}",
|
||||
L10nKey::AppTabsNotRestored => "上次的 {count} 个标签页没能重新打开",
|
||||
L10nKey::AppFullscreenEntered => "已进入全屏 —— 按 {key} 退出",
|
||||
L10nKey::AppFullscreenEnteredNoKey => "已进入全屏 —— 窗口按钮在退出前会一直隐藏",
|
||||
L10nKey::LaunchWorkspacesLeftRunning => {
|
||||
"只恢复了这个窗口——还有 {count} 个工作区在后台运行,可从侧边栏重新打开。"
|
||||
}
|
||||
|
||||
+7
-6
@@ -626,8 +626,8 @@ pub(crate) fn trailing_chrome_tiles_w() -> f32 {
|
||||
/// Anything else drawn into that end of the title bar has to stop short of it —
|
||||
/// which for the hoisted document header means the case where the detail panel
|
||||
/// is closed and the document column runs to the window's right edge.
|
||||
pub(crate) fn trailing_chrome_w() -> f32 {
|
||||
trailing_chrome_tiles_w() + crate::ui::app::WINDOW_CONTROLS_W
|
||||
pub(crate) fn trailing_chrome_w(fullscreen: bool) -> f32 {
|
||||
trailing_chrome_tiles_w() + crate::ui::app::window_controls_w(fullscreen)
|
||||
}
|
||||
|
||||
pub(crate) fn chrome_tile_sized(
|
||||
@@ -1812,14 +1812,15 @@ impl Tty7App {
|
||||
// instead: that header carries no fill of its own, and a chip left
|
||||
// under it showed through the file name while staying clickable.
|
||||
let document_w = self.document_dock_px(window, cx).unwrap_or(0.);
|
||||
let controls_w = crate::ui::app::window_controls_w(window.is_fullscreen());
|
||||
let strip_w = if cfg!(target_os = "macos") {
|
||||
(window.viewport_size().width - px(80. + panel_w + document_w)).max(px(160.))
|
||||
} else {
|
||||
(window.viewport_size().width - px(114.)).max(px(140.))
|
||||
(window.viewport_size().width - px(crate::ui::app::TITLE_BAR_LEAD + controls_w))
|
||||
.max(px(140.))
|
||||
};
|
||||
let chrome_band_w = (!cfg!(target_os = "macos") && self.right_panel_open(cx)).then(|| {
|
||||
(self.right_panel_px(window, cx) - crate::ui::app::WINDOW_CONTROLS_W - 1.).max(0.)
|
||||
});
|
||||
let chrome_band_w = (!cfg!(target_os = "macos") && self.right_panel_open(cx))
|
||||
.then(|| (self.right_panel_px(window, cx) - controls_w - 1.).max(0.));
|
||||
// `corner_w` reserves the trailing window chrome. With the panel open on
|
||||
// macOS that chrome belongs to the panel's own header, which the strip
|
||||
// now stops short of, so reserving for it here would charge the chips
|
||||
|
||||
Reference in New Issue
Block a user