From 531bd49c23f16a7f93086e483c19e77e4c718ca2 Mon Sep 17 00:00:00 2001 From: Daniel Berlin Date: Fri, 6 Oct 2023 17:04:58 -0400 Subject: [PATCH 1/9] Add support for stretching tab bar to fill width of tab bar Resolves #1914 --- config/src/config.rs | 4 + docs/config/appearance.md | 2 + docs/config/lua/config/tab_bar_title_fill.md | 15 +++ wezterm-gui/src/tabbar.rs | 23 ++-- wezterm-gui/src/termwindow/box_model.rs | 100 ++++++++++++++---- .../src/termwindow/render/fancy_tab_bar.rs | 25 +++-- 6 files changed, 132 insertions(+), 37 deletions(-) create mode 100644 docs/config/lua/config/tab_bar_title_fill.md diff --git a/config/src/config.rs b/config/src/config.rs index 5b099edc7ad..0fa5e2b68f7 100644 --- a/config/src/config.rs +++ b/config/src/config.rs @@ -474,6 +474,10 @@ pub struct Config { #[dynamic(default)] pub tab_and_split_indices_are_zero_based: bool, + /// Specifies to fill the width of the window with the tab bar + #[dynamic(default)] + pub tab_bar_fill: bool, + /// Specifies the maximum width that a tab can have in the /// tab bar. Defaults to 16 glyphs in width. #[dynamic(default = "default_tab_max_width")] diff --git a/docs/config/appearance.md b/docs/config/appearance.md index bd8955f8864..445bdac4f62 100644 --- a/docs/config/appearance.md +++ b/docs/config/appearance.md @@ -277,6 +277,8 @@ details. bar at the bottom of the window instead of the top * [tab_max_width](lua/config/tab_max_width.md) sets the maximum width, measured in cells, of a given tab when using retro tab mode. +* [tab_bar_fill](lua/config/tab_bar_fill.md) sets the fancy tab bar to fill + the width of the title bar. #### Native (Fancy) Tab Bar appearance diff --git a/docs/config/lua/config/tab_bar_title_fill.md b/docs/config/lua/config/tab_bar_title_fill.md new file mode 100644 index 00000000000..195e2593d6d --- /dev/null +++ b/docs/config/lua/config/tab_bar_title_fill.md @@ -0,0 +1,15 @@ +--- +tags: + - tab_bar +--- +# `tab_bar_fill` + +Specifies that the fancy tab bar should allow tab titles +to take up the entire width of the tab bar. +In this mode, maximum tab width is ignored. + +Defaults to false. + +```lua +config.tab_bar_fill = true +``` diff --git a/wezterm-gui/src/tabbar.rs b/wezterm-gui/src/tabbar.rs index 5e007f54a42..6f62e968caf 100644 --- a/wezterm-gui/src/tabbar.rs +++ b/wezterm-gui/src/tabbar.rs @@ -324,6 +324,14 @@ impl TabBarState { let mut active_tab_no = 0; + let config_tab_max_width = if config.tab_bar_fill { + // We have no layout, so this is a rough estimate + // The tab bar consists of the tab titles, the new tab button, and some padding + title_width.saturating_sub(new_tab.len() + 2 + tab_info.len()) / (tab_info.len()) + } else { + config.tab_max_width + }; + let tab_titles: Vec = if config.show_tabs_in_tab_bar { tab_info .iter() @@ -337,7 +345,7 @@ impl TabBarState { pane_info, config, false, - config.tab_max_width, + config_tab_max_width, ) }) .collect() @@ -346,18 +354,16 @@ impl TabBarState { }; let titles_len: usize = tab_titles.iter().map(|s| s.len).sum(); let number_of_tabs = tab_titles.len(); - let available_cells = title_width.saturating_sub(number_of_tabs.saturating_sub(1) + new_tab.len()); let tab_width_max = if config.use_fancy_tab_bar || available_cells >= titles_len { // We can render each title with its full width - usize::max_value() + usize::MAX } else { // We need to clamp the length to balance them out available_cells / number_of_tabs } - .min(config.tab_max_width); - + .min(config_tab_max_width); let mut line = Line::with_width(0, SEQ_ZERO); let mut x = 0; @@ -400,9 +406,10 @@ impl TabBarState { } for (tab_idx, tab_title) in tab_titles.iter().enumerate() { - let tab_title_len = tab_title.len.min(tab_width_max); + // The title is allowed to grow to the max size of the computed tab width + let tab_title_max_len = tab_title.len.max(tab_width_max).min(tab_width_max); let active = tab_idx == active_tab_no; - let hover = !active && is_tab_hover(mouse_x, x, tab_title_len); + let hover = !active && is_tab_hover(mouse_x, x, tab_title_max_len); // Recompute the title so that it factors in both the hover state // and the adjusted maximum tab width based on available space. @@ -412,7 +419,7 @@ impl TabBarState { pane_info, config, hover, - tab_title_len, + tab_title_max_len, ); let cell_attrs = if active { diff --git a/wezterm-gui/src/termwindow/box_model.rs b/wezterm-gui/src/termwindow/box_model.rs index 5f0c046123a..64be2f6ea49 100644 --- a/wezterm-gui/src/termwindow/box_model.rs +++ b/wezterm-gui/src/termwindow/box_model.rs @@ -239,6 +239,7 @@ pub struct Element { pub max_width: Option, pub min_width: Option, pub min_height: Option, + pub fill_width: bool, } impl Element { @@ -262,6 +263,7 @@ impl Element { max_width: None, min_width: None, min_height: None, + fill_width: false, } } @@ -689,8 +691,8 @@ impl super::TermWindow { let mut block_pixel_height: f32 = 0.; let mut computed_kids = vec![]; let mut max_x: f32 = 0.; - let mut float_width: f32 = 0.; let mut y_coord: f32 = 0.; + let mut filled_layout_contexts = Vec::new(); for child in kids { if child.display == DisplayType::Block { @@ -713,40 +715,39 @@ impl super::TermWindow { context.bounds.max_y() - (context.bounds.min_y() + y_coord), ), }; - let kid = self.compute_element( - &LayoutContext { - bounds, - gl_state: context.gl_state, - height: context.height, - metrics: context.metrics, - width: DimensionContext { - dpi: context.width.dpi, - pixel_cell: context.width.pixel_cell, - pixel_max: max_width, - }, - zindex: context.zindex + element.zindex, + let layout_context = LayoutContext { + bounds, + gl_state: context.gl_state, + height: context.height, + metrics: context.metrics, + width: DimensionContext { + dpi: context.width.dpi, + pixel_cell: context.width.pixel_cell, + pixel_max: max_width, }, - child, - )?; + zindex: context.zindex + element.zindex, + }; + let kid = self.compute_element(&layout_context, child)?; match child.float { - Float::Right => { - float_width += float_width.max(kid.bounds.width()); - } Float::None => { block_pixel_width += kid.bounds.width(); max_x = max_x.max(block_pixel_width); } + // Float right is taken care of below + _ => {} + } + if child.fill_width { + filled_layout_contexts.push(layout_context); } block_pixel_height = block_pixel_height.max(kid.bounds.height()); - computed_kids.push(kid); } // Respect min-width max_x = max_x.max(min_width); - let mut float_max_x = (max_x + float_width).min(max_width); - + // Right floated things start at the right edge, and move left as needed + let mut float_max_x = max_width; let pixel_height = (y_coord + block_pixel_height).max(min_height); for (kid, child) in computed_kids.iter_mut().zip(kids.iter()) { @@ -773,6 +774,63 @@ impl super::TermWindow { } } + // We have to compute fill after all elements have been floated + if filled_layout_contexts.len() > 0 { + // This mechanism assumes that we don't mix float right and + // fill width elements and try to have it work That is, we + // assume all float right elements are all the way to the + // right, in a row, and are not marked to be filled + let mut available_min_right_x = context.bounds.max_x(); + let mut static_element_size = 0.; + for (kid, child) in computed_kids.iter_mut().zip(kids.iter()) { + // Subtract out the space from non-filling, non right-floating elements + if !child.fill_width { + match child.float { + Float::Right => { + available_min_right_x = + available_min_right_x.min(kid.bounds.min_x()); + } + Float::None => { + static_element_size += kid.bounds.width(); + } + } + } + } + let mut current_kid = 0; + + // We only fill from the leftmost point to the leftmost point of the first float right element. + // We assume we have all the space not taken up by non-filled elements. + let available_space = available_min_right_x - static_element_size; + // Evenly distribute remaining space + let new_width = available_space / filled_layout_contexts.len() as f32; + // We know there is at least one kid or we would not be in this loop + let mut new_origin = computed_kids.first().unwrap().bounds.origin.x; + for (kid, child) in computed_kids.iter_mut().zip(kids.iter()) { + let old_bounds = kid.bounds; + if child.fill_width { + // Recompute + let layout_context = &mut filled_layout_contexts[current_kid]; + current_kid = current_kid + 1; + layout_context.width = DimensionContext { + dpi: context.width.dpi, + pixel_cell: context.width.pixel_cell, + pixel_max: max_width, + }; + layout_context.bounds = euclid::rect( + new_origin, + old_bounds.origin.y, + new_width, + old_bounds.height(), + ); + let new_kid = self.compute_element(layout_context, child)?; + *kid = new_kid; + } else { + kid.translate(euclid::vec2(new_origin - old_bounds.origin.x, 0.)); + } + new_origin = kid.bounds.max_x(); + } + } + computed_kids.sort_by(|a, b| a.zindex.cmp(&b.zindex)); let content_rect = euclid::rect(0., 0., max_x.min(max_width), pixel_height); diff --git a/wezterm-gui/src/termwindow/render/fancy_tab_bar.rs b/wezterm-gui/src/termwindow/render/fancy_tab_bar.rs index 180d9bb6692..6152c7ef019 100644 --- a/wezterm-gui/src/termwindow/render/fancy_tab_bar.rs +++ b/wezterm-gui/src/termwindow/render/fancy_tab_bar.rs @@ -126,7 +126,12 @@ impl crate::TermWindow { bottom: Dimension::Cells(0.), }) .border(BoxDimension::new(Dimension::Pixels(0.))) - .colors(bar_colors.clone()), + .colors(bar_colors.clone()) + .float(if item.item == TabBarItem::LeftStatus { + Float::None + } else { + Float::Right + }), TabBarItem::NewTabButton => Element::new( &font, ElementContent::Poly { @@ -301,9 +306,11 @@ impl crate::TermWindow { _ => 0., }) .sum(); + let max_tab_width = ((self.dimensions.pixel_width as f32 / num_tabs) - (1.5 * metrics.cell_size.width as f32)) .max(0.); + let min_tab_width = 0.; // Reserve space for the native titlebar buttons if self @@ -338,7 +345,12 @@ impl crate::TermWindow { } TabBarItem::Tab { tab_idx, active } => { let mut elem = item_to_elem(item); - elem.max_width = Some(Dimension::Pixels(max_tab_width)); + elem.min_width = Some(Dimension::Pixels(min_tab_width)); + if self.config.tab_bar_fill { + elem.fill_width = true; + } else { + elem.max_width = Some(Dimension::Pixels(max_tab_width)); + } elem.content = match elem.content { ElementContent::Text(_) => unreachable!(), ElementContent::Poly { .. } => unreachable!(), @@ -442,8 +454,10 @@ impl crate::TermWindow { Dimension::Cells(0.5) }; + let mut new_children = left_eles; + new_children.append(&mut right_eles); children.push( - Element::new(&font, ElementContent::Children(left_eles)) + Element::new(&font, ElementContent::Children(new_children)) .vertical_align(VerticalAlign::Bottom) .colors(bar_colors.clone()) .padding(BoxDimension { @@ -454,11 +468,6 @@ impl crate::TermWindow { }) .zindex(1), ); - children.push( - Element::new(&font, ElementContent::Children(right_eles)) - .colors(bar_colors.clone()) - .float(Float::Right), - ); let content = ElementContent::Children(children); From efd8505f9fcb35f4ed4042154c9595019e635625 Mon Sep 17 00:00:00 2001 From: Daniel Berlin Date: Tue, 17 Oct 2023 18:11:15 -0400 Subject: [PATCH 2/9] Use LTO --- .cargo/config.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.cargo/config.toml b/.cargo/config.toml index fe26b9b0edc..6d227bd9f56 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -7,3 +7,7 @@ ar = "x86_64-w64-mingw32-gcc-ar" # enabled for the target, so let's turn that on here. [target.x86_64-pc-windows-msvc] rustflags = "-C target-feature=+crt-static" + +[aarch64-unknown-linux-gnu] +rustflags = ["-C", "linker-plugin-lto", "-C", "link-arg=-fuse-ld=lld"] + From 2859e21f16e0b778d5277e8c6ebfff6929406146 Mon Sep 17 00:00:00 2001 From: Daniel Berlin Date: Wed, 31 Jan 2024 14:56:52 -0500 Subject: [PATCH 3/9] Fix minimium tab bar width bug --- wezterm-gui/src/tabbar.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wezterm-gui/src/tabbar.rs b/wezterm-gui/src/tabbar.rs index 6f62e968caf..2a9d4e49639 100644 --- a/wezterm-gui/src/tabbar.rs +++ b/wezterm-gui/src/tabbar.rs @@ -323,11 +323,11 @@ impl TabBarState { // are symbols representing minimize, maximize and close. let mut active_tab_no = 0; - + let tab_info_len = tab_info.len().max(1); let config_tab_max_width = if config.tab_bar_fill { // We have no layout, so this is a rough estimate // The tab bar consists of the tab titles, the new tab button, and some padding - title_width.saturating_sub(new_tab.len() + 2 + tab_info.len()) / (tab_info.len()) + title_width.saturating_sub(new_tab.len() + 2 + tab_info_len) / (tab_info_len) } else { config.tab_max_width }; From c54a8c576acf88c77a6b97f0ddbae3f5118560e1 Mon Sep 17 00:00:00 2001 From: Daniel Berlin Date: Fri, 5 Apr 2024 22:33:49 -0400 Subject: [PATCH 4/9] Bluetooth entitlement --- assets/macos/WezTerm.app/Contents/Info.plist | 2 ++ 1 file changed, 2 insertions(+) diff --git a/assets/macos/WezTerm.app/Contents/Info.plist b/assets/macos/WezTerm.app/Contents/Info.plist index fee5020cc82..24eb50221dd 100644 --- a/assets/macos/WezTerm.app/Contents/Info.plist +++ b/assets/macos/WezTerm.app/Contents/Info.plist @@ -60,6 +60,8 @@ An application launched via WezTerm would like to access your Downloads folder. NSSystemAdministrationUsageDescription An application launched via WezTerm requires elevated permission. + NSBluetoothAlwaysUsageDescription + An application launched via WeZterm would like to access Bluetooth adapter. CFBundleDocumentTypes From 6a72d38b8f771a0d56616b2dcdb5436e0de98a8f Mon Sep 17 00:00:00 2001 From: Daniel Berlin Date: Tue, 6 Feb 2024 17:53:08 -0500 Subject: [PATCH 5/9] Bluetooth entitlement --- Cargo.lock | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 850dac8baf1..abe8cec6dd0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7373,3 +7373,8 @@ dependencies = [ "quote", "syn 2.0.68", ] + +[[patch.unused]] +name = "xcb" +version = "1.2.1" +source = "git+https://github.com/rust-x-bindings/rust-xcb?rev=dbdaa01c178c6fbe68bd51b7ad44c08172181083#dbdaa01c178c6fbe68bd51b7ad44c08172181083" From 9d3deb1fd6647f00b7a643a4d1bcaa2498abe8d2 Mon Sep 17 00:00:00 2001 From: Daniel Berlin Date: Tue, 17 Oct 2023 18:11:15 -0400 Subject: [PATCH 6/9] Use LTO --- Cargo.toml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 02cf5464f0d..88d6a5562e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,13 +15,13 @@ members = [ "wezterm-uds", ] resolver = "2" -exclude = [ - "termwiz/codegen" -] +exclude = ["termwiz/codegen"] [profile.release] opt-level = 3 # debug = 2 +lto = "fat" +codegen-units = 1 [profile.dev] # https://jakedeichert.com/blog/reducing-rust-incremental-compilation-times-on-macos-by-70-percent/ @@ -29,6 +29,8 @@ opt-level = 3 #split-debuginfo = "unpacked" [patch.crates-io] +xcb = { git = "https://github.com/rust-x-bindings/rust-xcb", rev = "dbdaa01c178c6fbe68bd51b7ad44c08172181083" } # waiting on a release with https://github.com/rust-x-bindings/rust-xcb/pull/230 + # We use our own vendored cairo, which has minimal deps and should just # build via cargo. -cairo-sys-rs = {path="deps/cairo", version="0.18.0"} +cairo-sys-rs = { path = "deps/cairo", version = "0.18.0" } From ea19513d1d5d717edb0e98a6d2238ed0a2ca97be Mon Sep 17 00:00:00 2001 From: Daniel Berlin Date: Thu, 9 May 2024 00:12:27 -0400 Subject: [PATCH 7/9] Fix keyboard keymap handling --- window/src/os/wayland/keyboard.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/window/src/os/wayland/keyboard.rs b/window/src/os/wayland/keyboard.rs index 6542b7065f2..783d962beff 100644 --- a/window/src/os/wayland/keyboard.rs +++ b/window/src/os/wayland/keyboard.rs @@ -90,7 +90,7 @@ impl Dispatch for WaylandState { } Err(err) => { - log::error!("Error processing keymap change: {:#}", err); + log::error!("{}", err); } } } From ed0d4c64c911f52b675583ee0908b897aaad3527 Mon Sep 17 00:00:00 2001 From: Daniel Berlin Date: Thu, 9 May 2024 00:20:09 -0400 Subject: [PATCH 8/9] Fix copy and paste --- window/src/os/wayland/copy_and_paste.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/window/src/os/wayland/copy_and_paste.rs b/window/src/os/wayland/copy_and_paste.rs index fce9b75e7ba..958321a9495 100644 --- a/window/src/os/wayland/copy_and_paste.rs +++ b/window/src/os/wayland/copy_and_paste.rs @@ -102,9 +102,12 @@ impl CopyAndPaste { impl WaylandState { pub(super) fn resolve_copy_and_paste(&mut self) -> Option>> { let active_surface_id = self.active_surface_id.borrow(); - let active_surface_id = active_surface_id.as_ref().unwrap(); - if let Some(pending) = self.surface_to_pending.get(&active_surface_id) { - Some(Arc::clone(&pending.lock().unwrap().copy_and_paste)) + if let Some(active_surface_id) = active_surface_id.as_ref() { + if let Some(pending) = self.surface_to_pending.get(&active_surface_id) { + Some(Arc::clone(&pending.lock().unwrap().copy_and_paste)) + } else { + None + } } else { None } From 2164d07e7382c17fe7ea1d8604e1b165596470d4 Mon Sep 17 00:00:00 2001 From: Daniel Berlin Date: Fri, 17 May 2024 01:31:31 -0400 Subject: [PATCH 9/9] Update cargo.toml --- Cargo.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 88d6a5562e3..125a677e318 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,8 +29,6 @@ codegen-units = 1 #split-debuginfo = "unpacked" [patch.crates-io] -xcb = { git = "https://github.com/rust-x-bindings/rust-xcb", rev = "dbdaa01c178c6fbe68bd51b7ad44c08172181083" } # waiting on a release with https://github.com/rust-x-bindings/rust-xcb/pull/230 - # We use our own vendored cairo, which has minimal deps and should just # build via cargo. cairo-sys-rs = { path = "deps/cairo", version = "0.18.0" }