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

Implement dragging and resizing of borderless window #7952

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ Notable changes to the `alacritty_terminal` crate are documented in its

## 0.14.0-dev

### Added

- Moving and resizing of windows without decorations by dragging the padding at the top or bottom-right

### Changed

- Pressing `Alt` with unicode input will now add `ESC` like for ASCII input
Expand Down
17 changes: 15 additions & 2 deletions alacritty/src/display/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use {
png::Decoder,
};

use log::debug;
use std::fmt::{self, Display, Formatter};

#[cfg(target_os = "macos")]
Expand All @@ -33,8 +34,8 @@ use winit::monitor::MonitorHandle;
#[cfg(windows)]
use winit::platform::windows::IconExtWindows;
use winit::window::{
CursorIcon, Fullscreen, ImePurpose, Theme, UserAttentionType, Window as WinitWindow,
WindowAttributes, WindowId,
CursorIcon, Fullscreen, ImePurpose, ResizeDirection, Theme, UserAttentionType,
Window as WinitWindow, WindowAttributes, WindowId,
};

use alacritty_terminal::index::Point;
Expand Down Expand Up @@ -363,6 +364,18 @@ impl Window {
self.window.set_resize_increments(Some(increments));
}

pub fn drag_window(&self) {
if let Err(err) = self.window.drag_window() {
debug!("Unable to initiate dragging the window: {}", err);
}
}

pub fn drag_resize_window(&self, direction: ResizeDirection) {
if let Err(err) = self.window.drag_resize_window(direction) {
debug!("Unable to initiate resizing the window: {}", err);
}
}

/// Toggle the window's fullscreen state.
pub fn toggle_fullscreen(&self) {
self.set_fullscreen(self.window.fullscreen().is_none());
Expand Down
2 changes: 2 additions & 0 deletions alacritty/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1582,6 +1582,7 @@ pub struct Mouse {
pub block_hint_launcher: bool,
pub hint_highlight_dirty: bool,
pub inside_text_area: bool,
pub padding_interaction: bool,
pub x: usize,
pub y: usize,
}
Expand All @@ -1599,6 +1600,7 @@ impl Default for Mouse {
hint_highlight_dirty: Default::default(),
block_hint_launcher: Default::default(),
inside_text_area: Default::default(),
padding_interaction: Default::default(),
accumulated_scroll: Default::default(),
x: Default::default(),
y: Default::default(),
Expand Down
76 changes: 72 additions & 4 deletions alacritty/src/input/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use winit::event_loop::ActiveEventLoop;
use winit::keyboard::ModifiersState;
#[cfg(target_os = "macos")]
use winit::platform::macos::ActiveEventLoopExtMacOS;
use winit::window::CursorIcon;
use winit::window::{CursorIcon, ResizeDirection};

use alacritty_terminal::event::EventListener;
use alacritty_terminal::grid::{Dimensions, Scroll};
Expand All @@ -36,7 +36,6 @@ use alacritty_terminal::vi_mode::ViMotion;
use alacritty_terminal::vte::ansi::{ClearMode, Handler};

use crate::clipboard::Clipboard;
#[cfg(target_os = "macos")]
use crate::config::window::Decorations;
use crate::config::{Action, BindingMode, MouseAction, SearchAction, UiConfig, ViAction};
use crate::display::hint::HintMatch;
Expand Down Expand Up @@ -77,6 +76,13 @@ pub struct Processor<T: EventListener, A: ActionContext<T>> {
_phantom: PhantomData<T>,
}

struct PaddingHoverinfo {
pub north: bool,
pub east: bool,
pub south: bool,
pub west: bool,
}

pub trait ActionContext<T: EventListener> {
fn write_to_pty<B: Into<Cow<'static, [u8]>>>(&self, _data: B) {}
fn mark_dirty(&mut self) {}
Expand Down Expand Up @@ -449,6 +455,19 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> {

let inside_text_area = size_info.contains_point(x, y);
let cell_side = self.cell_side(x);
let padding_hover = self.padding_hoverinfo(self.ctx.mouse(), &size_info);

let mut padding_interaction = false;
if !inside_text_area
&& !lmb_pressed
&& !rmb_pressed
&& self.ctx.config().window.decorations == Decorations::None
{
// North padding of borderless window allows to move the window, south-east and
// south-west corner allow resize.
padding_interaction = padding_hover.north
|| (padding_hover.south && (padding_hover.east || padding_hover.west));
}

let point = self.ctx.mouse().point(&size_info, display_offset);
let cell_changed = old_point != point;
Expand All @@ -457,12 +476,14 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> {
if !cell_changed
&& self.ctx.mouse().cell_side == cell_side
&& self.ctx.mouse().inside_text_area == inside_text_area
&& self.ctx.mouse().padding_interaction == padding_interaction
{
return;
}

self.ctx.mouse_mut().inside_text_area = inside_text_area;
self.ctx.mouse_mut().cell_side = cell_side;
self.ctx.mouse_mut().padding_interaction = padding_interaction;

// Update mouse state and check for URL change.
let mouse_state = self.cursor_state();
Expand Down Expand Up @@ -625,7 +646,27 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> {

// Load mouse point, treating message bar and padding as the closest cell.
let display_offset = self.ctx.terminal().grid().display_offset();
let point = self.ctx.mouse().point(&self.ctx.size_info(), display_offset);
let size_info = self.ctx.size_info();
let point = self.ctx.mouse().point(&size_info, display_offset);

if self.ctx.config().window.decorations == Decorations::None {
let padding_hover = self.padding_hoverinfo(self.ctx.mouse(), &size_info);
// When clicking the north padding, drag window.
if padding_hover.north {
self.ctx.window().drag_window();
return;
}
// When clicking the south-east padding corner, resize window.
if padding_hover.south && padding_hover.east {
self.ctx.window().drag_resize_window(ResizeDirection::SouthEast);
return;
}
// When clicking the south-west padding corner, resize window.
if padding_hover.south && padding_hover.west {
self.ctx.window().drag_resize_window(ResizeDirection::SouthWest);
return;
}
}

if let MouseButton::Left = button {
self.on_left_click(point)
Expand Down Expand Up @@ -1054,11 +1095,21 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> {
let display_offset = self.ctx.terminal().grid().display_offset();
let point = self.ctx.mouse().point(&self.ctx.size_info(), display_offset);
let hyperlink = self.ctx.terminal().grid()[point].hyperlink();
let padding_hover = self.padding_hoverinfo(self.ctx.mouse(), &self.ctx.size_info());
let padding_interaction_active = self.ctx.config().window.decorations == Decorations::None
&& self.ctx.mouse().left_button_state == ElementState::Released
&& self.ctx.mouse().right_button_state == ElementState::Released;

// Function to check if mouse is on top of a hint.
let hint_highlighted = |hint: &HintMatch| hint.should_highlight(point, hyperlink.as_ref());

if let Some(mouse_state) = self.message_bar_cursor_state() {
if padding_interaction_active && padding_hover.north {
CursorIcon::Move
} else if padding_interaction_active && padding_hover.south && padding_hover.east {
CursorIcon::SeResize
} else if padding_interaction_active && padding_hover.south && padding_hover.west {
CursorIcon::SwResize
} else if let Some(mouse_state) = self.message_bar_cursor_state() {
mouse_state
} else if self.ctx.display().highlighted_hint.as_ref().map_or(false, hint_highlighted) {
CursorIcon::Pointer
Expand Down Expand Up @@ -1103,6 +1154,23 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> {
scheduler.unschedule(timer_id);
scheduler.schedule(event, SELECTION_SCROLLING_INTERVAL, true, timer_id);
}

/// Whether the padding is hovered in the north, east, south and west direction, respectively.
fn padding_hoverinfo(&self, mouse: &Mouse, size_info: &SizeInfo) -> PaddingHoverinfo {
let x = mouse.x;
let y = mouse.y;
let padding_x = size_info.padding_x() as usize;
let padding_y = size_info.padding_y() as usize;
let width = size_info.width() as usize;
let height = size_info.height() as usize;

PaddingHoverinfo {
north: y < padding_y,
east: x > width - padding_x,
south: y > height - padding_y,
west: x < padding_x,
}
}
}

#[cfg(test)]
Expand Down