Skip to content

Commit

Permalink
implements ad hoc dimming
Browse files Browse the repository at this point in the history
  • Loading branch information
farwyler committed Jul 2, 2022
1 parent a94db20 commit bb4fec7
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 53 deletions.
16 changes: 15 additions & 1 deletion helix-term/src/compositor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ pub trait Component: Any + AnyComponent {
fn id(&self) -> Option<&'static str> {
None
}

fn is_opaque(&self) -> bool {
true
}
}

use anyhow::Context as AnyhowContext;
Expand Down Expand Up @@ -197,8 +201,18 @@ impl Compositor {

let area = *surface.area();

for layer in &mut self.layers {
let dim_backdrops = cx.editor.config().dim.overlay_backdrops;
let mut layers = self.layers.iter_mut();
while let Some(layer) = layers.next() {
// begin dimming if enabled and any overlay layers follow
let dimmed = dim_backdrops.filter(|_| layers.as_slice().iter().any(|l| !l.is_opaque()));
if let Some(shade) = dimmed {
surface.begin_dimmed(shade);
}
layer.render(area, surface, cx);
if dimmed.is_some() {
surface.end_dimmed();
}
}

let (pos, kind) = self.cursor(area, cx.editor);
Expand Down
9 changes: 9 additions & 0 deletions helix-term/src/ui/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ impl EditorView {
let area = view.area;
let theme = &editor.theme;

let dimmed = editor.config().dim.unfocused_views.filter(|_| !is_focused);
if let Some(shade) = dimmed {
surface.begin_dimmed(shade);
}

// DAP: Highlight current stack frame position
let stack_frame = editor.debugger.as_ref().and_then(|debugger| {
if let (Some(frame), Some(thread_id)) = (debugger.active_frame, debugger.thread_id) {
Expand Down Expand Up @@ -162,6 +167,10 @@ impl EditorView {
.clip_top(view.area.height.saturating_sub(1))
.clip_bottom(1); // -1 from bottom to remove commandline
self.render_statusline(editor, doc, view, statusline_area, surface, is_focused);

if dimmed.is_some() {
surface.end_dimmed();
}
}

pub fn render_rulers(
Expand Down
24 changes: 4 additions & 20 deletions helix-term/src/ui/overlay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,26 +46,6 @@ fn clip_rect_relative(rect: Rect, percent_horizontal: u8, percent_vertical: u8)
impl<T: Component + 'static> Component for Overlay<T> {
fn render(&mut self, area: Rect, frame: &mut Buffer, ctx: &mut Context) {
let dimensions = (self.calc_child_size)(area);
if let Some(shade) = ctx.editor.config().dim.overlay_backdrops {
frame.dim(area.clip_left(dimensions.right()), shade);
frame.dim(area.with_width(dimensions.left()), shade);
frame.dim(
Rect {
y: 0,
height: dimensions.y,
..dimensions
},
shade,
);
frame.dim(
Rect {
y: dimensions.bottom(),
height: area.height - dimensions.bottom(),
..dimensions
},
shade,
);
}
self.content.render(dimensions, frame, ctx)
}

Expand All @@ -90,4 +70,8 @@ impl<T: Component + 'static> Component for Overlay<T> {
let dimensions = (self.calc_child_size)(area);
self.content.cursor(dimensions, ctx)
}

fn is_opaque(&self) -> bool {
false
}
}
62 changes: 30 additions & 32 deletions helix-tui/src/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::text::{Span, Spans};
use helix_core::unicode::width::UnicodeWidthStr;
use helix_view::graphics::{Color, Modifier, Rect, Style};
use std::cmp::min;
use std::collections::VecDeque;
use unicode_segmentation::UnicodeSegmentation;

/// A buffer cell
Expand Down Expand Up @@ -108,6 +109,8 @@ pub struct Buffer {
/// The content of the buffer. The length of this Vec should always be equal to area.width *
/// area.height
pub content: Vec<Cell>,
/// Stack of dimming operations that is applied to all set_style invocations
dim_operations: VecDeque<i8>,
}

impl Buffer {
Expand All @@ -124,7 +127,11 @@ impl Buffer {
for _ in 0..size {
content.push(cell.clone());
}
Buffer { area, content }
Buffer {
area,
content,
dim_operations: VecDeque::new(),
}
}

/// Returns a Buffer containing the given lines
Expand Down Expand Up @@ -327,7 +334,8 @@ impl Buffer {
}

self.content[index].set_symbol(s);
self.content[index].set_style(style(byte_offset));
let style = self.applied_style(style(byte_offset));
self.content[index].set_style(style);
// Reset following cells if multi-width (they would be hidden by the grapheme),
for i in index + 1..index + width {
self.content[i].reset();
Expand Down Expand Up @@ -361,7 +369,8 @@ impl Buffer {
break;
}
self.content[start].set_symbol(s);
self.content[start].set_style(style(byte_offset));
let style = self.applied_style(style(byte_offset));
self.content[start].set_style(style);
for i in start + 1..index {
self.content[i].reset();
}
Expand All @@ -386,6 +395,7 @@ impl Buffer {
return (x, y);
}

let style = self.applied_style(style);
let mut index = self.index_of(x, y);
let mut x_offset = x as usize;
let max_x_offset = min(self.area.right() as usize, width.saturating_add(x as usize));
Expand Down Expand Up @@ -451,7 +461,24 @@ impl Buffer {
}
}

/// Begin dimming. Apply shade to all set_style calls, until end_dimmed is called
pub fn begin_dimmed(&mut self, shade: i8) {
self.dim_operations.push_back(shade);
}

/// Remove last dimming operation
pub fn end_dimmed(&mut self) {
assert!(!self.dim_operations.is_empty());
self.dim_operations.pop_back();
}

#[inline]
fn applied_style(&self, style: Style) -> Style {
self.dim_operations.iter().fold(style, |a, s| a.dimmed(*s))
}

pub fn set_style(&mut self, area: Rect, style: Style) {
let style = self.applied_style(style);
for y in area.top()..area.bottom() {
for x in area.left()..area.right() {
self[(x, y)].set_style(style);
Expand Down Expand Up @@ -582,35 +609,6 @@ impl Buffer {
}
updates
}

/// Apply shade to all cells in area.
///
/// - `shade = 0`: set text modifier `DIM`
/// - `shade < 0`: darken rgb color
/// - `shade > 0`: lighten rgb color
pub fn dim(&mut self, area: Rect, shade: i8) {
let alpha = i32::from(shade).unsigned_abs() << 1;
let (src_factor, dst_factor) = (alpha, 256 - alpha);
let src = if shade > 0 { 255u32 } else { 0u32 } * src_factor;
let shaded = |dst_color: u8| ((u32::from(dst_color) * dst_factor + src) >> 8) as u8;

for y in area.top()..area.bottom() {
for x in area.left()..area.right() {
let cell = &mut self[(x, y)];

if shade == 0 {
cell.modifier.insert(Modifier::DIM);
} else {
if let Color::Rgb(r, g, b) = cell.fg {
cell.fg = Color::Rgb(shaded(r), shaded(g), shaded(b))
};
if let Color::Rgb(r, g, b) = cell.bg {
cell.bg = Color::Rgb(shaded(r), shaded(g), shaded(b))
}
}
}
}
}
}

impl std::ops::Index<(u16, u16)> for Buffer {
Expand Down
25 changes: 25 additions & 0 deletions helix-view/src/graphics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,31 @@ impl Style {

self
}

/// Return new Style with applied shade.
///
/// - `shade = 0`: set text modifier `DIM`
/// - `shade < 0`: darken rgb color
/// - `shade > 0`: lighten rgb color
pub fn dimmed(self, shade: i8) -> Style {
let alpha = i32::from(shade).unsigned_abs() << 1;
let (src_factor, dst_factor) = (alpha, 256 - alpha);
let src = if shade > 0 { 255u32 } else { 0u32 } * src_factor;
let shaded = |dst_color: u8| ((u32::from(dst_color) * dst_factor + src) >> 8) as u8;
let mut res = self;
if shade == 0 {
res.add_modifier.insert(Modifier::DIM);
res.sub_modifier.remove(Modifier::DIM);
} else {
if let Some(Color::Rgb(r, g, b)) = res.fg {
res.fg = Some(Color::Rgb(shaded(r), shaded(g), shaded(b)))
};
if let Some(Color::Rgb(r, g, b)) = res.bg {
res.bg = Some(Color::Rgb(shaded(r), shaded(g), shaded(b)))
}
}
res
}
}

#[cfg(test)]
Expand Down

0 comments on commit bb4fec7

Please sign in to comment.