Improve interaction between clicking and dragging (#3737)
Once a drag starts, we won't fire click listeners or style any elements as active. - Don't fire click listeners or show active state once a drag is in progress - Don't show hover style when a drag is in progress - Draw borders above content - If borders are opaque, apply them to the content mask. This prevents hovers from firing on content underneath the border, which was creating issues where the drag handle was inside the border, so we'd flicker the hover when the mouse moved out of the drag handle and into the 1px border on the left dock. - Add a `block_mouse` helper which causes transparent elements to paint an "opaque" layer to prevent mouse events from falling through. We use this for the drag handle as well to disable hover, click, etc on items in the panel. Release Notes: - N/A
This commit is contained in:
+565
-517
File diff suppressed because it is too large
Load Diff
@@ -209,54 +209,52 @@ impl Element for UniformList {
|
||||
bounds.lower_right() - point(border.right + padding.right, border.bottom),
|
||||
);
|
||||
|
||||
style.paint(bounds, cx, |cx| {
|
||||
if self.item_count > 0 {
|
||||
let content_height =
|
||||
item_height * self.item_count + padding.top + padding.bottom;
|
||||
let min_scroll_offset = padded_bounds.size.height - content_height;
|
||||
let is_scrolled = scroll_offset.y != px(0.);
|
||||
if self.item_count > 0 {
|
||||
let content_height =
|
||||
item_height * self.item_count + padding.top + padding.bottom;
|
||||
let min_scroll_offset = padded_bounds.size.height - content_height;
|
||||
let is_scrolled = scroll_offset.y != px(0.);
|
||||
|
||||
if is_scrolled && scroll_offset.y < min_scroll_offset {
|
||||
shared_scroll_offset.borrow_mut().y = min_scroll_offset;
|
||||
scroll_offset.y = min_scroll_offset;
|
||||
}
|
||||
if is_scrolled && scroll_offset.y < min_scroll_offset {
|
||||
shared_scroll_offset.borrow_mut().y = min_scroll_offset;
|
||||
scroll_offset.y = min_scroll_offset;
|
||||
}
|
||||
|
||||
if let Some(scroll_handle) = self.scroll_handle.clone() {
|
||||
scroll_handle.0.borrow_mut().replace(ScrollHandleState {
|
||||
item_height,
|
||||
list_height: padded_bounds.size.height,
|
||||
scroll_offset: shared_scroll_offset,
|
||||
});
|
||||
}
|
||||
|
||||
let first_visible_element_ix =
|
||||
(-(scroll_offset.y + padding.top) / item_height).floor() as usize;
|
||||
let last_visible_element_ix =
|
||||
((-scroll_offset.y + padded_bounds.size.height) / item_height).ceil()
|
||||
as usize;
|
||||
let visible_range = first_visible_element_ix
|
||||
..cmp::min(last_visible_element_ix, self.item_count);
|
||||
|
||||
let mut items = (self.render_items)(visible_range.clone(), cx);
|
||||
cx.with_z_index(1, |cx| {
|
||||
let content_mask = ContentMask { bounds };
|
||||
cx.with_content_mask(Some(content_mask), |cx| {
|
||||
for (item, ix) in items.iter_mut().zip(visible_range) {
|
||||
let item_origin = padded_bounds.origin
|
||||
+ point(
|
||||
px(0.),
|
||||
item_height * ix + scroll_offset.y + padding.top,
|
||||
);
|
||||
let available_space = size(
|
||||
AvailableSpace::Definite(padded_bounds.size.width),
|
||||
AvailableSpace::Definite(item_height),
|
||||
);
|
||||
item.draw(item_origin, available_space, cx);
|
||||
}
|
||||
});
|
||||
if let Some(scroll_handle) = self.scroll_handle.clone() {
|
||||
scroll_handle.0.borrow_mut().replace(ScrollHandleState {
|
||||
item_height,
|
||||
list_height: padded_bounds.size.height,
|
||||
scroll_offset: shared_scroll_offset,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
let first_visible_element_ix =
|
||||
(-(scroll_offset.y + padding.top) / item_height).floor() as usize;
|
||||
let last_visible_element_ix = ((-scroll_offset.y + padded_bounds.size.height)
|
||||
/ item_height)
|
||||
.ceil() as usize;
|
||||
let visible_range = first_visible_element_ix
|
||||
..cmp::min(last_visible_element_ix, self.item_count);
|
||||
|
||||
let mut items = (self.render_items)(visible_range.clone(), cx);
|
||||
cx.with_z_index(1, |cx| {
|
||||
let content_mask = ContentMask { bounds };
|
||||
cx.with_content_mask(Some(content_mask), |cx| {
|
||||
for (item, ix) in items.iter_mut().zip(visible_range) {
|
||||
let item_origin = padded_bounds.origin
|
||||
+ point(
|
||||
px(0.),
|
||||
item_height * ix + scroll_offset.y + padding.top,
|
||||
);
|
||||
let available_space = size(
|
||||
AvailableSpace::Definite(padded_bounds.size.width),
|
||||
AvailableSpace::Definite(item_height),
|
||||
);
|
||||
item.draw(item_origin, available_space, cx);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
+79
-52
@@ -258,16 +258,30 @@ impl Style {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn overflow_mask(&self, bounds: Bounds<Pixels>) -> Option<ContentMask<Pixels>> {
|
||||
pub fn overflow_mask(
|
||||
&self,
|
||||
bounds: Bounds<Pixels>,
|
||||
rem_size: Pixels,
|
||||
) -> Option<ContentMask<Pixels>> {
|
||||
match self.overflow {
|
||||
Point {
|
||||
x: Overflow::Visible,
|
||||
y: Overflow::Visible,
|
||||
} => None,
|
||||
_ => {
|
||||
let current_mask = bounds;
|
||||
let min = current_mask.origin;
|
||||
let max = current_mask.lower_right();
|
||||
let mut min = bounds.origin;
|
||||
let mut max = bounds.lower_right();
|
||||
|
||||
if self
|
||||
.border_color
|
||||
.map_or(false, |color| !color.is_transparent())
|
||||
{
|
||||
min.x += self.border_widths.left.to_pixels(rem_size);
|
||||
max.x -= self.border_widths.right.to_pixels(rem_size);
|
||||
min.y += self.border_widths.top.to_pixels(rem_size);
|
||||
max.y -= self.border_widths.bottom.to_pixels(rem_size);
|
||||
}
|
||||
|
||||
let bounds = match (
|
||||
self.overflow.x == Overflow::Visible,
|
||||
self.overflow.y == Overflow::Visible,
|
||||
@@ -285,61 +299,62 @@ impl Style {
|
||||
point(bounds.lower_right().x, max.y),
|
||||
),
|
||||
// both hidden
|
||||
(false, false) => bounds,
|
||||
(false, false) => Bounds::from_corners(min, max),
|
||||
};
|
||||
|
||||
Some(ContentMask { bounds })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_text_style<C, F, R>(&self, cx: &mut C, f: F) -> R
|
||||
where
|
||||
C: BorrowAppContext,
|
||||
F: FnOnce(&mut C) -> R,
|
||||
{
|
||||
if self.text.is_some() {
|
||||
cx.with_text_style(Some(self.text.clone()), f)
|
||||
} else {
|
||||
f(cx)
|
||||
}
|
||||
}
|
||||
// pub fn apply_text_style<C, F, R>(&self, cx: &mut C, f: F) -> R
|
||||
// where
|
||||
// C: BorrowAppContext,
|
||||
// F: FnOnce(&mut C) -> R,
|
||||
// {
|
||||
// if self.text.is_some() {
|
||||
// cx.with_text_style(Some(self.text.clone()), f)
|
||||
// } else {
|
||||
// f(cx)
|
||||
// }
|
||||
// }
|
||||
|
||||
/// Apply overflow to content mask
|
||||
pub fn apply_overflow<C, F, R>(&self, bounds: Bounds<Pixels>, cx: &mut C, f: F) -> R
|
||||
where
|
||||
C: BorrowWindow,
|
||||
F: FnOnce(&mut C) -> R,
|
||||
{
|
||||
let current_mask = cx.content_mask();
|
||||
// /// Apply overflow to content mask
|
||||
// pub fn apply_overflow<C, F, R>(&self, bounds: Bounds<Pixels>, cx: &mut C, f: F) -> R
|
||||
// where
|
||||
// C: BorrowWindow,
|
||||
// F: FnOnce(&mut C) -> R,
|
||||
// {
|
||||
// let current_mask = cx.content_mask();
|
||||
|
||||
let min = current_mask.bounds.origin;
|
||||
let max = current_mask.bounds.lower_right();
|
||||
// let min = current_mask.bounds.origin;
|
||||
// let max = current_mask.bounds.lower_right();
|
||||
|
||||
let mask_bounds = match (
|
||||
self.overflow.x == Overflow::Visible,
|
||||
self.overflow.y == Overflow::Visible,
|
||||
) {
|
||||
// x and y both visible
|
||||
(true, true) => return f(cx),
|
||||
// x visible, y hidden
|
||||
(true, false) => Bounds::from_corners(
|
||||
point(min.x, bounds.origin.y),
|
||||
point(max.x, bounds.lower_right().y),
|
||||
),
|
||||
// x hidden, y visible
|
||||
(false, true) => Bounds::from_corners(
|
||||
point(bounds.origin.x, min.y),
|
||||
point(bounds.lower_right().x, max.y),
|
||||
),
|
||||
// both hidden
|
||||
(false, false) => bounds,
|
||||
};
|
||||
let mask = ContentMask {
|
||||
bounds: mask_bounds,
|
||||
};
|
||||
// let mask_bounds = match (
|
||||
// self.overflow.x == Overflow::Visible,
|
||||
// self.overflow.y == Overflow::Visible,
|
||||
// ) {
|
||||
// // x and y both visible
|
||||
// (true, true) => return f(cx),
|
||||
// // x visible, y hidden
|
||||
// (true, false) => Bounds::from_corners(
|
||||
// point(min.x, bounds.origin.y),
|
||||
// point(max.x, bounds.lower_right().y),
|
||||
// ),
|
||||
// // x hidden, y visible
|
||||
// (false, true) => Bounds::from_corners(
|
||||
// point(bounds.origin.x, min.y),
|
||||
// point(bounds.lower_right().x, max.y),
|
||||
// ),
|
||||
// // both hidden
|
||||
// (false, false) => bounds,
|
||||
// };
|
||||
// let mask = ContentMask {
|
||||
// bounds: mask_bounds,
|
||||
// };
|
||||
|
||||
cx.with_content_mask(Some(mask), f)
|
||||
}
|
||||
// cx.with_content_mask(Some(mask), f)
|
||||
// }
|
||||
|
||||
/// Paints the background of an element styled with this style.
|
||||
pub fn paint(
|
||||
@@ -369,14 +384,14 @@ impl Style {
|
||||
});
|
||||
|
||||
let background_color = self.background.as_ref().and_then(Fill::color);
|
||||
if background_color.is_some() || self.is_border_visible() {
|
||||
if background_color.is_some() {
|
||||
cx.with_z_index(1, |cx| {
|
||||
cx.paint_quad(quad(
|
||||
bounds,
|
||||
self.corner_radii.to_pixels(bounds.size, rem_size),
|
||||
background_color.unwrap_or_default(),
|
||||
self.border_widths.to_pixels(rem_size),
|
||||
self.border_color.unwrap_or_default(),
|
||||
Edges::default(),
|
||||
Hsla::transparent_black(),
|
||||
));
|
||||
});
|
||||
}
|
||||
@@ -385,6 +400,18 @@ impl Style {
|
||||
continuation(cx);
|
||||
});
|
||||
|
||||
if self.is_border_visible() {
|
||||
cx.with_z_index(3, |cx| {
|
||||
cx.paint_quad(quad(
|
||||
bounds,
|
||||
self.corner_radii.to_pixels(bounds.size, rem_size),
|
||||
Hsla::transparent_black(),
|
||||
self.border_widths.to_pixels(rem_size),
|
||||
self.border_color.unwrap_or_default(),
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
if self.debug_below {
|
||||
cx.remove_global::<DebugBelow>();
|
||||
|
||||
@@ -499,7 +499,8 @@ impl Render for Dock {
|
||||
cx.stop_propagation();
|
||||
}
|
||||
}))
|
||||
.z_index(1);
|
||||
.z_index(1)
|
||||
.block_mouse();
|
||||
|
||||
const HANDLE_SIZE: Pixels = Pixels(6.);
|
||||
|
||||
@@ -536,6 +537,7 @@ impl Render for Dock {
|
||||
div()
|
||||
.flex()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.overflow_hidden()
|
||||
.map(|this| match self.position().axis() {
|
||||
Axis::Horizontal => this.w(px(size)).h_full().flex_row(),
|
||||
Axis::Vertical => this.h(px(size)).w_full().flex_col(),
|
||||
|
||||
Reference in New Issue
Block a user