From 2f892e3523123129d2a6b9e7859095593169ee76 Mon Sep 17 00:00:00 2001 From: Philipp Schaffrath Date: Thu, 18 Apr 2024 21:28:25 +0200 Subject: [PATCH] Improve drop targets (#10643) This introduces multiple improvements to the drop targets. ## Hitbox shape Currently, hitboxes are rectangles, where the vertical ones reach all the way to the ends, which reduces the space for the horizontal ones, making the hitboxes a bit awkward in the corners. This new approach just determines the closest side. Visual representation: ![Frame 3](https://github.com/zed-industries/zed/assets/1282767/1cd2ca31-d9d4-41dd-87fb-1a8fbb8b7fcc) ## Hitbox size The width of the hitbox was currently always 8 rem all around. In setups with many columns or rows, or when the font size was very large, this could potentially overlap the center hitbox, not allowing to drop a tab without another split. Now the width of the hitboxes are a fraction of the smaller size of its parents width and height. This makes sure the hitboxes have the same width all around, but never fully block the center hitbox. I've also made this value configurable through the new `drop_target_size` config which takes a `f32` fraction and is set to 0.2 by default. Not sure if this is worth mentioning, but this technically allows to remove the split hitboxes all together by setting it to `0.0`, or removing the center hitbox by setting it to any value `>=0.5`. Not that this is necessary, but it would be possible now. ## Larger visualization The visual overlay when using one of the side hitboxes were also `8em` wide. Since their logical size now changed, and it can't currently be represented with GPUI (without abusing the `canvas` element), I made the visual feedback take half of the width or height of the available space, just like how other editors do this. Also, the opacity/alpha value set by a theme is currently ignored. This change now respects the themes opacity for it! ## Respect alpha value Currently, the alpha value of `drop_target.background` is ignored. Even the default themes set a value that is overwritten by a hard coded value. I have removed this hard coded value and it now respects the alpha value. This change affects existing themes, see https://github.com/zed-industries/zed/pull/10643#issuecomment-2059641528 ## ~~No more lag while dragging over gutter~~ Extracted into #10737 ~~It looks like the editor had a small optimization to drop events when hovering the gutter. This also happens while dragging a tab over the gutter, and causes some stuttering. Please correct me if this wasn't just a small optimization, but I could not derive a different reason for this code to exist.~~ Here is a video that tries to show all those changes with a before on the left, and the after on the right: https://github.com/zed-industries/zed/assets/1282767/f97f3420-513f-410f-a1c8-7966429ad348 Release Notes: - Added `drop_target_size` setting. This should be a fractional percent (e.g., `0.5`). - Improved the hitboxes for drop targets. - Updated drop targets to respect the alpha channel of the `drop_target.background` color. --- assets/settings/default.json | 2 + crates/workspace/src/pane.rs | 71 ++++++++++++++-------- crates/workspace/src/workspace_settings.rs | 6 ++ 3 files changed, 54 insertions(+), 25 deletions(-) diff --git a/assets/settings/default.json b/assets/settings/default.json index ee50d43670ccf5da538404da34adc2616581d33a..5c950e2469698ec8ff655159a1514e2d22fa7045 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -69,6 +69,8 @@ "confirm_quit": false, // Whether to restore last closed project when fresh Zed instance is opened. "restore_on_startup": "last_workspace", + // Size of the drop target in the editor. + "drop_target_size": 0.2, // Whether the cursor blinks in the editor. "cursor_blink": true, // Whether to pop the completions menu while typing in an editor without diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index c8658699ca61d4a3fa577a9c423b558df957abd8..26626f5ba16c8697d0d09ebfa23d861e0e0ffeb8 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -1721,16 +1721,35 @@ impl Pane { return; } - let edge_width = cx.rem_size() * 8; - let cursor = event.event.position; - let direction = if cursor.x < event.bounds.left() + edge_width { - Some(SplitDirection::Left) - } else if cursor.x > event.bounds.right() - edge_width { - Some(SplitDirection::Right) - } else if cursor.y < event.bounds.top() + edge_width { - Some(SplitDirection::Up) - } else if cursor.y > event.bounds.bottom() - edge_width { - Some(SplitDirection::Down) + let rect = event.bounds.size; + + let size = event.bounds.size.width.min(event.bounds.size.height) + * WorkspaceSettings::get_global(cx).drop_target_size; + + let relative_cursor = Point::new( + event.event.position.x - event.bounds.left(), + event.event.position.y - event.bounds.top(), + ); + + let direction = if relative_cursor.x < size + || relative_cursor.x > rect.width - size + || relative_cursor.y < size + || relative_cursor.y > rect.height - size + { + [ + SplitDirection::Up, + SplitDirection::Right, + SplitDirection::Down, + SplitDirection::Left, + ] + .iter() + .min_by_key(|side| match side { + SplitDirection::Up => relative_cursor.y, + SplitDirection::Right => rect.width - relative_cursor.x, + SplitDirection::Down => rect.height - relative_cursor.y, + SplitDirection::Left => relative_cursor.x, + }) + .cloned() } else { None }; @@ -2013,10 +2032,7 @@ impl Render for Pane { div() .invisible() .absolute() - .bg(theme::color_alpha( - cx.theme().colors().drop_target_background, - 0.75, - )) + .bg(cx.theme().colors().drop_target_background) .group_drag_over::("", |style| style.visible()) .group_drag_over::("", |style| style.visible()) .group_drag_over::("", |style| style.visible()) @@ -2032,17 +2048,22 @@ impl Render for Pane { .on_drop(cx.listener(move |this, paths, cx| { this.handle_external_paths_drop(paths, cx) })) - .map(|div| match self.drag_split_direction { - None => div.top_0().left_0().right_0().bottom_0(), - Some(SplitDirection::Up) => div.top_0().left_0().right_0().h_32(), - Some(SplitDirection::Down) => { - div.left_0().bottom_0().right_0().h_32() - } - Some(SplitDirection::Left) => { - div.top_0().left_0().bottom_0().w_32() - } - Some(SplitDirection::Right) => { - div.top_0().bottom_0().right_0().w_32() + .map(|div| { + let size = DefiniteLength::Fraction(0.5); + match self.drag_split_direction { + None => div.top_0().right_0().bottom_0().left_0(), + Some(SplitDirection::Up) => { + div.top_0().left_0().right_0().h(size) + } + Some(SplitDirection::Down) => { + div.left_0().bottom_0().right_0().h(size) + } + Some(SplitDirection::Left) => { + div.top_0().left_0().bottom_0().w(size) + } + Some(SplitDirection::Right) => { + div.top_0().bottom_0().right_0().w(size) + } } }), ) diff --git a/crates/workspace/src/workspace_settings.rs b/crates/workspace/src/workspace_settings.rs index 0b7bc949f6ba09459c4206c6410b9760e09add2e..4552cda3a384c023f24e33811d3ea6c7470e3f15 100644 --- a/crates/workspace/src/workspace_settings.rs +++ b/crates/workspace/src/workspace_settings.rs @@ -12,6 +12,7 @@ pub struct WorkspaceSettings { pub show_call_status_icon: bool, pub autosave: AutosaveSetting, pub restore_on_startup: RestoreOnStartupBehaviour, + pub drop_target_size: f32, } #[derive(Copy, Clone, Default, Serialize, Deserialize, JsonSchema)] @@ -50,6 +51,11 @@ pub struct WorkspaceSettingsContent { /// Values: none, last_workspace /// Default: last_workspace pub restore_on_startup: Option, + /// The size of the workspace split drop targets on the outer edges. + /// Given as a fraction that will be multiplied by the smaller dimension of the workspace. + /// + /// Default: `0.2` (20% of the smaller dimension of the workspace) + pub drop_target_size: Option, } #[derive(Deserialize)]