Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

force_reverse_video_selection #3440

Open
John-Gee opened this issue Apr 3, 2023 · 8 comments
Open

force_reverse_video_selection #3440

John-Gee opened this issue Apr 3, 2023 · 8 comments
Labels
enhancement New feature or request

Comments

@John-Gee
Copy link

John-Gee commented Apr 3, 2023

Hello,

Describe the solution you'd like
Following the Discussion #3432 , I would like for wezterm to be able to reverse fg and bg colors on selection like other terminal emulators can do. Since I was told no setting currently exists for this, I am opening this FR.

Additional context
reverse_color_selection

Thank you!

@John-Gee John-Gee added the enhancement New feature or request label Apr 3, 2023
@wez
Copy link
Owner

wez commented Apr 3, 2023

FWIW, the logic for this stuff is already rather complex:

pub fn compute_cell_fg_bg(&self, params: ComputeCellFgBgParams) -> ComputeCellFgBgResult {
if params.cursor.is_some() {
if let Some(bg_color_mix) = self.get_intensity_if_bell_target_ringing(
params.pane.expect("cursor only set if pane present"),
params.config,
VisualBellTarget::CursorColor,
) {
let (fg_color, bg_color) =
if self.config.force_reverse_video_cursor && params.cursor_is_default_color {
(params.bg_color, params.fg_color)
} else {
(params.cursor_fg, params.cursor_bg)
};
// interpolate between the background color
// and the the target color
let bg_color_alt = params
.config
.resolved_palette
.visual_bell
.map(|c| c.to_linear())
.unwrap_or(fg_color);
return ComputeCellFgBgResult {
fg_color,
fg_color_alt: fg_color,
fg_color_mix: 0.,
bg_color,
bg_color_alt,
bg_color_mix,
cursor_shape: Some(CursorShape::Default),
cursor_border_color: bg_color,
cursor_border_color_alt: bg_color_alt,
cursor_border_mix: bg_color_mix,
};
}
let dead_key_or_leader =
self.dead_key_status != DeadKeyStatus::None || self.leader_is_active();
if dead_key_or_leader && params.is_active_pane {
let (fg_color, bg_color) =
if self.config.force_reverse_video_cursor && params.cursor_is_default_color {
(params.bg_color, params.fg_color)
} else {
(params.cursor_fg, params.cursor_bg)
};
let color = params
.config
.resolved_palette
.compose_cursor
.map(|c| c.to_linear())
.unwrap_or(bg_color);
return ComputeCellFgBgResult {
fg_color,
fg_color_alt: fg_color,
fg_color_mix: 0.,
bg_color,
bg_color_alt: bg_color,
bg_color_mix: 0.,
cursor_shape: Some(CursorShape::Default),
cursor_border_color: color,
cursor_border_color_alt: color,
cursor_border_mix: 0.,
};
}
}
let (cursor_shape, visibility) = match params.cursor {
Some(cursor) => (
params
.config
.default_cursor_style
.effective_shape(cursor.shape),
cursor.visibility,
),
_ => (CursorShape::default(), CursorVisibility::Hidden),
};
let focused_and_active = self.focused.is_some() && params.is_active_pane;
let (fg_color, bg_color, cursor_bg) = match (
params.selected,
focused_and_active,
cursor_shape,
visibility,
) {
// Selected text overrides colors
(true, _, _, CursorVisibility::Hidden) => (
params.selection_fg.when_fully_transparent(params.fg_color),
params.selection_bg,
params.cursor_bg,
),
// block Cursor cell overrides colors
(
_,
true,
CursorShape::BlinkingBlock | CursorShape::SteadyBlock,
CursorVisibility::Visible,
) => {
if self.config.force_reverse_video_cursor && params.cursor_is_default_color {
(params.bg_color, params.fg_color, params.fg_color)
} else {
(
params.cursor_fg.when_fully_transparent(params.fg_color),
params.cursor_bg,
params.cursor_bg,
)
}
}
(
_,
true,
CursorShape::BlinkingUnderline
| CursorShape::SteadyUnderline
| CursorShape::BlinkingBar
| CursorShape::SteadyBar,
CursorVisibility::Visible,
) => {
if self.config.force_reverse_video_cursor && params.cursor_is_default_color {
(params.fg_color, params.bg_color, params.fg_color)
} else {
(params.fg_color, params.bg_color, params.cursor_bg)
}
}
// Normally, render the cell as configured (or if the window is unfocused)
_ => (params.fg_color, params.bg_color, params.cursor_border_color),
};
let blinking = params.cursor.is_some()
&& params.is_active_pane
&& cursor_shape.is_blinking()
&& params.config.cursor_blink_rate != 0
&& self.focused.is_some();
let mut fg_color_alt = fg_color;
let bg_color_alt = bg_color;
let mut fg_color_mix = 0.;
let bg_color_mix = 0.;
let mut cursor_border_color_alt = cursor_bg;
let mut cursor_border_mix = 0.;
if blinking {
let mut color_ease = self.cursor_blink_state.borrow_mut();
color_ease.update_start(self.prev_cursor.last_cursor_movement());
let (intensity, next) = color_ease.intensity_continuous();
cursor_border_mix = intensity;
cursor_border_color_alt = params.bg_color;
if matches!(
cursor_shape,
CursorShape::BlinkingBlock | CursorShape::SteadyBlock,
) {
fg_color_alt = params.fg_color;
fg_color_mix = intensity;
}
self.update_next_frame_time(Some(next));
}
ComputeCellFgBgResult {
fg_color,
fg_color_alt,
bg_color,
bg_color_alt,
fg_color_mix,
bg_color_mix,
cursor_border_color: cursor_bg,
cursor_border_color_alt,
cursor_border_mix,
cursor_shape: if visibility == CursorVisibility::Visible {
match cursor_shape {
CursorShape::BlinkingBlock | CursorShape::SteadyBlock if focused_and_active => {
Some(CursorShape::Default)
}
// When not focused, convert bar to block to make it more visually
// distinct from the focused bar in another pane
_shape if !focused_and_active => Some(CursorShape::SteadyBlock),
shape => Some(shape),
}
} else {
None
},
}
}

I'm not eager to try adding more options in here unless there is a really clear and pressing need.
There would need to be answers for questions like:

  • How do you treat reverse video cursor coupled with reverse video selection?

and other combinations of factors/configs.

@John-Gee
Copy link
Author

John-Gee commented Apr 3, 2023

Hmmm, I don't really know much about this so I'll take you word for it about complexity, but for reverse video cursor coupled with reverse video selection couldn't you disallow having both for a start? Since you already have the cursor one you could let that one take priority, and so if both are set the selection one would have no effect.

There's obviously no clear and pressing need, it's just a look thing, I can definitely go without it, I'm just trying to replicate my old Konsole look on WezTerm.

@Tomiyou
Copy link

Tomiyou commented Jul 17, 2023

As far as I know, the logic for this should be fairly simple. If reverse video for selection is enabled in config, simply swap foreground and background color for selected text. I looked at the code, here is what that would look like IMO:

            // Selected text overrides colors
            (true, _, _, CursorVisibility::Hidden) => if self.config.force_reverse_video_selection {(
                params.bg_color,
                params.fg_color,
                params.cursor_bg,
            )} else {(
                params.selection_fg.when_fully_transparent(params.fg_color),
                params.selection_bg,
                params.cursor_bg,
            )},

While it is not a pressing need, this is the default behavior in gnome terminal, terminator, konsole, kitty and a couple more I think. It ensures that selected text is always very easy to read and contrasts the background nicely (no matter the colors), since the selection is simply the user theme color swapped. I am used to it to the point, that I actually can't live without it 😅

I tried to implement this feature myself. I ran into an issue however, right now the selection in a line is rendered as a rectangle with a single color:

            let mut quad = self
                .filled_rectangle(
                    layers,
                    0,
                    euclid::rect(start, params.top_pixel_y, width, cell_height),
                    params.selection_bg,
                )
                .context("filled_rectangle")?;

This feature would require that selection background color depends on the color of the text in front of it. So if you have a line with first white text, then red text, then more white text, that would require 3 separate rectangles each with its own color. I don't know the performance implications of this, I thought background color was rendered on a per character basis.

How would I go about implementing this?

@wez
Copy link
Owner

wez commented Jul 17, 2023

if you're interested in working on this, the only changes you need to make are in the compute_cell_fg_bg method I linked to above; the struct that it returns tells the rest of the code what the effect fg and bg colors should be for the cell:

#[derive(Debug)]
pub struct ComputeCellFgBgResult {
pub fg_color: LinearRgba,
pub fg_color_alt: LinearRgba,
pub bg_color: LinearRgba,
pub bg_color_alt: LinearRgba,
pub fg_color_mix: f32,
pub bg_color_mix: f32,
pub cursor_border_color: LinearRgba,
pub cursor_border_color_alt: LinearRgba,
pub cursor_border_mix: f32,
pub cursor_shape: Option<CursorShape>,
}

it is passed this set of information that encodes all of the pertinent information for that cell:

pub struct ComputeCellFgBgParams<'a> {
pub selected: bool,
pub cursor: Option<&'a StableCursorPosition>,
pub fg_color: LinearRgba,
pub bg_color: LinearRgba,
pub is_active_pane: bool,
pub config: &'a ConfigHandle,
pub selection_fg: LinearRgba,
pub selection_bg: LinearRgba,
pub cursor_fg: LinearRgba,
pub cursor_bg: LinearRgba,
pub cursor_is_default_color: bool,
pub cursor_border_color: LinearRgba,
pub pane: Option<&'a Arc<dyn Pane>>,
}

@Tomiyou
Copy link

Tomiyou commented Jul 18, 2023

Okay, I checked the compute_cell_fg_bg method. I can successfully swap the color of the selected text like I want, but I am having issues swapping the color of the selection background. It seems to me like bg_color the method compute_cell_fg_bg() returns is not used for drawing anything to the screen at all and is only used to prevent anti-aliasing ghostly outline. Am I missing something?

The only place where selection background is drawn is with a color set in the config file, am I correct?

// Render the selection background color.
// This always uses a physical x position, regardles of the line
// direction.
let selection_pixel_range = if !params.selection.is_empty() {
let start = params.left_pixel_x + (params.selection.start as f32 * cell_width);
let width = (params.selection.end - params.selection.start) as f32 * cell_width;
let mut quad = self
.filled_rectangle(
layers,
0,
euclid::rect(start, params.top_pixel_y, width, cell_height),
params.selection_bg,
)
.context("filled_rectangle")?;
quad.set_hsv(hsv);
start..start + width
} else {
0.0..0.0
};

I'd like to make selection background adapt to the foreground color of the text that selection covers.

@wez
Copy link
Owner

wez commented Jul 18, 2023

ah, then I think you'll need use compute_cell_fg_bg for the background painting logic which is a bit further up in the code:

// Make a pass to compute background colors.
// Need to consider:
// * background when it is not the default color
// * Reverse video attribute
for item in shaped.iter() {

the thing to be careful about here is that we paint the background first, then composite the selection rectangle over the top; the selection color can have an alpha component and blend together, so it isn't valid to simply draw the selection background instead of the main background. There needs to be two separate rectangles of different colors.

They can be both draw in the same loop, so maybe the thing to try is adding a compute_cell_fg_bg just before:

// Underlines
if item.underline_tex_rect != params.white_space {
// Draw one per cell, otherwise curly underlines

and using that to draw the selection color. Then this logic can be removed:

let mut quad = self
.filled_rectangle(
layers,
0,
euclid::rect(start, params.top_pixel_y, width, cell_height),
params.selection_bg,
)
.context("filled_rectangle")?;
quad.set_hsv(hsv);

Tomiyou pushed a commit to Tomiyou/wezterm that referenced this issue Aug 4, 2023
This config option allows selection to behave similarly as the cursor
does when force_reverse_video_cursor config option is set to true. With
this new config set to true, selected text's foreground and background
colors are swapped. Addresses issue wez#3440
@Tomiyou
Copy link

Tomiyou commented Aug 4, 2023

Hey, I added a PR for this feature #4093. When the new config option I added is set to true, the selection behaves exactly as it does in Terminator (what I used before Wezterm). I left comments in the code, so hopefully my code makes sense. Let me know what you think about it.

There is one more thing to address here and that is the cursor itself. When the cursor itself is selected, and force reverse video cursor config is enabled, colors should be swapped again for the cursor. Otherwise the cursor and the text is covers has the same color as the selection making it illegible.

Here is the example of PR in action. Notice how the reverse video cursor is the same color as the selection itself. That needs fixing, otherwise I think it's perfect.

Screenshot from 2023-08-04 19-10-59

Tomiyou pushed a commit to Tomiyou/wezterm that referenced this issue Sep 9, 2023
This config option allows selection to behave similarly as the cursor
does when force_reverse_video_cursor config option is set to true. With
this new config set to true, selected text's foreground and background
colors are swapped. Addresses issue wez#3440
Tomiyou pushed a commit to Tomiyou/wezterm that referenced this issue Sep 15, 2023
This config option allows selection to behave similarly as the cursor
does when force_reverse_video_cursor config option is set to true. With
this new config set to true, selected text's foreground and background
colors are swapped. Addresses issue wez#3440
Tomiyou pushed a commit to Tomiyou/wezterm that referenced this issue Sep 21, 2023
This config option allows selection to behave similarly as the cursor
does when force_reverse_video_cursor config option is set to true. With
this new config set to true, selected text's foreground and background
colors are swapped. Addresses issue wez#3440
@John-Gee
Copy link
Author

Thank you for working on this @Tomiyou !

Tomiyou pushed a commit to Tomiyou/wezterm that referenced this issue Oct 2, 2023
This config option allows selection to behave similarly as the cursor
does when force_reverse_video_cursor config option is set to true. With
this new config set to true, selected text's foreground and background
colors are swapped. Addresses issue wez#3440
Tomiyou added a commit to Tomiyou/wezterm that referenced this issue Oct 2, 2023
This config option allows selection to behave similarly as the cursor
does when force_reverse_video_cursor config option is set to true. With
this new config set to true, selected text's foreground and background
colors are swapped. Addresses issue wez#3440
Tomiyou added a commit to Tomiyou/wezterm that referenced this issue Oct 3, 2023
This config option allows selection to behave similarly as the cursor
does when force_reverse_video_cursor config option is set to true. With
this new config set to true, selected text's foreground and background
colors are swapped. Addresses issue wez#3440
Tomiyou added a commit to Tomiyou/wezterm that referenced this issue Oct 7, 2023
This config option allows selection to behave similarly as the cursor
does when force_reverse_video_cursor config option is set to true. With
this new config set to true, selected text's foreground and background
colors are swapped. Addresses issue wez#3440
Tomiyou added a commit to Tomiyou/wezterm that referenced this issue Oct 9, 2023
This config option allows selection to behave similarly as the cursor
does when force_reverse_video_cursor config option is set to true. With
this new config set to true, selected text's foreground and background
colors are swapped. Addresses issue wez#3440
Tomiyou added a commit to Tomiyou/wezterm that referenced this issue Jan 19, 2024
This config option allows selection to behave similarly as the cursor
does when force_reverse_video_cursor config option is set to true. With
this new config set to true, selected text's foreground and background
colors are swapped. Addresses issue wez#3440
Tomiyou added a commit to Tomiyou/wezterm that referenced this issue Jan 24, 2024
This config option allows selection to behave similarly as the cursor
does when force_reverse_video_cursor config option is set to true. With
this new config set to true, selected text's foreground and background
colors are swapped. Addresses issue wez#3440
Tomiyou added a commit to Tomiyou/wezterm that referenced this issue Feb 2, 2024
This config option allows selection to behave similarly as the cursor
does when force_reverse_video_cursor config option is set to true. With
this new config set to true, selected text's foreground and background
colors are swapped. Addresses issue wez#3440
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants