1
1
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:
Wez Furlong 2019-12-22 18:57:54 -08:00
parent c961f934f2
commit ec609a2e41
9 changed files with 213 additions and 18 deletions

View File

@ -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() {

View File

@ -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

View File

@ -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);
}
}

View File

@ -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(

View File

@ -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();

View File

@ -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)
}
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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,
}
}
}