1
1
mirror of https://github.com/wez/wezterm.git synced 2024-11-30 06:03:39 +03:00

Allow using unit like "1cell" or "5%" for window_padding

This commit introduces the `Dimension` type which allows specifying
a value in a variety of units; pixels, points, cells, percent.

`Dimension` needs contextual information to be evaluated as pixel
values, which makes resolving the value from the config slightly
more of a chore.

However, this type allows more flexible configurations that scale
with the font size and display dpi.

refs: #1124, #291, #195
This commit is contained in:
Wez Furlong 2021-10-07 19:26:22 -07:00
parent 9b4f7e78d6
commit 8c3477006f
9 changed files with 401 additions and 80 deletions

View File

@ -39,6 +39,7 @@ pub mod lua;
mod ssh; mod ssh;
mod terminal; mod terminal;
mod tls; mod tls;
mod units;
mod unix; mod unix;
mod version; mod version;
@ -52,6 +53,7 @@ pub use keys::*;
pub use ssh::*; pub use ssh::*;
pub use terminal::*; pub use terminal::*;
pub use tls::*; pub use tls::*;
pub use units::*;
pub use unix::*; pub use unix::*;
pub use version::*; pub use version::*;
@ -1339,14 +1341,14 @@ impl DefaultCursorStyle {
#[derive(Default, Deserialize, Serialize, Clone, Copy, Debug)] #[derive(Default, Deserialize, Serialize, Clone, Copy, Debug)]
pub struct WindowPadding { pub struct WindowPadding {
#[serde(default)] #[serde(default, deserialize_with = "de_pixels")]
pub left: u16, pub left: Dimension,
#[serde(default)] #[serde(default, deserialize_with = "de_pixels")]
pub top: u16, pub top: Dimension,
#[serde(default)] #[serde(default, deserialize_with = "de_pixels")]
pub right: u16, pub right: Dimension,
#[serde(default)] #[serde(default, deserialize_with = "de_pixels")]
pub bottom: u16, pub bottom: Dimension,
} }
impl_lua_conversion!(WindowPadding); impl_lua_conversion!(WindowPadding);

188
config/src/units.rs Normal file
View File

@ -0,0 +1,188 @@
use serde::{Deserializer, Serialize, Serializer};
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum DefaultUnit {
Points,
Pixels,
Percent,
Cells,
}
impl DefaultUnit {
fn to_dimension(self, value: f32) -> Dimension {
match self {
Self::Points => Dimension::Points(value),
Self::Pixels => Dimension::Pixels(value),
Self::Percent => Dimension::Percent(value / 100.),
Self::Cells => Dimension::Cells(value),
}
}
}
impl<'de> serde::de::Visitor<'de> for DefaultUnit {
type Value = Dimension;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("f64 or i64")
}
fn visit_f32<E>(self, value: f32) -> Result<Dimension, E>
where
E: serde::de::Error,
{
Ok(self.to_dimension(value))
}
fn visit_f64<E>(self, value: f64) -> Result<Dimension, E>
where
E: serde::de::Error,
{
Ok(self.to_dimension(value as f32))
}
fn visit_i64<E>(self, value: i64) -> Result<Dimension, E>
where
E: serde::de::Error,
{
Ok(self.to_dimension(value as f32))
}
fn visit_str<E>(self, s: &str) -> Result<Dimension, E>
where
E: serde::de::Error,
{
if let Ok(value) = s.parse::<f32>() {
Ok(self.to_dimension(value))
} else {
fn is_unit(s: &str, unit: &'static str) -> Option<f32> {
let s = s.strip_suffix(unit)?.trim();
s.parse().ok()
}
if let Some(v) = is_unit(s, "px") {
Ok(DefaultUnit::Pixels.to_dimension(v))
} else if let Some(v) = is_unit(s, "%") {
Ok(DefaultUnit::Percent.to_dimension(v))
} else if let Some(v) = is_unit(s, "pt") {
Ok(DefaultUnit::Points.to_dimension(v))
} else if let Some(v) = is_unit(s, "cell") {
Ok(DefaultUnit::Cells.to_dimension(v))
} else {
Err(serde::de::Error::custom(format!(
"expected either a number or a string of \
the form '123px' where 'px' is a unit and \
can be one of 'px', '%', 'pt' or 'cell', \
but got {}",
s
)))
}
}
}
}
#[derive(Copy, Clone, Debug)]
pub enum Dimension {
/// A value expressed in points, where 72 points == 1 inch.
Points(f32),
/// A value expressed in raw pixels
Pixels(f32),
/// A value expressed in terms of a fraction of the maximum
/// value in the same direction. For example, left padding
/// of 10% depends on the pixel width of that element.
/// The value is 1.0 == 100%. It is possible to express
/// eg: 2.0 for 200%.
Percent(f32),
/// A value expressed in terms of a fraction of the cell
/// size computed from the configured font size.
/// 1.0 == the cell size.
Cells(f32),
}
impl Dimension {
pub fn is_zero(&self) -> bool {
match self {
Self::Points(n) | Self::Pixels(n) | Self::Percent(n) | Self::Cells(n) => *n == 0.,
}
}
}
impl Default for Dimension {
fn default() -> Self {
Self::Pixels(0.)
}
}
impl Serialize for Dimension {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let s = match self {
Self::Points(n) => format!("{}pt", n),
Self::Pixels(n) => format!("{}px", n),
Self::Percent(n) => format!("{}%", n * 100.),
Self::Cells(n) => format!("{}cell", n),
};
serializer.serialize_str(&s)
}
}
#[derive(Clone, Copy, Debug)]
pub struct DimensionContext {
pub dpi: f32,
/// Width/Height or other upper bound on the dimension,
/// measured in pixels.
pub pixel_max: f32,
/// Width/Height of the font metrics cell size in the appropriate
/// dimension, measured in pixels.
pub pixel_cell: f32,
}
impl Dimension {
pub fn evaluate_as_pixels(&self, context: DimensionContext) -> f32 {
match self {
Self::Pixels(n) => *n,
Self::Points(pt) => pt * context.dpi / 72.0,
Self::Percent(p) => p * context.pixel_max,
Self::Cells(c) => c * context.pixel_cell,
}
}
}
fn de_dimension<'de, D>(unit: DefaultUnit, deserializer: D) -> Result<Dimension, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_any(unit)
}
pub fn de_pixels<'de, D>(deserializer: D) -> Result<Dimension, D::Error>
where
D: Deserializer<'de>,
{
de_dimension(DefaultUnit::Pixels, deserializer)
}
pub fn de_points<'de, D>(deserializer: D) -> Result<Dimension, D::Error>
where
D: Deserializer<'de>,
{
de_dimension(DefaultUnit::Points, deserializer)
}
pub fn de_percent<'de, D>(deserializer: D) -> Result<Dimension, D::Error>
where
D: Deserializer<'de>,
{
de_dimension(DefaultUnit::Percent, deserializer)
}
pub fn de_cells<'de, D>(deserializer: D) -> Result<Dimension, D::Error>
where
D: Deserializer<'de>,
{
de_dimension(DefaultUnit::Cells, deserializer)
}

View File

@ -57,6 +57,7 @@ As features stabilize some brief notes about them will accumulate here.
* Fixed: ssh config parser incorrectly split `Host` patterns with commas instead of whitespace [#1196](https://github.com/wez/wezterm/issues/1196) * Fixed: ssh config parser incorrectly split `Host` patterns with commas instead of whitespace [#1196](https://github.com/wez/wezterm/issues/1196)
* Fixed: search now auto-updates when the pane content changes [#1205](https://github.com/wez/wezterm/issues/1205) * Fixed: search now auto-updates when the pane content changes [#1205](https://github.com/wez/wezterm/issues/1205)
* Fixed: fonts with emoji presentation are shifted to better align with the primary font baseline [#1203](https://github.com/wez/wezterm/issues/1203) * Fixed: fonts with emoji presentation are shifted to better align with the primary font baseline [#1203](https://github.com/wez/wezterm/issues/1203)
* New: [window_padding](config/lua/config/window_padding.md) now accepts values such as `"1cell"` or `"30%"` to compute values based on font or window metrics.
### 20210814-124438-54e29167 ### 20210814-124438-54e29167

View File

@ -227,20 +227,9 @@ return {
### Window Padding ### Window Padding
You may add padding around the edges of the terminal cells: You may add padding around the edges of the terminal area.
```lua [See the window_padding docs for more info](lua/config/window_padding.md)
return {
window_padding = {
left = 2,
-- This will become the scrollbar width if you have enabled the scrollbar!
right = 2,
top = 0,
bottom = 0,
}
}
```
## Styling Inactive Panes ## Styling Inactive Panes

View File

@ -0,0 +1,46 @@
# window_padding
Controls the amount of padding between the window border and the
terminal cells.
Padding is measured in pixels.
If [enable_scroll_bar](enable_scroll_bar.md) is `true`, then the value you
set for `right` will control the width of the scrollbar. If you have
enabled the scrollbar and have set `right` to `0` then the right padding
(and thus the scrollbar width) will instead match the width of a cell.
```lua
return {
window_padding = {
left = 2,
right = 2,
top = 0,
bottom = 0,
}
}
```
*Since: nightly builds only*
You may now express padding using a number of different units by specifying
a string value with a unit suffix:
* `"1px"` - the `px` suffix indicates pixels, so this represents a `1` pixel value
* `"1pt"` - the `pt` suffix indicates points. There are `72` points in `1 inch`. The actual size this occupies on screen depends on the dpi of the display device.
* `"1cell"` - the `cell` suffix indicates the size of the terminal cell, which in turn depends on the font size, font scaling and dpi. When used for width, the width of the cell is used. When used for height, the height of the cell is used.
* `"1%"` - the `%` suffix indicates the size of the terminal portion of the display, which is computed based on the number of rows/columns and the size of the cell. While it is possible to specify percentage, there are some resize scenarios where the percentage value may not be 100% stable/deterministic, as the size of the padding is used to compute the number of rows/columns.
You may use a fractional number such as `"0.5cell"` or numbers large than one such as `"72pt"`.
```lua
return {
window_padding = {
left = "1cell",
right = "1cell",
top = "0.5cell",
bottom = "0.5cell",
}
}
```

View File

@ -22,7 +22,7 @@ use config::keyassignment::{
ClipboardCopyDestination, ClipboardPasteSource, InputMap, KeyAssignment, SpawnCommand, ClipboardCopyDestination, ClipboardPasteSource, InputMap, KeyAssignment, SpawnCommand,
}; };
use config::{ use config::{
configuration, AudibleBell, ConfigHandle, GradientOrientation, TermConfig, configuration, AudibleBell, ConfigHandle, DimensionContext, GradientOrientation, TermConfig,
WindowCloseConfirmation, WindowCloseConfirmation,
}; };
use luahelper::impl_lua_conversion; use luahelper::impl_lua_conversion;
@ -550,14 +550,26 @@ impl TermWindow {
pixel_height: (render_metrics.cell_size.height as usize * physical_rows) as u16, pixel_height: (render_metrics.cell_size.height as usize * physical_rows) as u16,
}; };
let h_context = DimensionContext {
dpi: dpi as f32,
pixel_max: terminal_size.pixel_width as f32,
pixel_cell: render_metrics.cell_size.width as f32,
};
let padding_left = config.window_padding.left.evaluate_as_pixels(h_context) as u16;
let padding_right = resize::effective_right_padding(&config, h_context);
let v_context = DimensionContext {
dpi: dpi as f32,
pixel_max: terminal_size.pixel_height as f32,
pixel_cell: render_metrics.cell_size.height as f32,
};
let padding_top = config.window_padding.top.evaluate_as_pixels(v_context) as u16;
let padding_bottom = config.window_padding.bottom.evaluate_as_pixels(v_context) as u16;
let dimensions = Dimensions { let dimensions = Dimensions {
pixel_width: (terminal_size.pixel_width pixel_width: (terminal_size.pixel_width + padding_left + padding_right) as usize,
+ config.window_padding.left
+ resize::effective_right_padding(&config, &render_metrics))
as usize,
pixel_height: ((terminal_size.rows * render_metrics.cell_size.height as u16) pixel_height: ((terminal_size.rows * render_metrics.cell_size.height as u16)
+ config.window_padding.top + padding_top
+ config.window_padding.bottom) as usize + padding_bottom) as usize
+ tab_bar_height, + tab_bar_height,
dpi, dpi,
}; };
@ -1263,10 +1275,23 @@ impl TermWindow {
let active_tab = tabs.iter().find(|t| t.is_active).cloned(); let active_tab = tabs.iter().find(|t| t.is_active).cloned();
let active_pane = panes.iter().find(|p| p.is_active).cloned(); let active_pane = panes.iter().find(|p| p.is_active).cloned();
let v_context = DimensionContext {
dpi: self.dimensions.dpi as f32,
pixel_max: self.terminal_size.pixel_height as f32,
pixel_cell: self.render_metrics.cell_size.height as f32,
};
let padding_top = self.config.window_padding.top.evaluate_as_pixels(v_context) as u16;
let padding_bottom = self
.config
.window_padding
.bottom
.evaluate_as_pixels(v_context) as u16;
let tab_bar_y = if self.config.tab_bar_at_bottom { let tab_bar_y = if self.config.tab_bar_at_bottom {
let avail_height = self.dimensions.pixel_height.saturating_sub( let avail_height = self
(self.config.window_padding.top + self.config.window_padding.bottom) as usize, .dimensions
); .pixel_height
.saturating_sub((padding_top + padding_bottom) as usize);
let num_rows = avail_height as usize / self.render_metrics.cell_size.height as usize; let num_rows = avail_height as usize / self.render_metrics.cell_size.height as usize;
@ -1396,14 +1421,16 @@ impl TermWindow {
fn update_text_cursor(&mut self, pane: &Rc<dyn Pane>) { fn update_text_cursor(&mut self, pane: &Rc<dyn Pane>) {
let cursor = pane.get_cursor_position(); let cursor = pane.get_cursor_position();
if let Some(win) = self.window.as_ref() { if let Some(win) = self.window.as_ref() {
let config = &self.config;
let top = pane.get_dimensions().physical_top + if self.show_tab_bar { -1 } else { 0 }; let top = pane.get_dimensions().physical_top + if self.show_tab_bar { -1 } else { 0 };
let (padding_left, padding_top) = self.padding_left_top();
let r = Rect::new( let r = Rect::new(
Point::new( Point::new(
(cursor.x.max(0) as isize * self.render_metrics.cell_size.width) (cursor.x.max(0) as isize * self.render_metrics.cell_size.width)
.add(config.window_padding.left as isize), .add(padding_left as isize),
((cursor.y - top).max(0) as isize * self.render_metrics.cell_size.height) ((cursor.y - top).max(0) as isize * self.render_metrics.cell_size.height)
.add(config.window_padding.top as isize), .add(padding_top as isize),
), ),
self.render_metrics.cell_size, self.render_metrics.cell_size,
); );

View File

@ -58,25 +58,23 @@ impl super::TermWindow {
self.current_mouse_event.replace(event.clone()); self.current_mouse_event.replace(event.clone());
let config = &self.config;
let first_line_offset = if self.show_tab_bar && !self.config.tab_bar_at_bottom { let first_line_offset = if self.show_tab_bar && !self.config.tab_bar_at_bottom {
self.tab_bar_pixel_height().unwrap_or(0.) as isize self.tab_bar_pixel_height().unwrap_or(0.) as isize
} else { } else {
0 0
}; };
let (padding_left, padding_top) = self.padding_left_top();
let y = (event let y = (event
.coords .coords
.y .y
.sub(config.window_padding.top as isize) .sub(padding_top as isize)
.sub(first_line_offset) .sub(first_line_offset)
.max(0) .max(0)
/ self.render_metrics.cell_size.height) as i64; / self.render_metrics.cell_size.height) as i64;
let x = (event let x = (event.coords.x.sub(padding_left as isize).max(0) as f32)
.coords
.x
.sub(config.window_padding.left as isize)
.max(0) as f32)
/ self.render_metrics.cell_size.width as f32; / self.render_metrics.cell_size.width as f32;
let x = if !pane.is_mouse_grabbed() { let x = if !pane.is_mouse_grabbed() {
// Round the x coordinate so that we're a bit more forgiving of // Round the x coordinate so that we're a bit more forgiving of

View File

@ -18,7 +18,9 @@ use ::window::glium::uniforms::{
use ::window::glium::{uniform, BlendingFunction, LinearBlendingFactor, Surface}; use ::window::glium::{uniform, BlendingFunction, LinearBlendingFactor, Surface};
use ::window::{Point, Rect, Size, WindowOps}; use ::window::{Point, Rect, Size, WindowOps};
use anyhow::anyhow; use anyhow::anyhow;
use config::{ConfigHandle, HsbTransform, TabBarColors, TextStyle, VisualBellTarget}; use config::{
ConfigHandle, DimensionContext, HsbTransform, TabBarColors, TextStyle, VisualBellTarget,
};
use mux::pane::Pane; use mux::pane::Pane;
use mux::renderable::{RenderableDimensions, StableCursorPosition}; use mux::renderable::{RenderableDimensions, StableCursorPosition};
use mux::tab::{PositionedPane, PositionedSplit, SplitDirection}; use mux::tab::{PositionedPane, PositionedSplit, SplitDirection};
@ -807,12 +809,14 @@ impl super::TermWindow {
let config = &self.config; let config = &self.config;
let palette = pos.pane.palette(); let palette = pos.pane.palette();
let (padding_left, padding_top) = self.padding_left_top();
let tab_bar_height = if self.show_tab_bar && !self.config.tab_bar_at_bottom { let tab_bar_height = if self.show_tab_bar && !self.config.tab_bar_at_bottom {
self.tab_bar_pixel_height()? self.tab_bar_pixel_height()?
} else { } else {
0. 0.
}; };
let top_pixel_y = tab_bar_height + self.config.window_padding.top as f32; let top_pixel_y = tab_bar_height + padding_top;
let cursor = pos.pane.get_cursor_position(); let cursor = pos.pane.get_cursor_position();
if pos.is_active { if pos.is_active {
@ -935,12 +939,8 @@ impl super::TermWindow {
&mut layers[0], &mut layers[0],
Rect::new( Rect::new(
Point::new( Point::new(
((pos.left as f32 * cell_width) + self.config.window_padding.left as f32) ((pos.left as f32 * cell_width) + padding_left) as isize,
as isize, (top_pixel_y + (pos.top as f32 * cell_height) + padding_top) as isize,
(top_pixel_y
+ (pos.top as f32 * cell_height)
+ self.config.window_padding.top as f32)
as isize,
), ),
Size::new( Size::new(
(pos.width as f32 * cell_width) as isize, (pos.width as f32 * cell_width) as isize,
@ -1002,13 +1002,8 @@ impl super::TermWindow {
&mut layers[0], &mut layers[0],
Rect::new( Rect::new(
Point::new( Point::new(
((pos.left as f32 * cell_width) ((pos.left as f32 * cell_width) + padding_left) as isize,
+ self.config.window_padding.left as f32) (top_pixel_y + (pos.top as f32 * cell_height) + padding_top) as isize,
as isize,
(top_pixel_y
+ (pos.top as f32 * cell_height)
+ self.config.window_padding.top as f32)
as isize,
), ),
Size::new( Size::new(
(pos.width as f32 * cell_width) as isize, (pos.width as f32 * cell_width) as isize,
@ -1104,7 +1099,7 @@ impl super::TermWindow {
RenderScreenLineOpenGLParams { RenderScreenLineOpenGLParams {
top_pixel_y: top_pixel_y top_pixel_y: top_pixel_y
+ (line_idx + pos.top) as f32 * self.render_metrics.cell_size.height as f32, + (line_idx + pos.top) as f32 * self.render_metrics.cell_size.height as f32,
left_pixel_x: self.config.window_padding.left as f32 left_pixel_x: padding_left
+ (pos.left as f32 * self.render_metrics.cell_size.width as f32), + (pos.left as f32 * self.render_metrics.cell_size.width as f32),
stable_line_idx: Some(stable_row), stable_line_idx: Some(stable_row),
line: &line, line: &line,
@ -1245,6 +1240,28 @@ impl super::TermWindow {
Ok(()) Ok(())
} }
pub fn padding_left_top(&self) -> (f32, f32) {
let h_context = DimensionContext {
dpi: self.dimensions.dpi as f32,
pixel_max: self.terminal_size.pixel_width as f32,
pixel_cell: self.render_metrics.cell_size.width as f32,
};
let v_context = DimensionContext {
dpi: self.dimensions.dpi as f32,
pixel_max: self.terminal_size.pixel_height as f32,
pixel_cell: self.render_metrics.cell_size.height as f32,
};
let padding_left = self
.config
.window_padding
.left
.evaluate_as_pixels(h_context);
let padding_top = self.config.window_padding.top.evaluate_as_pixels(v_context);
(padding_left, padding_top)
}
pub fn paint_split_opengl( pub fn paint_split_opengl(
&mut self, &mut self,
split: &PositionedSplit, split: &PositionedSplit,
@ -1285,13 +1302,15 @@ impl super::TermWindow {
quad.set_texture_adjust(0., 0., 0., 0.); quad.set_texture_adjust(0., 0., 0., 0.);
quad.set_has_color(false); quad.set_has_color(false);
let (padding_left, padding_top) = self.padding_left_top();
let pos_y = (self.dimensions.pixel_height as f32 / -2.) let pos_y = (self.dimensions.pixel_height as f32 / -2.)
+ split.top as f32 * cell_height + split.top as f32 * cell_height
+ first_row_offset + first_row_offset
+ self.config.window_padding.top as f32; + padding_top;
let pos_x = (self.dimensions.pixel_width as f32 / -2.) let pos_x = (self.dimensions.pixel_width as f32 / -2.)
+ split.left as f32 * cell_width + split.left as f32 * cell_width
+ self.config.window_padding.left as f32; + padding_left;
if split.direction == SplitDirection::Horizontal { if split.direction == SplitDirection::Horizontal {
quad.set_position( quad.set_position(
@ -1301,9 +1320,9 @@ impl super::TermWindow {
pos_y + split.size as f32 * cell_height, pos_y + split.size as f32 * cell_height,
); );
self.ui_items.push(UIItem { self.ui_items.push(UIItem {
x: self.config.window_padding.left as usize + (split.left * cell_width as usize), x: padding_left as usize + (split.left * cell_width as usize),
width: cell_width as usize, width: cell_width as usize,
y: self.config.window_padding.top as usize y: padding_top as usize
+ first_row_offset as usize + first_row_offset as usize
+ split.top * cell_height as usize, + split.top * cell_height as usize,
height: split.size * cell_height as usize, height: split.size * cell_height as usize,
@ -1317,9 +1336,9 @@ impl super::TermWindow {
pos_y + cell_height, pos_y + cell_height,
); );
self.ui_items.push(UIItem { self.ui_items.push(UIItem {
x: self.config.window_padding.left as usize + (split.left * cell_width as usize), x: padding_left as usize + (split.left * cell_width as usize),
width: split.size * cell_width as usize, width: split.size * cell_width as usize,
y: self.config.window_padding.top as usize y: padding_top as usize
+ first_row_offset as usize + first_row_offset as usize
+ split.top * cell_height as usize, + split.top * cell_height as usize,
height: cell_height as usize, height: cell_height as usize,

View File

@ -1,6 +1,6 @@
use crate::utilsprites::RenderMetrics; use crate::utilsprites::RenderMetrics;
use ::window::{Dimensions, Window, WindowOps, WindowState}; use ::window::{Dimensions, Window, WindowOps, WindowState};
use config::ConfigHandle; use config::{ConfigHandle, DimensionContext};
use mux::Mux; use mux::Mux;
use portable_pty::PtySize; use portable_pty::PtySize;
use std::rc::Rc; use std::rc::Rc;
@ -138,12 +138,27 @@ impl super::TermWindow {
let rows = size.rows; let rows = size.rows;
let cols = size.cols; let cols = size.cols;
let h_context = DimensionContext {
dpi: dimensions.dpi as f32,
pixel_max: size.pixel_width as f32,
pixel_cell: self.render_metrics.cell_size.width as f32,
};
let v_context = DimensionContext {
dpi: dimensions.dpi as f32,
pixel_max: size.pixel_height as f32,
pixel_cell: self.render_metrics.cell_size.height as f32,
};
let padding_left = config.window_padding.left.evaluate_as_pixels(h_context) as u16;
let padding_top = config.window_padding.top.evaluate_as_pixels(v_context) as u16;
let padding_bottom = config.window_padding.bottom.evaluate_as_pixels(v_context) as u16;
let padding_right = effective_right_padding(&config, h_context);
let pixel_height = (rows * self.render_metrics.cell_size.height as u16) let pixel_height = (rows * self.render_metrics.cell_size.height as u16)
+ (config.window_padding.top + config.window_padding.bottom) + (padding_top + padding_bottom)
+ tab_bar_height as u16; + tab_bar_height as u16;
let pixel_width = (cols * self.render_metrics.cell_size.width as u16) let pixel_width = (cols * self.render_metrics.cell_size.width as u16)
+ (config.window_padding.left + self.effective_right_padding(&config)); + (padding_left + padding_right);
let dims = Dimensions { let dims = Dimensions {
pixel_width: pixel_width as usize, pixel_width: pixel_width as usize,
@ -154,12 +169,28 @@ impl super::TermWindow {
(size, dims) (size, dims)
} else { } else {
// Resize of the window dimensions may result in changed terminal dimensions // Resize of the window dimensions may result in changed terminal dimensions
let avail_width = dimensions.pixel_width.saturating_sub(
(config.window_padding.left + self.effective_right_padding(&config)) as usize, let h_context = DimensionContext {
); dpi: dimensions.dpi as f32,
pixel_max: self.terminal_size.pixel_width as f32,
pixel_cell: self.render_metrics.cell_size.width as f32,
};
let v_context = DimensionContext {
dpi: dimensions.dpi as f32,
pixel_max: self.terminal_size.pixel_height as f32,
pixel_cell: self.render_metrics.cell_size.height as f32,
};
let padding_left = config.window_padding.left.evaluate_as_pixels(h_context) as u16;
let padding_top = config.window_padding.top.evaluate_as_pixels(v_context) as u16;
let padding_bottom = config.window_padding.bottom.evaluate_as_pixels(v_context) as u16;
let padding_right = effective_right_padding(&config, h_context);
let avail_width = dimensions
.pixel_width
.saturating_sub((padding_left + padding_right) as usize);
let avail_height = dimensions let avail_height = dimensions
.pixel_height .pixel_height
.saturating_sub((config.window_padding.top + config.window_padding.bottom) as usize) .saturating_sub((padding_top + padding_bottom) as usize)
.saturating_sub(tab_bar_height as usize); .saturating_sub(tab_bar_height as usize);
let rows = avail_height / self.render_metrics.cell_size.height as usize; let rows = avail_height / self.render_metrics.cell_size.height as usize;
@ -306,14 +337,27 @@ impl super::TermWindow {
0 0
}; };
let h_context = DimensionContext {
dpi: self.dimensions.dpi as f32,
pixel_max: self.dimensions.pixel_width as f32,
pixel_cell: render_metrics.cell_size.width as f32,
};
let v_context = DimensionContext {
dpi: self.dimensions.dpi as f32,
pixel_max: self.dimensions.pixel_height as f32,
pixel_cell: render_metrics.cell_size.height as f32,
};
let padding_left = config.window_padding.left.evaluate_as_pixels(h_context) as u16;
let padding_top = config.window_padding.top.evaluate_as_pixels(v_context) as u16;
let padding_bottom = config.window_padding.bottom.evaluate_as_pixels(v_context) as u16;
let dimensions = Dimensions { let dimensions = Dimensions {
pixel_width: ((terminal_size.cols * render_metrics.cell_size.width as u16) pixel_width: ((terminal_size.cols * render_metrics.cell_size.width as u16)
+ config.window_padding.left + padding_left
+ effective_right_padding(&config, &render_metrics)) + effective_right_padding(&config, h_context)) as usize,
as usize,
pixel_height: ((terminal_size.rows * render_metrics.cell_size.height as u16) pixel_height: ((terminal_size.rows * render_metrics.cell_size.height as u16)
+ config.window_padding.top + padding_top
+ config.window_padding.bottom) as usize + padding_bottom) as usize
+ tab_bar_height, + tab_bar_height,
dpi: config.dpi.unwrap_or_else(|| ::window::default_dpi()) as usize, dpi: config.dpi.unwrap_or_else(|| ::window::default_dpi()) as usize,
}; };
@ -331,7 +375,14 @@ impl super::TermWindow {
} }
pub fn effective_right_padding(&self, config: &ConfigHandle) -> u16 { pub fn effective_right_padding(&self, config: &ConfigHandle) -> u16 {
effective_right_padding(config, &self.render_metrics) effective_right_padding(
config,
DimensionContext {
pixel_cell: self.render_metrics.cell_size.width as f32,
dpi: self.dimensions.dpi as f32,
pixel_max: self.dimensions.pixel_width as f32,
},
)
} }
} }
@ -339,10 +390,10 @@ impl super::TermWindow {
/// This is needed because the default is 0, but if the user has /// This is needed because the default is 0, but if the user has
/// enabled the scroll bar then they will expect it to have a reasonable /// enabled the scroll bar then they will expect it to have a reasonable
/// size unless they've specified differently. /// size unless they've specified differently.
pub fn effective_right_padding(config: &ConfigHandle, render_metrics: &RenderMetrics) -> u16 { pub fn effective_right_padding(config: &ConfigHandle, context: DimensionContext) -> u16 {
if config.enable_scroll_bar && config.window_padding.right == 0 { if config.enable_scroll_bar && config.window_padding.right.is_zero() {
render_metrics.cell_size.width as u16 context.pixel_cell as u16
} else { } else {
config.window_padding.right as u16 config.window_padding.right.evaluate_as_pixels(context) as u16
} }
} }