Add support for resizing splits and docks, as well as utilities (#3595)

Making this PR to upstream changes to util and GPUI2, resizing exists
but isn't working yet.

Release Notes:

- N/A
This commit is contained in:
Mikayla Maki
2023-12-11 13:32:06 -08:00
committed by GitHub
20 changed files with 976 additions and 512 deletions
+4
View File
@@ -513,6 +513,10 @@ impl AppContext {
self.platform.path_for_auxiliary_executable(name)
}
pub fn double_click_interval(&self) -> Duration {
self.platform.double_click_interval()
}
pub fn prompt_for_paths(
&self,
options: PathPromptOptions,
+11 -11
View File
@@ -761,7 +761,7 @@ pub struct InteractiveBounds {
impl InteractiveBounds {
pub fn visibly_contains(&self, point: &Point<Pixels>, cx: &WindowContext) -> bool {
self.bounds.contains_point(point) && cx.was_top_layer(&point, &self.stacking_order)
self.bounds.contains(point) && cx.was_top_layer(&point, &self.stacking_order)
}
}
@@ -860,10 +860,10 @@ impl Interactivity {
.and_then(|group_hover| GroupBounds::get(&group_hover.group, cx));
if let Some(group_bounds) = hover_group_bounds {
let hovered = group_bounds.contains_point(&cx.mouse_position());
let hovered = group_bounds.contains(&cx.mouse_position());
cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
if phase == DispatchPhase::Capture {
if group_bounds.contains_point(&event.position) != hovered {
if group_bounds.contains(&event.position) != hovered {
cx.notify();
}
}
@@ -875,10 +875,10 @@ impl Interactivity {
|| cx.active_drag.is_some() && !self.drag_over_styles.is_empty()
{
let bounds = bounds.intersect(&cx.content_mask().bounds);
let hovered = bounds.contains_point(&cx.mouse_position());
let hovered = bounds.contains(&cx.mouse_position());
cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
if phase == DispatchPhase::Capture {
if bounds.contains_point(&event.position) != hovered {
if bounds.contains(&event.position) != hovered {
cx.notify();
}
}
@@ -1068,8 +1068,8 @@ impl Interactivity {
let interactive_bounds = interactive_bounds.clone();
cx.on_mouse_event(move |down: &MouseDownEvent, phase, cx| {
if phase == DispatchPhase::Bubble {
let group = active_group_bounds
.map_or(false, |bounds| bounds.contains_point(&down.position));
let group =
active_group_bounds.map_or(false, |bounds| bounds.contains(&down.position));
let element = interactive_bounds.visibly_contains(&down.position, cx);
if group || element {
*active_state.borrow_mut() = ElementClickedState { group, element };
@@ -1183,7 +1183,7 @@ impl Interactivity {
let mouse_position = cx.mouse_position();
if let Some(group_hover) = self.group_hover_style.as_ref() {
if let Some(group_bounds) = GroupBounds::get(&group_hover.group, cx) {
if group_bounds.contains_point(&mouse_position)
if group_bounds.contains(&mouse_position)
&& cx.was_top_layer(&mouse_position, cx.stacking_order())
{
style.refine(&group_hover.style);
@@ -1193,7 +1193,7 @@ impl Interactivity {
if self.hover_style.is_some() {
if bounds
.intersect(&cx.content_mask().bounds)
.contains_point(&mouse_position)
.contains(&mouse_position)
&& cx.was_top_layer(&mouse_position, cx.stacking_order())
{
style.refine(&self.hover_style);
@@ -1204,7 +1204,7 @@ impl Interactivity {
for (state_type, group_drag_style) in &self.group_drag_over_styles {
if let Some(group_bounds) = GroupBounds::get(&group_drag_style.group, cx) {
if *state_type == drag.view.entity_type()
&& group_bounds.contains_point(&mouse_position)
&& group_bounds.contains(&mouse_position)
{
style.refine(&group_drag_style.style);
}
@@ -1215,7 +1215,7 @@ impl Interactivity {
if *state_type == drag.view.entity_type()
&& bounds
.intersect(&cx.content_mask().bounds)
.contains_point(&mouse_position)
.contains(&mouse_position)
{
style.refine(drag_over_style);
}
+1 -1
View File
@@ -253,7 +253,7 @@ impl TextState {
}
fn index_for_position(&self, bounds: Bounds<Pixels>, position: Point<Pixels>) -> Option<usize> {
if !bounds.contains_point(&position) {
if !bounds.contains(&position) {
return None;
}
+5 -1
View File
@@ -57,8 +57,12 @@ where
T: 'static,
E: 'static + Debug,
{
#[track_caller]
pub fn detach_and_log_err(self, cx: &mut AppContext) {
cx.foreground_executor().spawn(self.log_err()).detach();
let location = core::panic::Location::caller();
cx.foreground_executor()
.spawn(self.log_tracked_err(*location))
.detach();
}
}
+109 -1
View File
@@ -8,6 +8,62 @@ use std::{
ops::{Add, Div, Mul, MulAssign, Sub},
};
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Axis {
Vertical,
Horizontal,
}
impl Axis {
pub fn invert(&self) -> Self {
match self {
Axis::Vertical => Axis::Horizontal,
Axis::Horizontal => Axis::Vertical,
}
}
}
pub trait Along {
type Unit;
fn along(&self, axis: Axis) -> Self::Unit;
fn apply_along(&self, axis: Axis, f: impl FnOnce(Self::Unit) -> Self::Unit) -> Self;
}
impl sqlez::bindable::StaticColumnCount for Axis {}
impl sqlez::bindable::Bind for Axis {
fn bind(
&self,
statement: &sqlez::statement::Statement,
start_index: i32,
) -> anyhow::Result<i32> {
match self {
Axis::Horizontal => "Horizontal",
Axis::Vertical => "Vertical",
}
.bind(statement, start_index)
}
}
impl sqlez::bindable::Column for Axis {
fn column(
statement: &mut sqlez::statement::Statement,
start_index: i32,
) -> anyhow::Result<(Self, i32)> {
String::column(statement, start_index).and_then(|(axis_text, next_index)| {
Ok((
match axis_text.as_str() {
"Horizontal" => Axis::Horizontal,
"Vertical" => Axis::Vertical,
_ => anyhow::bail!("Stored serialized item kind is incorrect"),
},
next_index,
))
})
}
}
/// Describes a location in a 2D cartesian coordinate space.
///
/// It holds two public fields, `x` and `y`, which represent the coordinates in the space.
@@ -96,6 +152,30 @@ impl<T: Clone + Debug + Default> Point<T> {
}
}
impl<T: Clone + Debug + Default> Along for Point<T> {
type Unit = T;
fn along(&self, axis: Axis) -> T {
match axis {
Axis::Horizontal => self.x.clone(),
Axis::Vertical => self.y.clone(),
}
}
fn apply_along(&self, axis: Axis, f: impl FnOnce(T) -> T) -> Point<T> {
match axis {
Axis::Horizontal => Point {
x: f(self.x.clone()),
y: self.y.clone(),
},
Axis::Vertical => Point {
x: self.x.clone(),
y: f(self.y.clone()),
},
}
}
}
impl Point<Pixels> {
/// Scales the point by a given factor, which is typically derived from the resolution
/// of a target display to ensure proper sizing of UI elements.
@@ -373,6 +453,34 @@ impl Size<Pixels> {
}
}
impl<T> Along for Size<T>
where
T: Clone + Default + Debug,
{
type Unit = T;
fn along(&self, axis: Axis) -> T {
match axis {
Axis::Horizontal => self.width.clone(),
Axis::Vertical => self.height.clone(),
}
}
/// Returns the value of this size along the given axis.
fn apply_along(&self, axis: Axis, f: impl FnOnce(T) -> T) -> Self {
match axis {
Axis::Horizontal => Size {
width: f(self.width.clone()),
height: self.height.clone(),
},
Axis::Vertical => Size {
width: self.width.clone(),
height: f(self.height.clone()),
},
}
}
}
impl<T> Size<T>
where
T: PartialOrd + Clone + Default + Debug,
@@ -992,7 +1100,7 @@ where
/// assert!(bounds.contains_point(&inside_point));
/// assert!(!bounds.contains_point(&outside_point));
/// ```
pub fn contains_point(&self, point: &Point<T>) -> bool {
pub fn contains(&self, point: &Point<T>) -> bool {
point.x >= self.origin.x
&& point.x <= self.origin.x.clone() + self.size.width.clone()
&& point.y >= self.origin.y
+6
View File
@@ -131,6 +131,12 @@ pub struct MouseMoveEvent {
pub modifiers: Modifiers,
}
impl MouseMoveEvent {
pub fn dragging(&self) -> bool {
self.pressed_button == Some(MouseButton::Left)
}
}
#[derive(Clone, Debug)]
pub struct ScrollWheelEvent {
pub position: Point<Pixels>,
+1
View File
@@ -105,6 +105,7 @@ pub(crate) trait Platform: 'static {
fn app_version(&self) -> Result<SemanticVersion>;
fn app_path(&self) -> Result<PathBuf>;
fn local_timezone(&self) -> UtcOffset;
fn double_click_interval(&self) -> Duration;
fn path_for_auxiliary_executable(&self, name: &str) -> Result<PathBuf>;
fn set_cursor_style(&self, style: CursorStyle);
@@ -49,6 +49,7 @@ use std::{
rc::Rc,
slice, str,
sync::Arc,
time::Duration,
};
use time::UtcOffset;
@@ -657,6 +658,13 @@ impl Platform for MacPlatform {
"macOS"
}
fn double_click_interval(&self) -> Duration {
unsafe {
let double_click_interval: f64 = msg_send![class!(NSEvent), doubleClickInterval];
Duration::from_secs_f64(double_click_interval)
}
}
fn os_version(&self) -> Result<SemanticVersion> {
unsafe {
let process_info = NSProcessInfo::processInfo(nil);
@@ -12,6 +12,7 @@ use std::{
path::PathBuf,
rc::{Rc, Weak},
sync::Arc,
time::Duration,
};
pub struct TestPlatform {
@@ -274,4 +275,8 @@ impl Platform for TestPlatform {
fn delete_credentials(&self, _url: &str) -> Result<()> {
Ok(())
}
fn double_click_interval(&self) -> std::time::Duration {
Duration::from_millis(500)
}
}
+9
View File
@@ -477,3 +477,12 @@ impl From<Pixels> for AvailableSpace {
AvailableSpace::Definite(pixels)
}
}
impl From<Size<Pixels>> for Size<AvailableSpace> {
fn from(size: Size<Pixels>) -> Self {
Size {
width: AvailableSpace::Definite(size.width),
height: AvailableSpace::Definite(size.height),
}
}
}
+11 -1
View File
@@ -60,6 +60,16 @@ pub enum DispatchPhase {
Capture,
}
impl DispatchPhase {
pub fn bubble(self) -> bool {
self == DispatchPhase::Bubble
}
pub fn capture(self) -> bool {
self == DispatchPhase::Capture
}
}
type AnyObserver = Box<dyn FnMut(&mut WindowContext) -> bool + 'static>;
type AnyMouseListener = Box<dyn FnMut(&dyn Any, DispatchPhase, &mut WindowContext) + 'static>;
type AnyFocusListener = Box<dyn Fn(&FocusEvent, &mut WindowContext) + 'static>;
@@ -866,7 +876,7 @@ impl<'a> WindowContext<'a> {
/// same layer as the given stacking order.
pub fn was_top_layer(&self, point: &Point<Pixels>, level: &StackingOrder) -> bool {
for (stack, bounds) in self.window.rendered_frame.depth_map.iter() {
if bounds.contains_point(point) {
if bounds.contains(point) {
return level.starts_with(stack) || stack.starts_with(level);
}
}