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.
This commit is contained in:
Philipp Schaffrath 2024-04-18 21:28:25 +02:00 committed by GitHub
parent 5c3e5cc45d
commit 2f892e3523
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 54 additions and 25 deletions

View file

@ -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::<DraggedTab>("", |style| style.visible())
.group_drag_over::<ProjectEntryId>("", |style| style.visible())
.group_drag_over::<ExternalPaths>("", |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)
}
}
}),
)

View file

@ -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<RestoreOnStartupBehaviour>,
/// 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<f32>,
}
#[derive(Deserialize)]