Detailed changes
@@ -175,12 +175,12 @@ use editor::Editor;
use feature_flags::{ChannelsAlpha, FeatureFlagAppExt, FeatureFlagViewExt};
use fuzzy::{match_strings, StringMatchCandidate};
use gpui::{
- actions, canvas, div, img, impl_actions, overlay, point, prelude::*, px, rems, serde_json,
- size, Action, AnyElement, AppContext, AsyncWindowContext, Bounds, ClipboardItem, DismissEvent,
- Div, EventEmitter, FocusHandle, Focusable, FocusableView, Hsla, InteractiveElement,
- IntoElement, Length, Model, MouseDownEvent, ParentElement, Pixels, Point, PromptLevel, Quad,
- Render, RenderOnce, ScrollHandle, SharedString, Size, Stateful, Styled, Subscription, Task,
- View, ViewContext, VisualContext, WeakView,
+ actions, canvas, div, fill, img, impl_actions, overlay, point, prelude::*, px, rems,
+ serde_json, size, Action, AnyElement, AppContext, AsyncWindowContext, Bounds, ClipboardItem,
+ DismissEvent, Div, EventEmitter, FocusHandle, Focusable, FocusableView, Hsla,
+ InteractiveElement, IntoElement, Length, Model, MouseDownEvent, ParentElement, Pixels, Point,
+ PromptLevel, Quad, Render, RenderOnce, ScrollHandle, SharedString, Size, Stateful, Styled,
+ Subscription, Task, View, ViewContext, VisualContext, WeakView,
};
use project::{Fs, Project};
use serde_derive::{Deserialize, Serialize};
@@ -2994,7 +2994,7 @@ fn render_tree_branch(is_last: bool, cx: &mut WindowContext) -> impl IntoElement
let right = bounds.right();
let top = bounds.top();
- cx.paint_quad(
+ cx.paint_quad(fill(
Bounds::from_corners(
point(start_x, top),
point(
@@ -3002,18 +3002,12 @@ fn render_tree_branch(is_last: bool, cx: &mut WindowContext) -> impl IntoElement
if is_last { start_y } else { bounds.bottom() },
),
),
- Default::default(),
color,
- Default::default(),
- Hsla::transparent_black(),
- );
- cx.paint_quad(
+ ));
+ cx.paint_quad(fill(
Bounds::from_corners(point(start_x, start_y), point(right, start_y + thickness)),
- Default::default(),
color,
- Default::default(),
- Hsla::transparent_black(),
- );
+ ));
})
.w(width)
.h(line_height)
@@ -23,13 +23,14 @@ use anyhow::Result;
use collections::{BTreeMap, HashMap};
use git::diff::DiffHunkStatus;
use gpui::{
- div, overlay, point, px, relative, size, transparent_black, Action, AnchorCorner, AnyElement,
- AsyncWindowContext, AvailableSpace, BorrowWindow, Bounds, ContentMask, Corners, CursorStyle,
- DispatchPhase, Edges, Element, ElementId, ElementInputHandler, Entity, EntityId, Hsla,
- InteractiveBounds, InteractiveElement, IntoElement, LineLayout, ModifiersChangedEvent,
- MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, RenderOnce,
- ScrollWheelEvent, ShapedLine, SharedString, Size, StackingOrder, StatefulInteractiveElement,
- Style, Styled, TextRun, TextStyle, View, ViewContext, WeakView, WindowContext, WrappedLine,
+ div, fill, outline, overlay, point, px, quad, relative, size, transparent_black, Action,
+ AnchorCorner, AnyElement, AsyncWindowContext, AvailableSpace, BorrowWindow, Bounds,
+ ContentMask, Corners, CursorStyle, DispatchPhase, Edges, Element, ElementId,
+ ElementInputHandler, Entity, EntityId, Hsla, InteractiveBounds, InteractiveElement,
+ IntoElement, LineLayout, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent,
+ MouseUpEvent, ParentElement, Pixels, RenderOnce, ScrollWheelEvent, ShapedLine, SharedString,
+ Size, StackingOrder, StatefulInteractiveElement, Style, Styled, TextRun, TextStyle, View,
+ ViewContext, WeakView, WindowContext, WrappedLine,
};
use itertools::Itertools;
use language::{language_settings::ShowWhitespaceSetting, Language};
@@ -620,20 +621,8 @@ impl EditorElement {
let scroll_top =
layout.position_map.snapshot.scroll_position().y * layout.position_map.line_height;
let gutter_bg = cx.theme().colors().editor_gutter_background;
- cx.paint_quad(
- gutter_bounds,
- Corners::default(),
- gutter_bg,
- Edges::default(),
- transparent_black(),
- );
- cx.paint_quad(
- text_bounds,
- Corners::default(),
- self.style.background,
- Edges::default(),
- transparent_black(),
- );
+ cx.paint_quad(fill(gutter_bounds, gutter_bg));
+ cx.paint_quad(fill(text_bounds, self.style.background));
if let EditorMode::Full = layout.mode {
let mut active_rows = layout.active_rows.iter().peekable();
@@ -657,13 +646,7 @@ impl EditorElement {
layout.position_map.line_height * (end_row - start_row + 1) as f32,
);
let active_line_bg = cx.theme().colors().editor_active_line_background;
- cx.paint_quad(
- Bounds { origin, size },
- Corners::default(),
- active_line_bg,
- Edges::default(),
- transparent_black(),
- );
+ cx.paint_quad(fill(Bounds { origin, size }, active_line_bg));
}
}
@@ -679,13 +662,7 @@ impl EditorElement {
layout.position_map.line_height * highlighted_rows.len() as f32,
);
let highlighted_line_bg = cx.theme().colors().editor_highlighted_line_background;
- cx.paint_quad(
- Bounds { origin, size },
- Corners::default(),
- highlighted_line_bg,
- Edges::default(),
- transparent_black(),
- );
+ cx.paint_quad(fill(Bounds { origin, size }, highlighted_line_bg));
}
let scroll_left =
@@ -706,16 +683,13 @@ impl EditorElement {
} else {
cx.theme().colors().editor_wrap_guide
};
- cx.paint_quad(
+ cx.paint_quad(fill(
Bounds {
origin: point(x, text_bounds.origin.y),
size: size(px(1.), text_bounds.size.height),
},
- Corners::default(),
color,
- Edges::default(),
- transparent_black(),
- );
+ ));
}
}
}
@@ -812,13 +786,13 @@ impl EditorElement {
let highlight_origin = bounds.origin + point(-width, start_y);
let highlight_size = size(width * 2., end_y - start_y);
let highlight_bounds = Bounds::new(highlight_origin, highlight_size);
- cx.paint_quad(
+ cx.paint_quad(quad(
highlight_bounds,
Corners::all(1. * line_height),
gpui::yellow(), // todo!("use the right color")
Edges::default(),
transparent_black(),
- );
+ ));
continue;
}
@@ -845,13 +819,13 @@ impl EditorElement {
let highlight_origin = bounds.origin + point(-width, start_y);
let highlight_size = size(width * 2., end_y - start_y);
let highlight_bounds = Bounds::new(highlight_origin, highlight_size);
- cx.paint_quad(
+ cx.paint_quad(quad(
highlight_bounds,
Corners::all(1. * line_height),
cx.theme().status().deleted,
Edges::default(),
transparent_black(),
- );
+ ));
continue;
}
@@ -867,13 +841,13 @@ impl EditorElement {
let highlight_origin = bounds.origin + point(-width, start_y);
let highlight_size = size(width * 2., end_y - start_y);
let highlight_bounds = Bounds::new(highlight_origin, highlight_size);
- cx.paint_quad(
+ cx.paint_quad(quad(
highlight_bounds,
Corners::all(0.05 * line_height),
color, // todo!("use the right color")
Edges::default(),
transparent_black(),
- );
+ ));
}
}
@@ -1278,7 +1252,7 @@ impl EditorElement {
let thumb_bounds = Bounds::from_corners(point(left, thumb_top), point(right, thumb_bottom));
if layout.show_scrollbars {
- cx.paint_quad(
+ cx.paint_quad(quad(
track_bounds,
Corners::default(),
cx.theme().colors().scrollbar_track_background,
@@ -1289,7 +1263,7 @@ impl EditorElement {
left: px(1.),
},
cx.theme().colors().scrollbar_track_border,
- );
+ ));
let scrollbar_settings = EditorSettings::get_global(cx).scrollbar;
if layout.is_singleton && scrollbar_settings.selections {
let start_anchor = Anchor::min();
@@ -1309,7 +1283,7 @@ impl EditorElement {
end_y = start_y + px(1.);
}
let bounds = Bounds::from_corners(point(left, start_y), point(right, end_y));
- cx.paint_quad(
+ cx.paint_quad(quad(
bounds,
Corners::default(),
cx.theme().status().info,
@@ -1320,7 +1294,7 @@ impl EditorElement {
left: px(1.),
},
cx.theme().colors().scrollbar_thumb_border,
- );
+ ));
}
}
@@ -1352,7 +1326,7 @@ impl EditorElement {
DiffHunkStatus::Modified => cx.theme().status().modified,
DiffHunkStatus::Removed => cx.theme().status().deleted,
};
- cx.paint_quad(
+ cx.paint_quad(quad(
bounds,
Corners::default(),
color,
@@ -1363,11 +1337,11 @@ impl EditorElement {
left: px(1.),
},
cx.theme().colors().scrollbar_thumb_border,
- );
+ ));
}
}
- cx.paint_quad(
+ cx.paint_quad(quad(
thumb_bounds,
Corners::default(),
cx.theme().colors().scrollbar_thumb_background,
@@ -1378,7 +1352,7 @@ impl EditorElement {
left: px(1.),
},
cx.theme().colors().scrollbar_thumb_border,
- );
+ ));
}
let mouse_position = cx.mouse_position();
@@ -3085,23 +3059,13 @@ impl Cursor {
};
//Draw background or border quad
- if matches!(self.shape, CursorShape::Hollow) {
- cx.paint_quad(
- bounds,
- Corners::default(),
- transparent_black(),
- Edges::all(px(1.)),
- self.color,
- );
+ let cursor = if matches!(self.shape, CursorShape::Hollow) {
+ outline(bounds, self.color)
} else {
- cx.paint_quad(
- bounds,
- Corners::default(),
- self.color,
- Edges::default(),
- transparent_black(),
- );
- }
+ fill(bounds, self.color)
+ };
+
+ cx.paint_quad(cursor);
if let Some(block_text) = &self.block_text {
block_text.paint(self.origin + origin, self.line_height, cx);
@@ -1138,6 +1138,10 @@ impl AppContext {
pub fn has_active_drag(&self) -> bool {
self.active_drag.is_some()
}
+
+ pub fn active_drag(&self) -> Option<AnyView> {
+ self.active_drag.as_ref().map(|drag| drag.view.clone())
+ }
}
impl Context for AppContext {
@@ -482,48 +482,31 @@ impl IntoElement for AnyElement {
}
}
-// impl<V, E, F> Element for Option<F>
-// where
-// V: 'static,
-// E: Element,
-// F: FnOnce(&mut V, &mut WindowContext<'_, V>) -> E + 'static,
-// {
-// type State = Option<AnyElement>;
-
-// fn element_id(&self) -> Option<ElementId> {
-// None
-// }
-
-// fn layout(
-// &mut self,
-// _: Option<Self::State>,
-// cx: &mut WindowContext,
-// ) -> (LayoutId, Self::State) {
-// let render = self.take().unwrap();
-// let mut element = (render)(view_state, cx).into_any();
-// let layout_id = element.layout(view_state, cx);
-// (layout_id, Some(element))
-// }
-
-// fn paint(
-// self,
-// _bounds: Bounds<Pixels>,
-// rendered_element: &mut Self::State,
-// cx: &mut WindowContext,
-// ) {
-// rendered_element.take().unwrap().paint(view_state, cx);
-// }
-// }
-
-// impl<V, E, F> RenderOnce for Option<F>
-// where
-// V: 'static,
-// E: Element,
-// F: FnOnce(&mut V, &mut WindowContext) -> E + 'static,
-// {
-// type Element = Self;
-
-// fn render(self) -> Self::Element {
-// self
-// }
-// }
+/// The empty element, which renders nothing.
+pub type Empty = ();
+
+impl IntoElement for () {
+ type Element = Self;
+
+ fn element_id(&self) -> Option<ElementId> {
+ None
+ }
+
+ fn into_element(self) -> Self::Element {
+ self
+ }
+}
+
+impl Element for () {
+ type State = ();
+
+ fn layout(
+ &mut self,
+ _state: Option<Self::State>,
+ cx: &mut WindowContext,
+ ) -> (LayoutId, Self::State) {
+ (cx.request_layout(&crate::Style::default(), None), ())
+ }
+
+ fn paint(self, _bounds: Bounds<Pixels>, _state: &mut Self::State, _cx: &mut WindowContext) {}
+}
@@ -2,7 +2,7 @@ use refineable::Refineable as _;
use crate::{Bounds, Element, IntoElement, Pixels, Style, StyleRefinement, Styled, WindowContext};
-pub fn canvas(callback: impl 'static + FnOnce(Bounds<Pixels>, &mut WindowContext)) -> Canvas {
+pub fn canvas(callback: impl 'static + FnOnce(&Bounds<Pixels>, &mut WindowContext)) -> Canvas {
Canvas {
paint_callback: Box::new(callback),
style: StyleRefinement::default(),
@@ -10,7 +10,7 @@ pub fn canvas(callback: impl 'static + FnOnce(Bounds<Pixels>, &mut WindowContext
}
pub struct Canvas {
- paint_callback: Box<dyn FnOnce(Bounds<Pixels>, &mut WindowContext)>,
+ paint_callback: Box<dyn FnOnce(&Bounds<Pixels>, &mut WindowContext)>,
style: StyleRefinement,
}
@@ -41,7 +41,7 @@ impl Element for Canvas {
}
fn paint(self, bounds: Bounds<Pixels>, _: &mut (), cx: &mut WindowContext) {
- (self.paint_callback)(bounds, cx)
+ (self.paint_callback)(&bounds, cx)
}
}
@@ -6,6 +6,7 @@ use crate::{
SharedString, Size, StackingOrder, Style, StyleRefinement, Styled, Task, View, Visibility,
WindowContext,
};
+
use collections::HashMap;
use refineable::Refineable;
use smallvec::SmallVec;
@@ -29,6 +30,11 @@ pub struct GroupStyle {
pub style: Box<StyleRefinement>,
}
+pub struct DragMoveEvent<W: Render> {
+ pub event: MouseMoveEvent,
+ pub drag: View<W>,
+}
+
pub trait InteractiveElement: Sized {
fn interactivity(&mut self) -> &mut Interactivity;
@@ -192,6 +198,34 @@ pub trait InteractiveElement: Sized {
self
}
+ fn on_drag_move<W>(
+ mut self,
+ listener: impl Fn(&DragMoveEvent<W>, &mut WindowContext) + 'static,
+ ) -> Self
+ where
+ W: Render,
+ {
+ self.interactivity().mouse_move_listeners.push(Box::new(
+ move |event, bounds, phase, cx| {
+ if phase == DispatchPhase::Capture
+ && bounds.drag_target_contains(&event.position, cx)
+ {
+ if let Some(view) = cx.active_drag().and_then(|view| view.downcast::<W>().ok())
+ {
+ (listener)(
+ &DragMoveEvent {
+ event: event.clone(),
+ drag: view,
+ },
+ cx,
+ );
+ }
+ }
+ },
+ ));
+ self
+ }
+
fn on_scroll_wheel(
mut self,
listener: impl Fn(&ScrollWheelEvent, &mut WindowContext) + 'static,
@@ -403,7 +437,7 @@ pub trait StatefulInteractiveElement: InteractiveElement {
self
}
- fn on_drag<W>(mut self, listener: impl Fn(&mut WindowContext) -> View<W> + 'static) -> Self
+ fn on_drag<W>(mut self, constructor: impl Fn(&mut WindowContext) -> View<W> + 'static) -> Self
where
Self: Sized,
W: 'static + Render,
@@ -413,7 +447,7 @@ pub trait StatefulInteractiveElement: InteractiveElement {
"calling on_drag more than once on the same element is not supported"
);
self.interactivity().drag_listener = Some(Box::new(move |cursor_offset, cx| AnyDrag {
- view: listener(cx).into(),
+ view: constructor(cx).into(),
cursor_offset,
}));
self
@@ -493,11 +527,19 @@ pub type DragEventListener = Box<dyn Fn(&MouseMoveEvent, &mut WindowContext) + '
pub type ActionListener = Box<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext) + 'static>;
+#[track_caller]
pub fn div() -> Div {
- Div {
+ let mut div = Div {
interactivity: Interactivity::default(),
children: SmallVec::default(),
+ };
+
+ #[cfg(debug_assertions)]
+ {
+ div.interactivity.location = Some(*core::panic::Location::caller());
}
+
+ div
}
pub struct Div {
@@ -607,10 +649,7 @@ impl Element for Div {
let z_index = style.z_index.unwrap_or(0);
cx.with_z_index(z_index, |cx| {
- cx.with_z_index(0, |cx| {
- style.paint(bounds, cx);
- });
- cx.with_z_index(1, |cx| {
+ style.paint(bounds, cx, |cx| {
cx.with_text_style(style.text_style().cloned(), |cx| {
cx.with_content_mask(style.overflow_mask(bounds), |cx| {
cx.with_element_offset(scroll_offset, |cx| {
@@ -620,7 +659,7 @@ impl Element for Div {
})
})
})
- })
+ });
})
},
);
@@ -678,6 +717,9 @@ pub struct Interactivity {
pub drag_listener: Option<DragListener>,
pub hover_listener: Option<Box<dyn Fn(&bool, &mut WindowContext)>>,
pub tooltip_builder: Option<TooltipBuilder>,
+
+ #[cfg(debug_assertions)]
+ pub location: Option<core::panic::Location<'static>>,
}
#[derive(Clone, Debug)]
@@ -737,6 +779,117 @@ impl Interactivity {
) {
let style = self.compute_style(Some(bounds), element_state, cx);
+ #[cfg(debug_assertions)]
+ if self.element_id.is_some()
+ && (style.debug || style.debug_below || cx.has_global::<crate::DebugBelow>())
+ && bounds.contains(&cx.mouse_position())
+ {
+ const FONT_SIZE: crate::Pixels = crate::Pixels(10.);
+ let element_id = format!("{:?}", self.element_id.unwrap());
+ let str_len = element_id.len();
+
+ let render_debug_text = |cx: &mut WindowContext| {
+ if let Some(text) = cx
+ .text_system()
+ .shape_text(
+ &element_id,
+ FONT_SIZE,
+ &[cx.text_style().to_run(str_len)],
+ None,
+ )
+ .ok()
+ .map(|mut text| text.pop())
+ .flatten()
+ {
+ text.paint(bounds.origin, FONT_SIZE, cx).ok();
+
+ let text_bounds = crate::Bounds {
+ origin: bounds.origin,
+ size: text.size(FONT_SIZE),
+ };
+ if self.location.is_some()
+ && text_bounds.contains(&cx.mouse_position())
+ && cx.modifiers().command
+ {
+ let command_held = cx.modifiers().command;
+ cx.on_key_event({
+ let text_bounds = text_bounds.clone();
+ move |e: &crate::ModifiersChangedEvent, _phase, cx| {
+ if e.modifiers.command != command_held
+ && text_bounds.contains(&cx.mouse_position())
+ {
+ cx.notify();
+ }
+ }
+ });
+
+ let hovered = bounds.contains(&cx.mouse_position());
+ cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
+ if phase == DispatchPhase::Capture {
+ if bounds.contains(&event.position) != hovered {
+ cx.notify();
+ }
+ }
+ });
+
+ cx.on_mouse_event({
+ let location = self.location.clone().unwrap();
+ let text_bounds = text_bounds.clone();
+ move |e: &crate::MouseDownEvent, phase, cx| {
+ if text_bounds.contains(&e.position) && phase.capture() {
+ cx.stop_propagation();
+ let Ok(dir) = std::env::current_dir() else {
+ return;
+ };
+
+ eprintln!(
+ "This element is created at:\n{}:{}:{}",
+ location.file(),
+ location.line(),
+ location.column()
+ );
+
+ std::process::Command::new("zed")
+ .arg(format!(
+ "{}/{}:{}:{}",
+ dir.to_string_lossy(),
+ location.file(),
+ location.line(),
+ location.column()
+ ))
+ .spawn()
+ .ok();
+ }
+ }
+ });
+ cx.paint_quad(crate::outline(
+ crate::Bounds {
+ origin: bounds.origin
+ + crate::point(crate::px(0.), FONT_SIZE - px(2.)),
+ size: crate::Size {
+ width: text_bounds.size.width,
+ height: crate::px(1.),
+ },
+ },
+ crate::red(),
+ ))
+ }
+ }
+ };
+
+ cx.with_z_index(1, |cx| {
+ cx.with_text_style(
+ Some(crate::TextStyleRefinement {
+ color: Some(crate::red()),
+ line_height: Some(FONT_SIZE.into()),
+ background_color: Some(crate::white()),
+ ..Default::default()
+ }),
+ render_debug_text,
+ )
+ });
+ }
+
if style
.background
.as_ref()
@@ -1234,6 +1387,9 @@ impl Default for Interactivity {
drag_listener: None,
hover_listener: None,
tooltip_builder: None,
+
+ #[cfg(debug_assertions)]
+ location: None,
}
}
}
@@ -10,6 +10,7 @@ use taffy::style::Overflow;
/// uniform_list provides lazy rendering for a set of items that are of uniform height.
/// When rendered into a container with overflow-y: hidden and a fixed (or max) height,
/// uniform_list will only render the visible subset of items.
+#[track_caller]
pub fn uniform_list<I, R, V>(
view: View<V>,
id: I,
@@ -42,6 +43,10 @@ where
interactivity: Interactivity {
element_id: Some(id.into()),
base_style: Box::new(base_style),
+
+ #[cfg(debug_assertions)]
+ location: Some(*core::panic::Location::caller()),
+
..Default::default()
},
scroll_handle: None,
@@ -197,41 +202,41 @@ impl Element for UniformList {
);
cx.with_z_index(style.z_index.unwrap_or(0), |cx| {
- style.paint(bounds, cx);
-
- if self.item_count > 0 {
- 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,
+ style.paint(bounds, cx, |cx| {
+ if self.item_count > 0 {
+ 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 / 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 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.into_iter().zip(visible_range) {
+ let item_origin = padded_bounds.origin
+ + point(px(0.), item_height * ix + scroll_offset.y);
+ let available_space = size(
+ AvailableSpace::Definite(padded_bounds.size.width),
+ AvailableSpace::Definite(item_height),
+ );
+ item.draw(item_origin, available_space, cx);
+ }
+ });
});
}
-
- let first_visible_element_ix =
- (-scroll_offset.y / 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 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.into_iter().zip(visible_range) {
- let item_origin = padded_bounds.origin
- + point(px(0.), item_height * ix + scroll_offset.y);
- let available_space = size(
- AvailableSpace::Definite(padded_bounds.size.width),
- AvailableSpace::Definite(item_height),
- );
- item.draw(item_origin, available_space, cx);
- }
- });
- });
- }
+ });
})
},
);
@@ -1592,6 +1592,17 @@ impl Edges<Pixels> {
}
}
+impl Into<Edges<Pixels>> for f32 {
+ fn into(self) -> Edges<Pixels> {
+ Edges {
+ top: self.into(),
+ right: self.into(),
+ bottom: self.into(),
+ left: self.into(),
+ }
+ }
+}
+
/// Represents the corners of a box in a 2D space, such as border radius.
///
/// Each field represents the size of the corner on one side of the box: `top_left`, `top_right`, `bottom_right`, and `bottom_left`.
@@ -1808,6 +1819,28 @@ where
impl<T> Copy for Corners<T> where T: Copy + Clone + Default + Debug {}
+impl Into<Corners<Pixels>> for f32 {
+ fn into(self) -> Corners<Pixels> {
+ Corners {
+ top_left: self.into(),
+ top_right: self.into(),
+ bottom_right: self.into(),
+ bottom_left: self.into(),
+ }
+ }
+}
+
+impl Into<Corners<Pixels>> for Pixels {
+ fn into(self) -> Corners<Pixels> {
+ Corners {
+ top_left: self,
+ top_right: self,
+ bottom_right: self,
+ bottom_left: self,
+ }
+ }
+}
+
/// Represents a length in pixels, the base unit of measurement in the UI framework.
///
/// `Pixels` is a value type that represents an absolute length in pixels, which is used
@@ -147,6 +147,7 @@ pub trait PlatformWindow {
fn appearance(&self) -> WindowAppearance;
fn display(&self) -> Rc<dyn PlatformDisplay>;
fn mouse_position(&self) -> Point<Pixels>;
+ fn modifiers(&self) -> Modifiers;
fn as_any_mut(&mut self) -> &mut dyn Any;
fn set_input_handler(&mut self, input_handler: Box<dyn PlatformInputHandler>);
fn clear_input_handler(&mut self);
@@ -9,9 +9,10 @@ use crate::{
use block::ConcreteBlock;
use cocoa::{
appkit::{
- CGPoint, NSApplication, NSBackingStoreBuffered, NSFilenamesPboardType, NSPasteboard,
- NSScreen, NSView, NSViewHeightSizable, NSViewWidthSizable, NSWindow, NSWindowButton,
- NSWindowCollectionBehavior, NSWindowStyleMask, NSWindowTitleVisibility,
+ CGPoint, NSApplication, NSBackingStoreBuffered, NSEventModifierFlags,
+ NSFilenamesPboardType, NSPasteboard, NSScreen, NSView, NSViewHeightSizable,
+ NSViewWidthSizable, NSWindow, NSWindowButton, NSWindowCollectionBehavior,
+ NSWindowStyleMask, NSWindowTitleVisibility,
},
base::{id, nil},
foundation::{
@@ -744,6 +745,26 @@ impl PlatformWindow for MacWindow {
convert_mouse_position(position, self.content_size().height)
}
+ fn modifiers(&self) -> Modifiers {
+ unsafe {
+ let modifiers: NSEventModifierFlags = msg_send![class!(NSEvent), modifierFlags];
+
+ let control = modifiers.contains(NSEventModifierFlags::NSControlKeyMask);
+ let alt = modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask);
+ let shift = modifiers.contains(NSEventModifierFlags::NSShiftKeyMask);
+ let command = modifiers.contains(NSEventModifierFlags::NSCommandKeyMask);
+ let function = modifiers.contains(NSEventModifierFlags::NSFunctionKeyMask);
+
+ Modifiers {
+ control,
+ alt,
+ shift,
+ command,
+ function,
+ }
+ }
+ }
+
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
@@ -79,6 +79,10 @@ impl PlatformWindow for TestWindow {
Point::default()
}
+ fn modifiers(&self) -> crate::Modifiers {
+ crate::Modifiers::default()
+ }
+
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
@@ -1,9 +1,9 @@
use std::{iter, mem, ops::Range};
use crate::{
- black, phi, point, rems, AbsoluteLength, BorrowAppContext, BorrowWindow, Bounds, ContentMask,
- Corners, CornersRefinement, CursorStyle, DefiniteLength, Edges, EdgesRefinement, Font,
- FontFeatures, FontStyle, FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Rgba,
+ black, phi, point, quad, rems, AbsoluteLength, BorrowAppContext, BorrowWindow, Bounds,
+ ContentMask, Corners, CornersRefinement, CursorStyle, DefiniteLength, Edges, EdgesRefinement,
+ Font, FontFeatures, FontStyle, FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Rgba,
SharedString, Size, SizeRefinement, Styled, TextRun, WindowContext,
};
use collections::HashSet;
@@ -14,6 +14,9 @@ pub use taffy::style::{
Overflow, Position,
};
+#[cfg(debug_assertions)]
+pub struct DebugBelow;
+
pub type StyleCascade = Cascade<Style>;
#[derive(Clone, Refineable, Debug)]
@@ -108,6 +111,11 @@ pub struct Style {
pub mouse_cursor: Option<CursorStyle>,
pub z_index: Option<u32>,
+
+ #[cfg(debug_assertions)]
+ pub debug: bool,
+ #[cfg(debug_assertions)]
+ pub debug_below: bool,
}
impl Styled for StyleRefinement {
@@ -334,7 +342,22 @@ impl Style {
}
/// Paints the background of an element styled with this style.
- pub fn paint(&self, bounds: Bounds<Pixels>, cx: &mut WindowContext) {
+ pub fn paint(
+ &self,
+ bounds: Bounds<Pixels>,
+ cx: &mut WindowContext,
+ continuation: impl FnOnce(&mut WindowContext),
+ ) {
+ #[cfg(debug_assertions)]
+ if self.debug_below {
+ cx.set_global(DebugBelow)
+ }
+
+ #[cfg(debug_assertions)]
+ if self.debug || cx.has_global::<DebugBelow>() {
+ cx.paint_quad(crate::outline(bounds, crate::red()));
+ }
+
let rem_size = cx.rem_size();
cx.with_z_index(0, |cx| {
@@ -348,15 +371,24 @@ impl Style {
let background_color = self.background.as_ref().and_then(Fill::color);
if background_color.is_some() || self.is_border_visible() {
cx.with_z_index(1, |cx| {
- cx.paint_quad(
+ 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(),
- );
+ ));
});
}
+
+ cx.with_z_index(2, |cx| {
+ continuation(cx);
+ });
+
+ #[cfg(debug_assertions)]
+ if self.debug_below {
+ cx.remove_global::<DebugBelow>();
+ }
}
fn is_border_visible(&self) -> bool {
@@ -404,6 +436,11 @@ impl Default for Style {
text: TextStyleRefinement::default(),
mouse_cursor: None,
z_index: None,
+
+ #[cfg(debug_assertions)]
+ debug: false,
+ #[cfg(debug_assertions)]
+ debug_below: false,
}
}
}
@@ -633,4 +633,16 @@ pub trait Styled: Sized {
.line_height = Some(line_height.into());
self
}
+
+ #[cfg(debug_assertions)]
+ fn debug(mut self) -> Self {
+ self.style().debug = Some(true);
+ self
+ }
+
+ #[cfg(debug_assertions)]
+ fn debug_below(mut self) -> Self {
+ self.style().debug_below = Some(true);
+ self
+ }
}
@@ -1,7 +1,6 @@
use crate::{
- black, point, px, size, transparent_black, BorrowWindow, Bounds, Corners, Edges, Hsla,
- LineLayout, Pixels, Point, Result, SharedString, UnderlineStyle, WindowContext, WrapBoundary,
- WrappedLineLayout,
+ black, fill, point, px, size, BorrowWindow, Bounds, Hsla, LineLayout, Pixels, Point, Result,
+ SharedString, UnderlineStyle, WindowContext, WrapBoundary, WrappedLineLayout,
};
use derive_more::{Deref, DerefMut};
use smallvec::SmallVec;
@@ -109,16 +108,13 @@ fn paint_line(
if wraps.peek() == Some(&&WrapBoundary { run_ix, glyph_ix }) {
wraps.next();
if let Some((background_origin, background_color)) = current_background.as_mut() {
- cx.paint_quad(
+ cx.paint_quad(fill(
Bounds {
origin: *background_origin,
size: size(glyph_origin.x - background_origin.x, line_height),
},
- Corners::default(),
*background_color,
- Edges::default(),
- transparent_black(),
- );
+ ));
background_origin.x = origin.x;
background_origin.y += line_height;
}
@@ -180,16 +176,13 @@ fn paint_line(
}
if let Some((background_origin, background_color)) = finished_background {
- cx.paint_quad(
+ cx.paint_quad(fill(
Bounds {
origin: background_origin,
size: size(glyph_origin.x - background_origin.x, line_height),
},
- Corners::default(),
background_color,
- Edges::default(),
- transparent_black(),
- );
+ ));
}
if let Some((underline_origin, underline_style)) = finished_underline {
@@ -235,16 +228,13 @@ fn paint_line(
}
if let Some((background_origin, background_color)) = current_background.take() {
- cx.paint_quad(
+ cx.paint_quad(fill(
Bounds {
origin: background_origin,
size: size(last_line_end_x - background_origin.x, line_height),
},
- Corners::default(),
background_color,
- Edges::default(),
- transparent_black(),
- );
+ ));
}
if let Some((underline_start, underline_style)) = current_underline.take() {
@@ -1,15 +1,15 @@
use crate::{
- key_dispatch::DispatchActionListener, px, size, Action, AnyDrag, AnyView, AppContext,
- AsyncWindowContext, AvailableSpace, Bounds, BoxShadow, Context, Corners, CursorStyle,
- DevicePixels, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity, EntityId,
- EventEmitter, FileDropEvent, Flatten, FocusEvent, FontId, GlobalElementId, GlyphId, Hsla,
- ImageData, InputEvent, IsZero, KeyBinding, KeyContext, KeyDownEvent, KeystrokeEvent, LayoutId,
- Model, ModelContext, Modifiers, MonochromeSprite, MouseButton, MouseMoveEvent, MouseUpEvent,
- Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point,
- PolychromeSprite, PromptLevel, Quad, Render, RenderGlyphParams, RenderImageParams,
- RenderSvgParams, ScaledPixels, Scene, SceneBuilder, Shadow, SharedString, Size, Style,
- SubscriberSet, Subscription, Surface, TaffyLayoutEngine, Task, Underline, UnderlineStyle, View,
- VisualContext, WeakView, WindowBounds, WindowOptions, SUBPIXEL_VARIANTS,
+ key_dispatch::DispatchActionListener, px, size, transparent_black, Action, AnyDrag, AnyView,
+ AppContext, AsyncWindowContext, AvailableSpace, Bounds, BoxShadow, Context, Corners,
+ CursorStyle, DevicePixels, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity,
+ EntityId, EventEmitter, FileDropEvent, Flatten, FocusEvent, FontId, GlobalElementId, GlyphId,
+ Hsla, ImageData, InputEvent, IsZero, KeyBinding, KeyContext, KeyDownEvent, KeystrokeEvent,
+ LayoutId, Model, ModelContext, Modifiers, MonochromeSprite, MouseButton, MouseMoveEvent,
+ MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformInputHandler,
+ PlatformWindow, Point, PolychromeSprite, PromptLevel, Quad, Render, RenderGlyphParams,
+ RenderImageParams, RenderSvgParams, ScaledPixels, Scene, SceneBuilder, Shadow, SharedString,
+ Size, Style, SubscriberSet, Subscription, Surface, TaffyLayoutEngine, Task, Underline,
+ UnderlineStyle, View, VisualContext, WeakView, WindowBounds, WindowOptions, SUBPIXEL_VARIANTS,
};
use anyhow::{anyhow, Context as _, Result};
use collections::HashMap;
@@ -231,6 +231,7 @@ pub struct Window {
pub(crate) blur_listeners: SubscriberSet<(), AnyObserver>,
default_prevented: bool,
mouse_position: Point<Pixels>,
+ modifiers: Modifiers,
requested_cursor_style: Option<CursorStyle>,
scale_factor: f32,
bounds: WindowBounds,
@@ -302,6 +303,7 @@ impl Window {
let display_id = platform_window.display().id();
let sprite_atlas = platform_window.sprite_atlas();
let mouse_position = platform_window.mouse_position();
+ let modifiers = platform_window.modifiers();
let content_size = platform_window.content_size();
let scale_factor = platform_window.scale_factor();
let bounds = platform_window.bounds();
@@ -365,6 +367,7 @@ impl Window {
blur_listeners: SubscriberSet::new(),
default_prevented: true,
mouse_position,
+ modifiers,
requested_cursor_style: None,
scale_factor,
bounds,
@@ -877,6 +880,11 @@ impl<'a> WindowContext<'a> {
self.window.mouse_position
}
+ /// The current state of the keyboard's modifiers
+ pub fn modifiers(&self) -> Modifiers {
+ self.window.modifiers
+ }
+
pub fn set_cursor_style(&mut self, style: CursorStyle) {
self.window.requested_cursor_style = Some(style)
}
@@ -963,14 +971,8 @@ impl<'a> WindowContext<'a> {
/// Paint one or more quads into the scene for the next frame at the current stacking context.
/// Quads are colored rectangular regions with an optional background, border, and corner radius.
- pub fn paint_quad(
- &mut self,
- bounds: Bounds<Pixels>,
- corner_radii: Corners<Pixels>,
- background: impl Into<Hsla>,
- border_widths: Edges<Pixels>,
- border_color: impl Into<Hsla>,
- ) {
+ /// see [`fill`], [`outline`], and [`quad`] to construct this type.
+ pub fn paint_quad(&mut self, quad: PaintQuad) {
let scale_factor = self.scale_factor();
let content_mask = self.content_mask();
@@ -979,12 +981,12 @@ impl<'a> WindowContext<'a> {
&window.next_frame.z_index_stack,
Quad {
order: 0,
- bounds: bounds.scale(scale_factor),
+ bounds: quad.bounds.scale(scale_factor),
content_mask: content_mask.scale(scale_factor),
- background: background.into(),
- border_color: border_color.into(),
- corner_radii: corner_radii.scale(scale_factor),
- border_widths: border_widths.scale(scale_factor),
+ background: quad.background,
+ border_color: quad.border_color,
+ corner_radii: quad.corner_radii.scale(scale_factor),
+ border_widths: quad.border_widths.scale(scale_factor),
},
);
}
@@ -1342,16 +1344,34 @@ impl<'a> WindowContext<'a> {
// API for the mouse position can only occur on the main thread.
InputEvent::MouseMove(mouse_move) => {
self.window.mouse_position = mouse_move.position;
+ self.window.modifiers = mouse_move.modifiers;
InputEvent::MouseMove(mouse_move)
}
InputEvent::MouseDown(mouse_down) => {
self.window.mouse_position = mouse_down.position;
+ self.window.modifiers = mouse_down.modifiers;
InputEvent::MouseDown(mouse_down)
}
InputEvent::MouseUp(mouse_up) => {
self.window.mouse_position = mouse_up.position;
+ self.window.modifiers = mouse_up.modifiers;
InputEvent::MouseUp(mouse_up)
}
+ InputEvent::MouseExited(mouse_exited) => {
+ // todo!("Should we record that the mouse is outside of the window somehow? Or are these global pixels?")
+ self.window.modifiers = mouse_exited.modifiers;
+
+ InputEvent::MouseExited(mouse_exited)
+ }
+ InputEvent::ModifiersChanged(modifiers_changed) => {
+ self.window.modifiers = modifiers_changed.modifiers;
+ InputEvent::ModifiersChanged(modifiers_changed)
+ }
+ InputEvent::ScrollWheel(scroll_wheel) => {
+ self.window.mouse_position = scroll_wheel.position;
+ self.window.modifiers = scroll_wheel.modifiers;
+ InputEvent::ScrollWheel(scroll_wheel)
+ }
// Translate dragging and dropping of external files from the operating system
// to internal drag and drop events.
InputEvent::FileDrop(file_drop) => match file_drop {
@@ -1394,7 +1414,7 @@ impl<'a> WindowContext<'a> {
click_count: 1,
}),
},
- _ => event,
+ InputEvent::KeyDown(_) | InputEvent::KeyUp(_) => event,
};
if let Some(any_mouse_event) = event.mouse_event() {
@@ -2962,3 +2982,85 @@ impl From<(&'static str, u64)> for ElementId {
ElementId::NamedInteger(name.into(), id as usize)
}
}
+
+/// A rectangle, to be rendered on the screen by GPUI at the given position and size.
+pub struct PaintQuad {
+ bounds: Bounds<Pixels>,
+ corner_radii: Corners<Pixels>,
+ background: Hsla,
+ border_widths: Edges<Pixels>,
+ border_color: Hsla,
+}
+
+impl PaintQuad {
+ /// Set the corner radii of the quad.
+ pub fn corner_radii(self, corner_radii: impl Into<Corners<Pixels>>) -> Self {
+ PaintQuad {
+ corner_radii: corner_radii.into(),
+ ..self
+ }
+ }
+
+ /// Set the border widths of the quad.
+ pub fn border_widths(self, border_widths: impl Into<Edges<Pixels>>) -> Self {
+ PaintQuad {
+ border_widths: border_widths.into(),
+ ..self
+ }
+ }
+
+ /// Set the border color of the quad.
+ pub fn border_color(self, border_color: impl Into<Hsla>) -> Self {
+ PaintQuad {
+ border_color: border_color.into(),
+ ..self
+ }
+ }
+
+ /// Set the background color of the quad.
+ pub fn background(self, background: impl Into<Hsla>) -> Self {
+ PaintQuad {
+ background: background.into(),
+ ..self
+ }
+ }
+}
+
+/// Create a quad with the given parameters.
+pub fn quad(
+ bounds: Bounds<Pixels>,
+ corner_radii: impl Into<Corners<Pixels>>,
+ background: impl Into<Hsla>,
+ border_widths: impl Into<Edges<Pixels>>,
+ border_color: impl Into<Hsla>,
+) -> PaintQuad {
+ PaintQuad {
+ bounds,
+ corner_radii: corner_radii.into(),
+ background: background.into(),
+ border_widths: border_widths.into(),
+ border_color: border_color.into(),
+ }
+}
+
+/// Create a filled quad with the given bounds and background color.
+pub fn fill(bounds: impl Into<Bounds<Pixels>>, background: impl Into<Hsla>) -> PaintQuad {
+ PaintQuad {
+ bounds: bounds.into(),
+ corner_radii: (0.).into(),
+ background: background.into(),
+ border_widths: (0.).into(),
+ border_color: transparent_black(),
+ }
+}
+
+/// Create a rectangle outline with the given bounds, border color, and a 1px border width
+pub fn outline(bounds: impl Into<Bounds<Pixels>>, border_color: impl Into<Hsla>) -> PaintQuad {
+ PaintQuad {
+ bounds: bounds.into(),
+ corner_radii: (0.).into(),
+ background: transparent_black(),
+ border_widths: (1.).into(),
+ border_color: border_color.into(),
+ }
+}
@@ -0,0 +1,23 @@
+use proc_macro::TokenStream;
+use quote::quote;
+use syn::{parse_macro_input, DeriveInput};
+
+pub fn derive_render(input: TokenStream) -> TokenStream {
+ let ast = parse_macro_input!(input as DeriveInput);
+ let type_name = &ast.ident;
+ let (impl_generics, type_generics, where_clause) = ast.generics.split_for_impl();
+
+ let gen = quote! {
+ impl #impl_generics gpui::Render for #type_name #type_generics
+ #where_clause
+ {
+ type Element = ();
+
+ fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
+ ()
+ }
+ }
+ };
+
+ gen.into()
+}
@@ -1,4 +1,5 @@
mod derive_into_element;
+mod derive_render;
mod register_action;
mod style_helpers;
mod test;
@@ -15,6 +16,11 @@ pub fn derive_into_element(input: TokenStream) -> TokenStream {
derive_into_element::derive_into_element(input)
}
+#[proc_macro_derive(Render)]
+pub fn derive_render(input: TokenStream) -> TokenStream {
+ derive_render::derive_render(input)
+}
+
#[proc_macro]
pub fn style_helpers(input: TokenStream) -> TokenStream {
style_helpers::style_helpers(input)
@@ -1,9 +1,9 @@
use editor::{Cursor, HighlightedRange, HighlightedRangeLine};
use gpui::{
- black, div, point, px, red, relative, transparent_black, AnyElement, AsyncWindowContext,
- AvailableSpace, Bounds, DispatchPhase, Element, ElementId, ExternalPaths, FocusHandle, Font,
- FontStyle, FontWeight, HighlightStyle, Hsla, InteractiveElement, InteractiveElementState,
- IntoElement, LayoutId, Model, ModelContext, ModifiersChangedEvent, MouseButton, Pixels,
+ black, div, fill, point, px, red, relative, AnyElement, AsyncWindowContext, AvailableSpace,
+ Bounds, DispatchPhase, Element, ElementId, ExternalPaths, FocusHandle, Font, FontStyle,
+ FontWeight, HighlightStyle, Hsla, InteractiveElement, InteractiveElementState, IntoElement,
+ LayoutId, Model, ModelContext, ModifiersChangedEvent, MouseButton, Pixels,
PlatformInputHandler, Point, Rgba, ShapedLine, Size, StatefulInteractiveElement, Styled,
TextRun, TextStyle, TextSystem, UnderlineStyle, WhiteSpace, WindowContext,
};
@@ -133,13 +133,7 @@ impl LayoutRect {
)
.into();
- cx.paint_quad(
- Bounds::new(position, size),
- Default::default(),
- self.color,
- Default::default(),
- transparent_black(),
- );
+ cx.paint_quad(fill(Bounds::new(position, size), self.color));
}
}
@@ -775,13 +769,7 @@ impl Element for TerminalElement {
let theme = cx.theme();
- cx.paint_quad(
- bounds,
- Default::default(),
- layout.background_color,
- Default::default(),
- Hsla::default(),
- );
+ cx.paint_quad(fill(bounds, layout.background_color));
let origin = bounds.origin + Point::new(layout.gutter, px(0.));
let terminal_input_handler = TerminalInputHandler {
@@ -1,5 +1,5 @@
+use crate::DraggedDock;
use crate::{status_bar::StatusItemView, Workspace};
-use crate::{DockClickReset, DockDragState};
use gpui::{
div, px, Action, AnchorCorner, AnyView, AppContext, Axis, ClickEvent, Div, Entity, EntityId,
EventEmitter, FocusHandle, FocusableView, IntoElement, MouseButton, ParentElement, Render,
@@ -493,47 +493,37 @@ impl Render for Dock {
let handler = div()
.id("resize-handle")
.bg(cx.theme().colors().border)
- .on_mouse_down(gpui::MouseButton::Left, move |_, cx| {
- cx.update_global(|drag: &mut DockDragState, cx| drag.0 = Some(position))
- })
+ .on_drag(move |cx| cx.build_view(|_| DraggedDock(position)))
.on_click(cx.listener(|v, e: &ClickEvent, cx| {
- if e.down.button == MouseButton::Left {
- cx.update_global(|state: &mut DockClickReset, cx| {
- if state.0.is_some() {
- state.0 = None;
- v.resize_active_panel(None, cx)
- } else {
- let double_click = cx.double_click_interval();
- let timer = cx.background_executor().timer(double_click);
- state.0 = Some(cx.spawn(|_, mut cx| async move {
- timer.await;
- cx.update_global(|state: &mut DockClickReset, cx| {
- state.0 = None;
- })
- .ok();
- }));
- }
- })
+ if e.down.button == MouseButton::Left && e.down.click_count == 2 {
+ v.resize_active_panel(None, cx)
}
- }));
+ }))
+ .z_index(1);
+
+ const HANDLE_SIZE: Pixels = Pixels(6.);
match self.position() {
DockPosition::Left => {
- post_resize_handle = Some(handler.w_1().h_full().cursor_col_resize())
+ post_resize_handle =
+ Some(handler.min_w(HANDLE_SIZE).h_full().cursor_col_resize())
}
DockPosition::Bottom => {
- pre_resize_handle = Some(handler.w_full().h_1().cursor_row_resize())
+ pre_resize_handle =
+ Some(handler.w_full().min_h(HANDLE_SIZE).cursor_row_resize())
}
DockPosition::Right => {
- pre_resize_handle = Some(handler.w_full().h_1().cursor_col_resize())
+ pre_resize_handle =
+ Some(handler.min_w(HANDLE_SIZE).h_full().cursor_col_resize())
}
}
div()
+ .flex()
.border_color(cx.theme().colors().border)
.map(|this| match self.position().axis() {
- Axis::Horizontal => this.w(px(size)).h_full(),
- Axis::Vertical => this.h(px(size)).w_full(),
+ Axis::Horizontal => this.w(px(size)).h_full().flex_row(),
+ Axis::Vertical => this.h(px(size)).w_full().flex_col(),
})
.map(|this| match self.position() {
DockPosition::Left => this.border_r(),
@@ -541,7 +531,14 @@ impl Render for Dock {
DockPosition::Bottom => this.border_t(),
})
.children(pre_resize_handle)
- .child(entry.panel.to_any())
+ .child(
+ div()
+ .map(|this| match self.position().axis() {
+ Axis::Horizontal => this.min_w(px(size) - HANDLE_SIZE).h_full(),
+ Axis::Vertical => this.min_h(px(size) - HANDLE_SIZE).w_full(),
+ })
+ .child(entry.panel.to_any()),
+ )
.children(post_resize_handle)
} else {
div()
@@ -30,11 +30,11 @@ use futures::{
};
use gpui::{
actions, canvas, div, impl_actions, point, size, Action, AnyModel, AnyView, AnyWeakView,
- AnyWindowHandle, AppContext, AsyncAppContext, AsyncWindowContext, Bounds, Context, Div, Entity,
- EntityId, EventEmitter, FocusHandle, FocusableView, GlobalPixels, InteractiveElement,
- KeyContext, ManagedView, Model, ModelContext, MouseMoveEvent, ParentElement, PathPromptOptions,
- Pixels, Point, PromptLevel, Render, Size, Styled, Subscription, Task, View, ViewContext,
- VisualContext, WeakView, WindowBounds, WindowContext, WindowHandle, WindowOptions,
+ AnyWindowHandle, AppContext, AsyncAppContext, AsyncWindowContext, Bounds, Context, Div,
+ DragMoveEvent, Entity, EntityId, EventEmitter, FocusHandle, FocusableView, GlobalPixels,
+ InteractiveElement, KeyContext, ManagedView, Model, ModelContext, ParentElement,
+ PathPromptOptions, Pixels, Point, PromptLevel, Render, Size, Styled, Subscription, Task, View,
+ ViewContext, VisualContext, WeakView, WindowBounds, WindowContext, WindowHandle, WindowOptions,
};
use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings, ProjectItem};
use itertools::Itertools;
@@ -227,9 +227,6 @@ pub fn init_settings(cx: &mut AppContext) {
}
pub fn init(app_state: Arc<AppState>, cx: &mut AppContext) {
- cx.default_global::<DockDragState>();
- cx.default_global::<DockClickReset>();
-
init_settings(cx);
notifications::init(cx);
@@ -466,6 +463,7 @@ pub struct Workspace {
_observe_current_user: Task<Result<()>>,
_schedule_serialize: Option<Task<()>>,
pane_history_timestamp: Arc<AtomicUsize>,
+ bounds: Bounds<Pixels>,
}
impl EventEmitter<Event> for Workspace {}
@@ -708,6 +706,8 @@ impl Workspace {
subscriptions,
pane_history_timestamp,
workspace_actions: Default::default(),
+ // This data will be incorrect, but it will be overwritten by the time it needs to be used.
+ bounds: Default::default(),
}
}
@@ -3580,13 +3580,8 @@ impl FocusableView for Workspace {
struct WorkspaceBounds(Bounds<Pixels>);
-//todo!("remove this when better drag APIs are in GPUI2")
-#[derive(Default)]
-struct DockDragState(Option<DockPosition>);
-
-//todo!("remove this when better double APIs are in GPUI2")
-#[derive(Default)]
-struct DockClickReset(Option<Task<()>>);
+#[derive(Render)]
+struct DraggedDock(DockPosition);
impl Render for Workspace {
type Element = Div;
@@ -3632,37 +3627,37 @@ impl Render for Workspace {
.border_t()
.border_b()
.border_color(cx.theme().colors().border)
- .on_mouse_up(gpui::MouseButton::Left, |_, cx| {
- cx.update_global(|drag: &mut DockDragState, cx| {
- drag.0 = None;
- })
- })
- .on_mouse_move(cx.listener(|workspace, e: &MouseMoveEvent, cx| {
- if let Some(types) = &cx.global::<DockDragState>().0 {
- let workspace_bounds = cx.global::<WorkspaceBounds>().0;
- match types {
+ .child(
+ canvas(cx.listener(|workspace, bounds, cx| {
+ workspace.bounds = *bounds;
+ }))
+ .absolute()
+ .size_full(),
+ )
+ .on_drag_move(
+ cx.listener(|workspace, e: &DragMoveEvent<DraggedDock>, cx| {
+ match e.drag.read(cx).0 {
DockPosition::Left => {
- let size = e.position.x;
+ let size = workspace.bounds.left() + e.event.position.x;
workspace.left_dock.update(cx, |left_dock, cx| {
left_dock.resize_active_panel(Some(size.0), cx);
});
}
DockPosition::Right => {
- let size = workspace_bounds.size.width - e.position.x;
+ let size = workspace.bounds.right() - e.event.position.x;
workspace.right_dock.update(cx, |right_dock, cx| {
right_dock.resize_active_panel(Some(size.0), cx);
});
}
DockPosition::Bottom => {
- let size = workspace_bounds.size.height - e.position.y;
+ let size = workspace.bounds.bottom() - e.event.position.y;
workspace.bottom_dock.update(cx, |bottom_dock, cx| {
bottom_dock.resize_active_panel(Some(size.0), cx);
});
}
}
- }
- }))
- .child(canvas(|bounds, cx| cx.set_global(WorkspaceBounds(bounds))))
+ }),
+ )
.child(self.modal_layer.clone())
.child(
div()