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

feat(window): drag resize window #208

Merged
merged 8 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions resources/panel.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/webview.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ impl Window {
"MINIMIZE" => {
self.window.set_minimized(true);
}
"MAXIMIZE" => {
"MAXIMIZE" | "DBCLICK_PANEL" => {
let is_maximized = self.window.is_maximized();
self.window.set_maximized(!is_maximized);
}
Expand Down
161 changes: 141 additions & 20 deletions src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ use webrender_api::{
units::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DevicePoint, LayoutVector2D},
ScrollLocation,
};
#[cfg(any(linux, target_os = "windows"))]
use winit::window::ResizeDirection;
use winit::{
dpi::PhysicalPosition,
event::{ElementState, TouchPhase, WindowEvent},
Expand Down Expand Up @@ -45,7 +47,7 @@ pub struct Window {
/// The WebView of this window.
pub(crate) webview: Option<WebView>,
/// The mouse physical position in the web view.
mouse_position: Cell<PhysicalPosition<f64>>,
mouse_position: Cell<Option<PhysicalPosition<f64>>>,
/// Modifiers state of the keyboard.
modifiers_state: Cell<ModifiersState>,
}
Expand Down Expand Up @@ -90,7 +92,7 @@ impl Window {
surface,
panel: None,
webview: None,
mouse_position: Cell::new(PhysicalPosition::default()),
mouse_position: Default::default(),
modifiers_state: Cell::new(ModifiersState::default()),
},
rendering_context,
Expand All @@ -99,10 +101,11 @@ impl Window {

/// Create a Verso window with the rendering context.
pub fn new_with_compositor(evl: &ActiveEventLoop, compositor: &mut IOCompositor) -> Self {
let window_attrs = WinitWindow::default_attributes()
.with_decorations(false)
.with_transparent(true);
let window = evl
.create_window(WinitWindow::default_attributes())
// .with_transparent(true)
// .with_decorations(false)
.create_window(window_attrs)
.expect("Failed to create window.");

#[cfg(macos)]
Expand All @@ -125,7 +128,7 @@ impl Window {
surface,
panel: None,
webview: None,
mouse_position: Cell::new(PhysicalPosition::default()),
mouse_position: Default::default(),
modifiers_state: Cell::new(ModifiersState::default()),
};
compositor.swap_current_window(&mut window);
Expand Down Expand Up @@ -196,12 +199,43 @@ impl Window {
WindowEvent::CursorEntered { .. } => {
compositor.swap_current_window(self);
}
WindowEvent::CursorLeft { .. } => {
self.mouse_position.set(None);
}
WindowEvent::CursorMoved { position, .. } => {
let cursor: DevicePoint = DevicePoint::new(position.x as f32, position.y as f32);
self.mouse_position.set(*position);
self.mouse_position.set(Some(*position));
compositor.on_mouse_window_move_event_class(cursor);

// handle Windows and Linux non-decoration window resize cursor
#[cfg(any(linux, target_os = "windows"))]
{
if self.is_resizable() {
let direction = self.get_drag_resize_direction();
self.set_drag_resize_cursor(direction);
}
}
}
WindowEvent::MouseInput { state, button, .. } => {
let position = match self.mouse_position.get() {
Some(position) => Point2D::new(position.x as f32, position.y as f32),
None => {
log::trace!("Mouse position is None, skipping MouseInput event.");
return false;
}
};

// handle Windows and Linux non-decoration window resize
#[cfg(any(linux, target_os = "windows"))]
{
if *state == ElementState::Pressed && *button == winit::event::MouseButton::Left
{
if self.is_resizable() {
self.drag_resize_window();
}
}
}

let button: script_traits::MouseButton = match button {
winit::event::MouseButton::Left => script_traits::MouseButton::Left,
winit::event::MouseButton::Right => script_traits::MouseButton::Right,
Expand All @@ -213,10 +247,6 @@ impl Window {
return false;
}
};
let position = Point2D::new(
self.mouse_position.get().x as f32,
self.mouse_position.get().y as f32,
);

let event: MouseWindowEvent = match state {
ElementState::Pressed => MouseWindowEvent::MouseDown(button, position),
Expand All @@ -234,6 +264,14 @@ impl Window {
compositor.on_zoom_window_event(1.0 + *delta as f32, self);
}
WindowEvent::MouseWheel { delta, phase, .. } => {
let position = match self.mouse_position.get() {
Some(position) => position,
None => {
log::trace!("Mouse position is None, skipping MouseWheel event.");
return false;
}
};

// FIXME: Pixels per line, should be configurable (from browser setting?) and vary by zoom level.
const LINE_HEIGHT: f32 = 38.0;

Expand All @@ -250,10 +288,7 @@ impl Window {
// Wheel Event
compositor.on_wheel_event(
WheelDelta { x, y, z: 0.0, mode },
DevicePoint::new(
self.mouse_position.get().x as f32,
self.mouse_position.get().y as f32,
),
DevicePoint::new(position.x as f32, position.y as f32),
);

// Scroll Event
Expand All @@ -273,10 +308,7 @@ impl Window {

compositor.on_scroll_event(
ScrollLocation::Delta(LayoutVector2D::new(x as f32, y as f32)),
DeviceIntPoint::new(
self.mouse_position.get().x as i32,
self.mouse_position.get().y as i32,
),
DeviceIntPoint::new(position.x as i32, position.y as i32),
phase,
);
}
Expand All @@ -301,7 +333,7 @@ impl Window {
clipboard: Option<&mut Clipboard>,
compositor: &mut IOCompositor,
) -> bool {
// // Handle message in Verso Panel
// Handle message in Verso Panel
if let Some(panel) = &self.panel {
if panel.webview.webview_id == webview_id {
return self.handle_servo_messages_with_panel(
Expand Down Expand Up @@ -430,6 +462,95 @@ impl Window {
}
}

// Non-decorated window resizing for Windows and Linux.
#[cfg(any(linux, target_os = "windows"))]
impl Window {
/// Check current window state is allowed to drag-resize.
fn is_resizable(&self) -> bool {
// TODO: Check if the window is in fullscreen mode.
!self.window.is_maximized() && self.window.is_resizable()
}

/// Drag resize the window.
fn drag_resize_window(&self) {
if let Some(direction) = self.get_drag_resize_direction() {
if let Err(err) = self.window.drag_resize_window(direction) {
log::error!("Failed to drag-resize window: {:?}", err);
}
}
}

/// Get drag-resize direction.
fn get_drag_resize_direction(&self) -> Option<ResizeDirection> {
let mouse_position = match self.mouse_position.get() {
Some(position) => position,
None => {
return None;
}
};

let window_size = self.window.outer_size();
let border_size = 5.0 * self.window.scale_factor();

let x_direction = if mouse_position.x < border_size {
Some(ResizeDirection::West)
} else if mouse_position.x > (window_size.width as f64 - border_size) {
Some(ResizeDirection::East)
} else {
None
};

let y_direction = if mouse_position.y < border_size {
Some(ResizeDirection::North)
} else if mouse_position.y > (window_size.height as f64 - border_size) {
Some(ResizeDirection::South)
} else {
None
};

let direction = match (x_direction, y_direction) {
(Some(ResizeDirection::East), None) => ResizeDirection::East,
(Some(ResizeDirection::West), None) => ResizeDirection::West,
(None, Some(ResizeDirection::South)) => ResizeDirection::South,
(None, Some(ResizeDirection::North)) => ResizeDirection::North,
(Some(ResizeDirection::East), Some(ResizeDirection::North)) => {
ResizeDirection::NorthEast
}
(Some(ResizeDirection::West), Some(ResizeDirection::North)) => {
ResizeDirection::NorthWest
}
(Some(ResizeDirection::East), Some(ResizeDirection::South)) => {
ResizeDirection::SouthEast
}
(Some(ResizeDirection::West), Some(ResizeDirection::South)) => {
ResizeDirection::SouthWest
}
_ => return None,
};

Some(direction)
}

/// Set drag-resize cursor when mouse is hover on the border of the window.
fn set_drag_resize_cursor(&self, direction: Option<ResizeDirection>) {
let cursor = match direction {
Some(direction) => match direction {
ResizeDirection::East => CursorIcon::EResize,
ResizeDirection::West => CursorIcon::WResize,
ResizeDirection::South => CursorIcon::SResize,
ResizeDirection::North => CursorIcon::NResize,
ResizeDirection::NorthEast => CursorIcon::NeResize,
ResizeDirection::NorthWest => CursorIcon::NwResize,
ResizeDirection::SouthEast => CursorIcon::SeResize,
ResizeDirection::SouthWest => CursorIcon::SwResize,
},
None => CursorIcon::Default,
};

self.window.set_cursor(cursor);
}
}

/* window decoration */
#[cfg(macos)]
use objc2::runtime::AnyObject;
Expand Down