mirror of
https://github.com/alacritty/alacritty
synced 2024-10-04 14:59:26 +00:00
Fix mouse pasting in mouse mode with shift
It is now possible to paste in mouse mode again by making use of the `shift` key while pressing the mouse button reserved for PasteSelection. All mouse bindings are now also matching the modifiers in a relaxed way, so extra modifiers are ignored.
This commit is contained in:
parent
a7d9554038
commit
2c37da48b5
|
@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
- Moved `cursor_style` to `cursor.style`
|
- Moved `cursor_style` to `cursor.style`
|
||||||
- Moved `unfocused_hollow_cursor` to `cursor.unfocused_hollow`
|
- Moved `unfocused_hollow_cursor` to `cursor.unfocused_hollow`
|
||||||
- Moved `hide_cursor_when_typing` to `mouse.hide_when_typing`
|
- Moved `hide_cursor_when_typing` to `mouse.hide_when_typing`
|
||||||
|
- Mouse bindings now ignore additional modifiers
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
|
@ -32,6 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
- Fixed rendering cursors other than rectangular with the RustType backend
|
- Fixed rendering cursors other than rectangular with the RustType backend
|
||||||
- Selection memory leak and glitches in the alternate screen buffer
|
- Selection memory leak and glitches in the alternate screen buffer
|
||||||
- Invalid default configuration on macOS and Linux
|
- Invalid default configuration on macOS and Linux
|
||||||
|
- Middle mouse pasting if mouse mode is enabled
|
||||||
|
|
||||||
## Version 0.2.1
|
## Version 0.2.1
|
||||||
|
|
||||||
|
|
|
@ -106,17 +106,6 @@ pub struct Url {
|
||||||
pub modifiers: ModifiersState,
|
pub modifiers: ModifiersState,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Url {
|
|
||||||
// Make sure that modifiers in the config are always present,
|
|
||||||
// but ignore surplus modifiers.
|
|
||||||
pub fn mods_match_relaxed(&self, mods: ModifiersState) -> bool {
|
|
||||||
!((self.modifiers.shift && !mods.shift)
|
|
||||||
|| (self.modifiers.ctrl && !mods.ctrl)
|
|
||||||
|| (self.modifiers.alt && !mods.alt)
|
|
||||||
|| (self.modifiers.logo && !mods.logo))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize_modifiers<'a, D>(deserializer: D) -> ::std::result::Result<ModifiersState, D::Error>
|
fn deserialize_modifiers<'a, D>(deserializer: D) -> ::std::result::Result<ModifiersState, D::Error>
|
||||||
where D: de::Deserializer<'a>
|
where D: de::Deserializer<'a>
|
||||||
{
|
{
|
||||||
|
|
75
src/input.rs
75
src/input.rs
|
@ -113,7 +113,8 @@ impl<T: Eq> Binding<T> {
|
||||||
&self,
|
&self,
|
||||||
mode: TermMode,
|
mode: TermMode,
|
||||||
mods: ModifiersState,
|
mods: ModifiersState,
|
||||||
input: &T
|
input: &T,
|
||||||
|
relaxed: bool,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// Check input first since bindings are stored in one big list. This is
|
// Check input first since bindings are stored in one big list. This is
|
||||||
// the most likely item to fail so prioritizing it here allows more
|
// the most likely item to fail so prioritizing it here allows more
|
||||||
|
@ -121,15 +122,15 @@ impl<T: Eq> Binding<T> {
|
||||||
self.trigger == *input &&
|
self.trigger == *input &&
|
||||||
self.mode_matches(mode) &&
|
self.mode_matches(mode) &&
|
||||||
self.not_mode_matches(mode) &&
|
self.not_mode_matches(mode) &&
|
||||||
self.mods_match(mods)
|
self.mods_match(mods, relaxed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Binding<T> {
|
impl<T> Binding<T> {
|
||||||
/// Execute the action associate with this binding
|
/// Execute the action associate with this binding
|
||||||
#[inline]
|
#[inline]
|
||||||
fn execute<A: ActionContext>(&self, ctx: &mut A) {
|
fn execute<A: ActionContext>(&self, ctx: &mut A, mouse_mode: bool) {
|
||||||
self.action.execute(ctx)
|
self.action.execute(ctx, mouse_mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -143,13 +144,12 @@ impl<T> Binding<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check that two mods descriptions for equivalence
|
/// Check that two mods descriptions for equivalence
|
||||||
///
|
|
||||||
/// Optimized to use single check instead of four (one per modifier)
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn mods_match(&self, mods: ModifiersState) -> bool {
|
fn mods_match(&self, mods: ModifiersState, relaxed: bool) -> bool {
|
||||||
assert_eq_size!(ModifiersState, u32);
|
if relaxed {
|
||||||
unsafe {
|
self.mods.relaxed_eq(mods)
|
||||||
mem::transmute_copy::<_, u32>(&self.mods) == mem::transmute_copy::<_, u32>(&mods)
|
} else {
|
||||||
|
self.mods == mods
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -204,7 +204,7 @@ pub enum Action {
|
||||||
|
|
||||||
impl Action {
|
impl Action {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn execute<A: ActionContext>(&self, ctx: &mut A) {
|
fn execute<A: ActionContext>(&self, ctx: &mut A, mouse_mode: bool) {
|
||||||
match *self {
|
match *self {
|
||||||
Action::Esc(ref s) => {
|
Action::Esc(ref s) => {
|
||||||
ctx.scroll(Scroll::Bottom);
|
ctx.scroll(Scroll::Bottom);
|
||||||
|
@ -223,8 +223,7 @@ impl Action {
|
||||||
},
|
},
|
||||||
Action::PasteSelection => {
|
Action::PasteSelection => {
|
||||||
// Only paste if mouse events are not captured by an application
|
// Only paste if mouse events are not captured by an application
|
||||||
let mouse_modes = TermMode::MOUSE_REPORT_CLICK | TermMode::MOUSE_DRAG | TermMode::MOUSE_MOTION;
|
if !mouse_mode {
|
||||||
if !ctx.terminal_mode().intersects(mouse_modes) {
|
|
||||||
Clipboard::new()
|
Clipboard::new()
|
||||||
.and_then(|clipboard| clipboard.load_selection() )
|
.and_then(|clipboard| clipboard.load_selection() )
|
||||||
.map(|contents| { self.paste(ctx, &contents) })
|
.map(|contents| { self.paste(ctx, &contents) })
|
||||||
|
@ -313,6 +312,21 @@ impl Action {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trait RelaxedEq<T: ?Sized = Self> {
|
||||||
|
fn relaxed_eq(&self, other: T) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RelaxedEq for ModifiersState {
|
||||||
|
// Make sure that modifiers in the config are always present,
|
||||||
|
// but ignore surplus modifiers.
|
||||||
|
fn relaxed_eq(&self, other: Self) -> bool {
|
||||||
|
!((self.shift && !other.shift)
|
||||||
|
|| (self.ctrl && !other.ctrl)
|
||||||
|
|| (self.alt && !other.alt)
|
||||||
|
|| (self.logo && !other.logo))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<&'static str> for Action {
|
impl From<&'static str> for Action {
|
||||||
fn from(s: &'static str) -> Action {
|
fn from(s: &'static str) -> Action {
|
||||||
Action::Esc(s.into())
|
Action::Esc(s.into())
|
||||||
|
@ -344,13 +358,16 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
|
||||||
self.ctx.mouse_mut().block_url_launcher = true;
|
self.ctx.mouse_mut().block_url_launcher = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.ctx.mouse().left_button_state == ElementState::Pressed &&
|
if self.ctx.mouse().left_button_state == ElementState::Pressed
|
||||||
( modifiers.shift || !self.ctx.terminal_mode().intersects(report_mode))
|
&& (modifiers.shift || !self.ctx.terminal_mode().intersects(report_mode))
|
||||||
{
|
{
|
||||||
self.ctx.update_selection(Point {
|
self.ctx.update_selection(
|
||||||
line: point.line,
|
Point {
|
||||||
col: point.col
|
line: point.line,
|
||||||
}, cell_side);
|
col: point.col,
|
||||||
|
},
|
||||||
|
cell_side,
|
||||||
|
);
|
||||||
} else if self.ctx.terminal_mode().intersects(motion_mode)
|
} else if self.ctx.terminal_mode().intersects(motion_mode)
|
||||||
// Only report motion when changing cells
|
// Only report motion when changing cells
|
||||||
&& (prev_line != self.ctx.mouse().line || prev_col != self.ctx.mouse().column)
|
&& (prev_line != self.ctx.mouse().line || prev_col != self.ctx.mouse().column)
|
||||||
|
@ -520,7 +537,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
|
||||||
|
|
||||||
// Spawn URL launcher when clicking on URLs
|
// Spawn URL launcher when clicking on URLs
|
||||||
fn launch_url(&self, modifiers: ModifiersState) -> Option<()> {
|
fn launch_url(&self, modifiers: ModifiersState) -> Option<()> {
|
||||||
if !self.mouse_config.url.mods_match_relaxed(modifiers)
|
if !self.mouse_config.url.modifiers.relaxed_eq(modifiers)
|
||||||
|| self.ctx.mouse().block_url_launcher
|
|| self.ctx.mouse().block_url_launcher
|
||||||
{
|
{
|
||||||
return None;
|
return None;
|
||||||
|
@ -711,10 +728,11 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
|
||||||
self.ctx.terminal_mode(),
|
self.ctx.terminal_mode(),
|
||||||
input.modifiers,
|
input.modifiers,
|
||||||
&Key::Scancode(input.scancode),
|
&Key::Scancode(input.scancode),
|
||||||
|
false,
|
||||||
),
|
),
|
||||||
_ => if let Some(key) = input.virtual_keycode {
|
_ => if let Some(key) = input.virtual_keycode {
|
||||||
let key = Key::from_glutin_input(key);
|
let key = Key::from_glutin_input(key);
|
||||||
binding.is_triggered_by(self.ctx.terminal_mode(), input.modifiers, &key)
|
binding.is_triggered_by(self.ctx.terminal_mode(), input.modifiers, &key, false)
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
},
|
},
|
||||||
|
@ -722,7 +740,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
|
||||||
|
|
||||||
if is_triggered {
|
if is_triggered {
|
||||||
// binding was triggered; run the action
|
// binding was triggered; run the action
|
||||||
binding.execute(&mut self.ctx);
|
binding.execute(&mut self.ctx, false);
|
||||||
has_binding = true;
|
has_binding = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -739,9 +757,14 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
|
||||||
fn process_mouse_bindings(&mut self, mods: ModifiersState, button: MouseButton) -> bool {
|
fn process_mouse_bindings(&mut self, mods: ModifiersState, button: MouseButton) -> bool {
|
||||||
let mut has_binding = false;
|
let mut has_binding = false;
|
||||||
for binding in self.mouse_bindings {
|
for binding in self.mouse_bindings {
|
||||||
if binding.is_triggered_by(self.ctx.terminal_mode(), mods, &button) {
|
if binding.is_triggered_by(self.ctx.terminal_mode(), mods, &button, true) {
|
||||||
// binding was triggered; run the action
|
// binding was triggered; run the action
|
||||||
binding.execute(&mut self.ctx);
|
let mouse_mode = !mods.shift && self.ctx.terminal_mode().intersects(
|
||||||
|
TermMode::MOUSE_REPORT_CLICK
|
||||||
|
| TermMode::MOUSE_DRAG
|
||||||
|
| TermMode::MOUSE_MOTION
|
||||||
|
);
|
||||||
|
binding.execute(&mut self.ctx, mouse_mode);
|
||||||
has_binding = true;
|
has_binding = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -944,9 +967,9 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn $name() {
|
fn $name() {
|
||||||
if $triggers {
|
if $triggers {
|
||||||
assert!($binding.is_triggered_by($mode, $mods, &KEY));
|
assert!($binding.is_triggered_by($mode, $mods, &KEY, false));
|
||||||
} else {
|
} else {
|
||||||
assert!(!$binding.is_triggered_by($mode, $mods, &KEY));
|
assert!(!$binding.is_triggered_by($mode, $mods, &KEY, false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue