mirror of
https://github.com/wez/wezterm.git
synced 2024-09-20 11:17:15 +03:00
Add optional scroll position UI
This commit adds a scrollbar that shows the scroll position but that does not currently allow dragging to scroll. The scrollbar occupies the right hand side window padding. The width of the scrollbar can be set by explicitly configuring `window_padding.right`. If the right padding is set to 0 (which is its default value), and the scroll bar is enabled then the cell width will be used for the right padding value instead. The scrollbar can be enabled/disabled via this config setting: ``` enable_scroll_bar = true ``` Its color by this: ``` [colors] scrollbar_thumb = "#444444" ``` (Note that color palette config will be reloaded when the config file is changed, but you'll need to spawn a new tab/window to see the effects because we cannot assume that a config reload should always replace a potentially dynamically adjusted color scheme in a tab).
This commit is contained in:
parent
c961f934f2
commit
ec609a2e41
@ -22,6 +22,9 @@ pub struct Palette {
|
||||
pub brights: Option<[RgbColor; 8]>,
|
||||
/// Configure the colors and styling of the tab bar
|
||||
pub tab_bar: Option<TabBarColors>,
|
||||
/// The color of the "thumb" of the scrollbar; the segment that
|
||||
/// represents the current viewable area
|
||||
pub scrollbar_thumb: Option<RgbColor>,
|
||||
}
|
||||
|
||||
impl From<Palette> for term::color::ColorPalette {
|
||||
@ -41,6 +44,7 @@ impl From<Palette> for term::color::ColorPalette {
|
||||
apply_color!(cursor_border);
|
||||
apply_color!(selection_fg);
|
||||
apply_color!(selection_bg);
|
||||
apply_color!(scrollbar_thumb);
|
||||
|
||||
if let Some(ansi) = cfg.ansi {
|
||||
for (idx, col) in ansi.iter().enumerate() {
|
||||
|
@ -420,6 +420,9 @@ pub struct Config {
|
||||
#[serde(default = "default_true")]
|
||||
pub enable_tab_bar: bool,
|
||||
|
||||
#[serde(default)]
|
||||
pub enable_scroll_bar: bool,
|
||||
|
||||
/// If false, do not try to use a Wayland protocol connection
|
||||
/// when starting the gui frontend, and instead use X11.
|
||||
/// This option is only considered on X11/Wayland systems and
|
||||
|
@ -117,4 +117,18 @@ impl<'a> Quad<'a> {
|
||||
v.cursor_color = color;
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn get_position(&self) -> (f32, f32, f32, f32) {
|
||||
let top_left = self.vert[V_TOP_LEFT].position;
|
||||
let bottom_right = self.vert[V_BOT_RIGHT].position;
|
||||
(top_left.0, top_left.1, bottom_right.0, bottom_right.1)
|
||||
}
|
||||
|
||||
pub fn set_position(&mut self, left: f32, top: f32, right: f32, bottom: f32) {
|
||||
self.vert[V_TOP_LEFT].position = (left, top);
|
||||
self.vert[V_TOP_RIGHT].position = (right, top);
|
||||
self.vert[V_BOT_LEFT].position = (left, bottom);
|
||||
self.vert[V_BOT_RIGHT].position = (right, bottom);
|
||||
}
|
||||
}
|
||||
|
@ -137,8 +137,9 @@ impl OpenGLRenderState {
|
||||
let mut indices = Vec::new();
|
||||
|
||||
let config = configuration();
|
||||
let avail_width = (width as usize)
|
||||
.saturating_sub((config.window_padding.left + config.window_padding.right) as usize);
|
||||
let padding_right = super::termwindow::effective_right_padding(&config, metrics);
|
||||
let avail_width =
|
||||
(width as usize).saturating_sub((config.window_padding.left + padding_right) as usize);
|
||||
let avail_height = (height as usize)
|
||||
.saturating_sub((config.window_padding.top + config.window_padding.bottom) as usize);
|
||||
|
||||
@ -197,6 +198,46 @@ impl OpenGLRenderState {
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// And a quad for the scrollbar thumb
|
||||
let x_pos = (width / 2.0) - cell_width;
|
||||
let y_pos = (height / -2.0) + padding_top;
|
||||
let thumb_width = cell_width;
|
||||
let thumb_height = height;
|
||||
|
||||
// Remember starting index for this position
|
||||
let idx = verts.len() as u32;
|
||||
verts.push(Vertex {
|
||||
// Top left
|
||||
position: (x_pos, thumb_width),
|
||||
..Default::default()
|
||||
});
|
||||
verts.push(Vertex {
|
||||
// Top Right
|
||||
position: (x_pos + thumb_width, y_pos),
|
||||
..Default::default()
|
||||
});
|
||||
verts.push(Vertex {
|
||||
// Bottom Left
|
||||
position: (x_pos, y_pos + thumb_height),
|
||||
..Default::default()
|
||||
});
|
||||
verts.push(Vertex {
|
||||
// Bottom Right
|
||||
position: (x_pos + thumb_width, y_pos + thumb_height),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
// Emit two triangles to form the glyph quad
|
||||
indices.push(idx + V_TOP_LEFT as u32);
|
||||
indices.push(idx + V_TOP_RIGHT as u32);
|
||||
indices.push(idx + V_BOT_LEFT as u32);
|
||||
|
||||
indices.push(idx + V_TOP_RIGHT as u32);
|
||||
indices.push(idx + V_BOT_LEFT as u32);
|
||||
indices.push(idx + V_BOT_RIGHT as u32);
|
||||
}
|
||||
|
||||
Ok((
|
||||
VertexBuffer::dynamic(context, &verts)?,
|
||||
IndexBuffer::new(
|
||||
|
@ -24,7 +24,7 @@ use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
use term::color::ColorPalette;
|
||||
use term::{CursorPosition, Line, Underline};
|
||||
use term::{CursorPosition, Line, Underline, VisibleRowIndex};
|
||||
use termwiz::color::RgbColor;
|
||||
use termwiz::surface::CursorShape;
|
||||
|
||||
@ -63,11 +63,13 @@ pub struct TermWindow {
|
||||
render_state: RenderState,
|
||||
keys: KeyMap,
|
||||
show_tab_bar: bool,
|
||||
show_scroll_bar: bool,
|
||||
tab_bar: TabBarState,
|
||||
last_mouse_coords: (usize, i64),
|
||||
drag_start_coords: Option<Point>,
|
||||
config_generation: usize,
|
||||
created_instant: Instant,
|
||||
last_scroll_info: (VisibleRowIndex, usize),
|
||||
}
|
||||
|
||||
struct Host<'a> {
|
||||
@ -251,11 +253,13 @@ impl WindowCallbacks for TermWindow {
|
||||
}
|
||||
}
|
||||
|
||||
// When hovering over a hyperlink, show an appropriate
|
||||
// mouse cursor to give the cue that it is clickable
|
||||
context.set_cursor(Some(if self.show_tab_bar && y == 0 {
|
||||
let in_tab_bar = self.show_tab_bar && y == 0;
|
||||
let in_scroll_bar = self.show_scroll_bar && x >= self.terminal_size.cols as usize;
|
||||
context.set_cursor(Some(if in_tab_bar || in_scroll_bar {
|
||||
MouseCursor::Arrow
|
||||
} else if tab.renderer().current_highlight().is_some() {
|
||||
// When hovering over a hyperlink, show an appropriate
|
||||
// mouse cursor to give the cue that it is clickable
|
||||
MouseCursor::Hand
|
||||
} else {
|
||||
MouseCursor::Text
|
||||
@ -397,6 +401,18 @@ impl WindowCallbacks for TermWindow {
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes the effective padding for the RHS.
|
||||
/// 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
|
||||
/// size unless they've specified differently.
|
||||
pub fn effective_right_padding(config: &ConfigHandle, render_metrics: &RenderMetrics) -> u16 {
|
||||
if config.enable_scroll_bar && config.window_padding.right == 0 {
|
||||
render_metrics.cell_size.width as u16
|
||||
} else {
|
||||
config.window_padding.right as u16
|
||||
}
|
||||
}
|
||||
|
||||
impl TermWindow {
|
||||
pub fn new_window(
|
||||
config: &ConfigHandle,
|
||||
@ -416,13 +432,12 @@ impl TermWindow {
|
||||
};
|
||||
|
||||
let rows_with_tab_bar = if config.enable_tab_bar { 1 } else { 0 } + terminal_size.rows;
|
||||
// Accomodating future scroll bar UI
|
||||
let cols_with_scroll_bar = terminal_size.cols;
|
||||
|
||||
let dimensions = Dimensions {
|
||||
pixel_width: ((cols_with_scroll_bar * render_metrics.cell_size.width as u16)
|
||||
pixel_width: ((terminal_size.cols * render_metrics.cell_size.width as u16)
|
||||
+ config.window_padding.left
|
||||
+ config.window_padding.right) as usize,
|
||||
+ effective_right_padding(&config, &render_metrics))
|
||||
as usize,
|
||||
pixel_height: ((rows_with_tab_bar * render_metrics.cell_size.height as u16)
|
||||
+ config.window_padding.top
|
||||
+ config.window_padding.bottom) as usize,
|
||||
@ -458,11 +473,13 @@ impl TermWindow {
|
||||
render_state,
|
||||
keys: KeyMap::new(),
|
||||
show_tab_bar: config.enable_tab_bar,
|
||||
show_scroll_bar: config.enable_scroll_bar,
|
||||
tab_bar: TabBarState::default(),
|
||||
last_mouse_coords: (0, -1),
|
||||
drag_start_coords: None,
|
||||
config_generation: config.generation(),
|
||||
created_instant: Instant::now(),
|
||||
last_scroll_info: (0, 0),
|
||||
}),
|
||||
)?;
|
||||
|
||||
@ -701,6 +718,7 @@ impl TermWindow {
|
||||
let config = configuration();
|
||||
|
||||
self.show_tab_bar = config.enable_tab_bar;
|
||||
self.show_scroll_bar = config.enable_scroll_bar;
|
||||
self.keys = KeyMap::new();
|
||||
self.config_generation = config.generation();
|
||||
let dimensions = self.dimensions;
|
||||
@ -712,6 +730,29 @@ impl TermWindow {
|
||||
}
|
||||
}
|
||||
|
||||
fn update_scrollbar(&mut self) {
|
||||
if !self.show_scroll_bar {
|
||||
return;
|
||||
}
|
||||
|
||||
let mux = Mux::get().unwrap();
|
||||
let tab = match mux.get_active_tab_for_window(self.mux_window_id) {
|
||||
Some(tab) => tab,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let info = tab.renderer().get_scrollbar_info();
|
||||
if info == self.last_scroll_info {
|
||||
return;
|
||||
}
|
||||
|
||||
self.last_scroll_info = info;
|
||||
|
||||
if let Some(window) = self.window.as_ref() {
|
||||
window.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
fn update_title(&mut self) {
|
||||
let mux = Mux::get().unwrap();
|
||||
let window = match mux.get_window(self.mux_window_id) {
|
||||
@ -792,6 +833,7 @@ impl TermWindow {
|
||||
|
||||
drop(window);
|
||||
self.update_title();
|
||||
self.update_scrollbar();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -971,7 +1013,7 @@ impl TermWindow {
|
||||
+ (config.window_padding.top + config.window_padding.bottom);
|
||||
|
||||
let pixel_width = (cols * self.render_metrics.cell_size.width as u16)
|
||||
+ (config.window_padding.left + config.window_padding.right);
|
||||
+ (config.window_padding.left + self.effective_right_padding(&config));
|
||||
|
||||
let dims = Dimensions {
|
||||
pixel_width: pixel_width as usize,
|
||||
@ -983,7 +1025,7 @@ impl TermWindow {
|
||||
} else {
|
||||
// Resize of the window dimensions may result in changed terminal dimensions
|
||||
let avail_width = dimensions.pixel_width
|
||||
- (config.window_padding.left + config.window_padding.right) as usize;
|
||||
- (config.window_padding.left + self.effective_right_padding(&config)) as usize;
|
||||
let avail_height = dimensions.pixel_height
|
||||
- (config.window_padding.top + config.window_padding.bottom) as usize;
|
||||
|
||||
@ -1153,24 +1195,57 @@ impl TermWindow {
|
||||
),
|
||||
bg,
|
||||
);
|
||||
// right padding
|
||||
// right padding / scroll bar
|
||||
let padding_right = self.effective_right_padding(&config);
|
||||
|
||||
ctx.clear_rect(
|
||||
Rect::new(
|
||||
Point::new(
|
||||
(self.dimensions.pixel_width - config.window_padding.right as usize) as isize,
|
||||
(self.dimensions.pixel_width - padding_right as usize) as isize,
|
||||
config.window_padding.top as isize,
|
||||
),
|
||||
Size::new(
|
||||
config.window_padding.right as isize,
|
||||
padding_right as isize,
|
||||
(self.dimensions.pixel_height - config.window_padding.top as usize) as isize,
|
||||
),
|
||||
),
|
||||
bg,
|
||||
);
|
||||
|
||||
if self.show_scroll_bar {
|
||||
let (scroll_top, scroll_size) = term.get_scrollbar_info();
|
||||
let thumb_size = (self.terminal_size.rows as f32 / scroll_size as f32)
|
||||
* self.terminal_size.pixel_height as f32;
|
||||
let thumb_top = (1.
|
||||
- (scroll_top + self.terminal_size.rows as i64) as f32 / scroll_size as f32)
|
||||
* self.terminal_size.pixel_height as f32;
|
||||
|
||||
let thumb_size = thumb_size.ceil() as isize;
|
||||
let thumb_top = thumb_top.ceil() as isize;
|
||||
|
||||
ctx.clear_rect(
|
||||
Rect::new(
|
||||
Point::new(
|
||||
(self
|
||||
.dimensions
|
||||
.pixel_width
|
||||
.saturating_sub(padding_right as usize))
|
||||
as isize,
|
||||
thumb_top + config.window_padding.top as isize,
|
||||
),
|
||||
Size::new(padding_right as isize, thumb_top + thumb_size),
|
||||
),
|
||||
rgbcolor_to_window_color(palette.scrollbar_thumb),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn effective_right_padding(&self, config: &ConfigHandle) -> u16 {
|
||||
effective_right_padding(config, &self.render_metrics)
|
||||
}
|
||||
|
||||
fn paint_tab_opengl(
|
||||
&mut self,
|
||||
tab: &Rc<dyn Tab>,
|
||||
@ -1205,6 +1280,35 @@ impl TermWindow {
|
||||
)?;
|
||||
}
|
||||
|
||||
if self.show_scroll_bar {
|
||||
let (scroll_top, scroll_size) = term.get_scrollbar_info();
|
||||
let thumb_size = (self.terminal_size.rows as f32 / scroll_size as f32)
|
||||
* self.terminal_size.pixel_height as f32;
|
||||
let thumb_top = (1.
|
||||
- (scroll_top + self.terminal_size.rows as i64) as f32 / scroll_size as f32)
|
||||
* self.terminal_size.pixel_height as f32;
|
||||
let gl_state = self.render_state.opengl();
|
||||
|
||||
// We reserved the final quad in the vertex buffer as the scrollbar
|
||||
let mut vb = gl_state.glyph_vertex_buffer.borrow_mut();
|
||||
let num_vert = vb.len() - VERTICES_PER_CELL;
|
||||
let mut vertices = &mut vb.slice_mut(..).unwrap().map();
|
||||
let mut quad = Quad::for_cell(num_vert / VERTICES_PER_CELL, &mut vertices);
|
||||
|
||||
// Adjust the scrollbar thumb position
|
||||
let top = (self.dimensions.pixel_height as f32 / -2.0) + thumb_top;
|
||||
let bottom = top + thumb_size;
|
||||
|
||||
let config = configuration();
|
||||
let padding = self.effective_right_padding(&config) as f32;
|
||||
|
||||
let right = self.dimensions.pixel_width as f32 / 2.;
|
||||
let left = right - padding;
|
||||
|
||||
quad.set_bg_color(rgbcolor_to_window_color(palette.scrollbar_thumb));
|
||||
quad.set_position(left, top, right, bottom);
|
||||
}
|
||||
|
||||
{
|
||||
let dirty_lines = term.get_dirty_lines();
|
||||
|
||||
|
@ -2,7 +2,7 @@ use downcast_rs::{impl_downcast, Downcast};
|
||||
use std::borrow::Cow;
|
||||
use std::ops::Range;
|
||||
use std::sync::Arc;
|
||||
use term::{CursorPosition, Line, Terminal, TerminalState};
|
||||
use term::{CursorPosition, Line, Terminal, TerminalState, VisibleRowIndex};
|
||||
use termwiz::hyperlink::Hyperlink;
|
||||
|
||||
/// Renderable allows passing something that isn't an actual term::Terminal
|
||||
@ -33,6 +33,11 @@ pub trait Renderable: Downcast {
|
||||
/// Returns physical, non-scrollback (rows, cols) for the
|
||||
/// terminal screen
|
||||
fn physical_dimensions(&self) -> (usize, usize);
|
||||
|
||||
/// Returns the potentially scrolled viewport offset, and the
|
||||
/// size of the scrollback. This information is intended to be
|
||||
/// used to render a scrollbar UI
|
||||
fn get_scrollbar_info(&self) -> (VisibleRowIndex, usize);
|
||||
}
|
||||
impl_downcast!(Renderable);
|
||||
|
||||
@ -68,4 +73,10 @@ impl Renderable for Terminal {
|
||||
fn has_dirty_lines(&self) -> bool {
|
||||
TerminalState::has_dirty_lines(self)
|
||||
}
|
||||
|
||||
fn get_scrollbar_info(&self) -> (VisibleRowIndex, usize) {
|
||||
let offset = self.get_viewport_offset();
|
||||
let num_lines = self.screen().lines.len();
|
||||
(offset, num_lines)
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ use std::sync::{Arc, Mutex};
|
||||
use std::time::{Duration, Instant};
|
||||
use term::color::ColorPalette;
|
||||
use term::selection::SelectionRange;
|
||||
use term::{Clipboard, CursorPosition, Line};
|
||||
use term::{Clipboard, CursorPosition, Line, VisibleRowIndex};
|
||||
use term::{KeyCode, KeyModifiers, MouseButton, MouseEvent, MouseEventKind, TerminalHost};
|
||||
use termwiz::hyperlink::Hyperlink;
|
||||
use termwiz::input::KeyEvent;
|
||||
@ -468,6 +468,13 @@ impl Renderable for RenderableState {
|
||||
let (cols, rows) = self.inner.borrow().surface.dimensions();
|
||||
(rows, cols)
|
||||
}
|
||||
|
||||
fn get_scrollbar_info(&self) -> (VisibleRowIndex, usize) {
|
||||
// Dummy scrollback information for now, until we
|
||||
// plumb this into the protocol
|
||||
let (_cols, rows) = self.physical_dimensions();
|
||||
(0, rows)
|
||||
}
|
||||
}
|
||||
|
||||
struct TabWriter {
|
||||
|
@ -25,7 +25,9 @@ use std::sync::{Arc, Mutex};
|
||||
use std::time::Duration;
|
||||
use term::color::ColorPalette;
|
||||
use term::selection::SelectionRange;
|
||||
use term::{CursorPosition, KeyCode, KeyModifiers, Line, MouseEvent, TerminalHost};
|
||||
use term::{
|
||||
CursorPosition, KeyCode, KeyModifiers, Line, MouseEvent, TerminalHost, VisibleRowIndex,
|
||||
};
|
||||
use termwiz::hyperlink::Hyperlink;
|
||||
use termwiz::input::{InputEvent, KeyEvent};
|
||||
use termwiz::lineedit::*;
|
||||
@ -142,6 +144,11 @@ impl Renderable for RenderableState {
|
||||
let (cols, rows) = self.inner.borrow().surface.dimensions();
|
||||
(rows, cols)
|
||||
}
|
||||
|
||||
fn get_scrollbar_info(&self) -> (VisibleRowIndex, usize) {
|
||||
let (_cols, rows) = self.physical_dimensions();
|
||||
(0, rows)
|
||||
}
|
||||
}
|
||||
|
||||
struct TermWizTerminalDomain {
|
||||
|
@ -17,6 +17,7 @@ pub struct ColorPalette {
|
||||
pub cursor_border: RgbColor,
|
||||
pub selection_fg: RgbColor,
|
||||
pub selection_bg: RgbColor,
|
||||
pub scrollbar_thumb: RgbColor,
|
||||
}
|
||||
|
||||
impl fmt::Debug for Palette256 {
|
||||
@ -187,6 +188,8 @@ impl Default for ColorPalette {
|
||||
let selection_fg = colors[AnsiColor::Black as usize];
|
||||
let selection_bg = RgbColor::new(0xff, 0xfa, 0xcd);
|
||||
|
||||
let scrollbar_thumb = RgbColor::new(0x44, 0x44, 0x44);
|
||||
|
||||
ColorPalette {
|
||||
colors: Palette256(colors),
|
||||
foreground,
|
||||
@ -196,6 +199,7 @@ impl Default for ColorPalette {
|
||||
cursor_border,
|
||||
selection_fg,
|
||||
selection_bg,
|
||||
scrollbar_thumb,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user