fix: exclude scroll area padding from pane sizing
This commit is contained in:
+159
-141
@@ -7,8 +7,8 @@ use gpui::{
|
||||
App, Axis, Bounds, CursorStyle, DispatchPhase, ElementId, HitboxBehavior,
|
||||
InteractiveElement as _, IntoElement, KeyDownEvent, MouseButton, MouseDownEvent,
|
||||
MouseMoveEvent, MouseUpEvent, ParentElement as _, Pixels, Point, RenderOnce, Role,
|
||||
SharedString, StatefulInteractiveElement as _, Styled as _, Window, canvas, div, fill, point,
|
||||
px,
|
||||
SharedString, StatefulInteractiveElement as _, Styled as _, Window, canvas, deferred, div,
|
||||
fill, point, px,
|
||||
};
|
||||
use gpui_native_theme::{ActiveTheme as _, Size};
|
||||
use gpui_native_utils::{focus_handle, focus_ring};
|
||||
@@ -100,6 +100,9 @@ impl RenderOnce for ResizeHandle {
|
||||
let border = theme.colors.border;
|
||||
let ring = theme.colors.ring;
|
||||
let thickness = theme.sizes.resolve(Size::default()).dense_padding_x;
|
||||
// Half the handle overlaps each pane so the hairline lands on their shared boundary,
|
||||
// leaving the handle no layout footprint of its own.
|
||||
let straddle = -thickness / 2.0;
|
||||
|
||||
let label = self
|
||||
.a11y_label
|
||||
@@ -114,6 +117,7 @@ impl RenderOnce for ResizeHandle {
|
||||
})
|
||||
.clone()
|
||||
});
|
||||
|
||||
let focus = focus_handle(&self.id, window, cx);
|
||||
let dragging = drag.read(cx).dragging;
|
||||
let axis = self.axis;
|
||||
@@ -126,153 +130,167 @@ impl RenderOnce for ResizeHandle {
|
||||
|
||||
let on_reset = self.on_reset;
|
||||
|
||||
div()
|
||||
.id(self.id.clone())
|
||||
.debug_selector(|| "resize-handle".to_string())
|
||||
.role(Role::Splitter)
|
||||
.aria_label(label)
|
||||
.flex_none()
|
||||
.when(axis == Axis::Horizontal, |handle| {
|
||||
handle.w(thickness).h_full()
|
||||
})
|
||||
.when(axis == Axis::Vertical, |handle| {
|
||||
handle.w_full().h(thickness)
|
||||
})
|
||||
.when(self.disabled, |handle| handle.opacity(0.5))
|
||||
.when(!self.disabled, |handle| {
|
||||
handle
|
||||
.track_focus(&focus)
|
||||
.focusable()
|
||||
.tab_stop(true)
|
||||
.focus_visible(|style| style.shadow(focus_ring(ring)))
|
||||
.on_mouse_down(MouseButton::Left, {
|
||||
let drag = drag.clone();
|
||||
let focus = focus.clone();
|
||||
move |event: &MouseDownEvent, window, cx| {
|
||||
window.focus(&focus, cx);
|
||||
cx.stop_propagation();
|
||||
if event.click_count >= 2 {
|
||||
deferred(
|
||||
div()
|
||||
.id(self.id.clone())
|
||||
.debug_selector(|| "resize-handle".to_string())
|
||||
.role(Role::Splitter)
|
||||
.aria_label(label)
|
||||
.flex_none()
|
||||
.when(axis == Axis::Horizontal, |handle| {
|
||||
handle.w(thickness).h_full().mx(straddle)
|
||||
})
|
||||
.when(axis == Axis::Vertical, |handle| {
|
||||
handle.w_full().h(thickness).my(straddle)
|
||||
})
|
||||
.when(self.disabled, |handle| handle.opacity(0.5))
|
||||
.when(!self.disabled, |handle| {
|
||||
handle
|
||||
.track_focus(&focus)
|
||||
.focusable()
|
||||
.tab_stop(true)
|
||||
.focus_visible(|style| style.shadow(focus_ring(ring)))
|
||||
.on_mouse_down(MouseButton::Left, {
|
||||
let drag = drag.clone();
|
||||
let focus = focus.clone();
|
||||
|
||||
move |event: &MouseDownEvent, window, cx| {
|
||||
window.focus(&focus, cx);
|
||||
cx.stop_propagation();
|
||||
|
||||
if event.click_count >= 2 {
|
||||
drag.update(cx, |drag, cx| {
|
||||
drag.dragging = false;
|
||||
cx.notify();
|
||||
});
|
||||
if let Some(on_reset) = on_reset.clone() {
|
||||
on_reset(window, cx);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
drag.update(cx, |drag, cx| {
|
||||
drag.dragging = false;
|
||||
drag.dragging = true;
|
||||
drag.last = event.position;
|
||||
cx.notify();
|
||||
});
|
||||
if let Some(on_reset) = on_reset.clone() {
|
||||
on_reset(window, cx);
|
||||
}
|
||||
return;
|
||||
}
|
||||
drag.update(cx, |drag, cx| {
|
||||
drag.dragging = true;
|
||||
drag.last = event.position;
|
||||
cx.notify();
|
||||
});
|
||||
}
|
||||
})
|
||||
.on_key_down({
|
||||
let on_resize = self.on_resize.clone();
|
||||
move |event: &KeyDownEvent, window, cx| {
|
||||
let delta = match (axis, event.keystroke.key.as_str()) {
|
||||
(Axis::Horizontal, "left") => Some(px(-KEYBOARD_STEP)),
|
||||
(Axis::Horizontal, "right") => Some(px(KEYBOARD_STEP)),
|
||||
(Axis::Vertical, "up") => Some(px(-KEYBOARD_STEP)),
|
||||
(Axis::Vertical, "down") => Some(px(KEYBOARD_STEP)),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(delta) = delta {
|
||||
cx.stop_propagation();
|
||||
if let Some(on_resize) = on_resize.clone() {
|
||||
on_resize(delta, window, cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
.child(
|
||||
canvas(
|
||||
{
|
||||
move |bounds, window, _| {
|
||||
let hitbox = window.insert_hitbox(bounds, HitboxBehavior::Normal);
|
||||
(hitbox, bounds)
|
||||
}
|
||||
},
|
||||
{
|
||||
let drag = drag.clone();
|
||||
let on_resize = self.on_resize.clone();
|
||||
move |_, (hitbox, bounds), window, _cx| {
|
||||
let hovered = hitbox.is_hovered(window);
|
||||
if !disabled {
|
||||
window.set_cursor_style(cursor, &hitbox);
|
||||
}
|
||||
|
||||
// The hairline highlights while hovered or dragged.
|
||||
let line_color = if !disabled && (dragging || hovered) {
|
||||
ring
|
||||
} else {
|
||||
border
|
||||
};
|
||||
let line = match axis {
|
||||
Axis::Horizontal => {
|
||||
let x = bounds.center().x - px(0.5);
|
||||
Bounds::from_corners(
|
||||
point(x, bounds.top()),
|
||||
point(x + px(1.), bounds.bottom()),
|
||||
)
|
||||
}
|
||||
Axis::Vertical => {
|
||||
let y = bounds.center().y - px(0.5);
|
||||
Bounds::from_corners(
|
||||
point(bounds.left(), y),
|
||||
point(bounds.right(), y + px(1.)),
|
||||
)
|
||||
}
|
||||
};
|
||||
window.paint_quad(fill(line, line_color));
|
||||
|
||||
window.on_mouse_event({
|
||||
let drag = drag.clone();
|
||||
let on_resize = on_resize.clone();
|
||||
move |event: &MouseMoveEvent, phase, window, cx| {
|
||||
if phase != DispatchPhase::Bubble {
|
||||
return;
|
||||
}
|
||||
let (dragging, last) = {
|
||||
let drag = drag.read(cx);
|
||||
(drag.dragging, drag.last)
|
||||
};
|
||||
if !dragging {
|
||||
return;
|
||||
}
|
||||
let delta = match axis {
|
||||
Axis::Horizontal => event.position.x - last.x,
|
||||
Axis::Vertical => event.position.y - last.y,
|
||||
};
|
||||
drag.update(cx, |drag, _| drag.last = event.position);
|
||||
})
|
||||
.on_key_down({
|
||||
let on_resize = self.on_resize.clone();
|
||||
move |event: &KeyDownEvent, window, cx| {
|
||||
let delta = match (axis, event.keystroke.key.as_str()) {
|
||||
(Axis::Horizontal, "left") => Some(px(-KEYBOARD_STEP)),
|
||||
(Axis::Horizontal, "right") => Some(px(KEYBOARD_STEP)),
|
||||
(Axis::Vertical, "up") => Some(px(-KEYBOARD_STEP)),
|
||||
(Axis::Vertical, "down") => Some(px(KEYBOARD_STEP)),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(delta) = delta {
|
||||
cx.stop_propagation();
|
||||
if let Some(on_resize) = on_resize.clone() {
|
||||
on_resize(delta, window, cx);
|
||||
}
|
||||
}
|
||||
});
|
||||
window.on_mouse_event({
|
||||
let drag = drag.clone();
|
||||
move |event: &MouseUpEvent, phase, _, cx| {
|
||||
if phase != DispatchPhase::Bubble
|
||||
|| event.button != MouseButton::Left
|
||||
{
|
||||
return;
|
||||
}
|
||||
if drag.read(cx).dragging {
|
||||
drag.update(cx, |drag, cx| {
|
||||
drag.dragging = false;
|
||||
cx.notify();
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
.child(
|
||||
canvas(
|
||||
{
|
||||
move |bounds, window, _| {
|
||||
let hitbox = window.insert_hitbox(bounds, HitboxBehavior::Normal);
|
||||
(hitbox, bounds)
|
||||
}
|
||||
},
|
||||
{
|
||||
let drag = drag.clone();
|
||||
let on_resize = self.on_resize.clone();
|
||||
move |_, (hitbox, bounds), window, _cx| {
|
||||
let hovered = hitbox.is_hovered(window);
|
||||
if !disabled {
|
||||
window.set_cursor_style(cursor, &hitbox);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
)
|
||||
.absolute()
|
||||
.inset_0(),
|
||||
)
|
||||
|
||||
// The hairline highlights while hovered or dragged.
|
||||
let line_color = if !disabled && (dragging || hovered) {
|
||||
ring
|
||||
} else {
|
||||
border
|
||||
};
|
||||
|
||||
let line = match axis {
|
||||
Axis::Horizontal => {
|
||||
let x = bounds.center().x - px(0.5);
|
||||
Bounds::from_corners(
|
||||
point(x, bounds.top()),
|
||||
point(x + px(1.), bounds.bottom()),
|
||||
)
|
||||
}
|
||||
Axis::Vertical => {
|
||||
let y = bounds.center().y - px(0.5);
|
||||
Bounds::from_corners(
|
||||
point(bounds.left(), y),
|
||||
point(bounds.right(), y + px(1.)),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
window.paint_quad(fill(line, line_color));
|
||||
|
||||
window.on_mouse_event({
|
||||
let drag = drag.clone();
|
||||
let on_resize = on_resize.clone();
|
||||
move |event: &MouseMoveEvent, phase, window, cx| {
|
||||
if phase != DispatchPhase::Bubble {
|
||||
return;
|
||||
}
|
||||
|
||||
let (dragging, last) = {
|
||||
let drag = drag.read(cx);
|
||||
(drag.dragging, drag.last)
|
||||
};
|
||||
|
||||
if !dragging {
|
||||
return;
|
||||
}
|
||||
|
||||
let delta = match axis {
|
||||
Axis::Horizontal => event.position.x - last.x,
|
||||
Axis::Vertical => event.position.y - last.y,
|
||||
};
|
||||
|
||||
drag.update(cx, |drag, _| drag.last = event.position);
|
||||
|
||||
if let Some(on_resize) = on_resize.clone() {
|
||||
on_resize(delta, window, cx);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
window.on_mouse_event({
|
||||
let drag = drag.clone();
|
||||
move |event: &MouseUpEvent, phase, _, cx| {
|
||||
if phase != DispatchPhase::Bubble
|
||||
|| event.button != MouseButton::Left
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if drag.read(cx).dragging {
|
||||
drag.update(cx, |drag, cx| {
|
||||
drag.dragging = false;
|
||||
cx.notify();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
)
|
||||
.absolute()
|
||||
.inset_0(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user