Merge branch 'main' into project_search_design

This commit is contained in:
Piotr Osiewicz 2023-08-12 21:57:21 +02:00
commit 6be73e46bf
41 changed files with 528 additions and 115 deletions

3
Cargo.lock generated
View File

@ -3172,6 +3172,7 @@ dependencies = [
name = "gpui_macros"
version = "0.1.0"
dependencies = [
"gpui",
"proc-macro2",
"quote",
"syn 1.0.109",
@ -9860,7 +9861,7 @@ dependencies = [
[[package]]
name = "zed"
version = "0.99.0"
version = "0.100.0"
dependencies = [
"activity_indicator",
"ai",

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,93 @@
Copyright © 2017 IBM Corp. with Reserved Font Name "Plex"
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

View File

@ -362,7 +362,7 @@ impl AssistantPanel {
this.set_active_editor_index(this.prev_active_editor_index, cx);
}
})
.with_tooltip::<History>(1, "History".into(), None, tooltip_style, cx)
.with_tooltip::<History>(1, "History", None, tooltip_style, cx)
}
fn render_editor_tools(&self, cx: &mut ViewContext<Self>) -> Vec<AnyElement<Self>> {
@ -394,7 +394,7 @@ impl AssistantPanel {
})
.with_tooltip::<Split>(
1,
"Split Message".into(),
"Split Message",
Some(Box::new(Split)),
tooltip_style,
cx,
@ -416,13 +416,7 @@ impl AssistantPanel {
active_editor.update(cx, |editor, cx| editor.assist(&Default::default(), cx));
}
})
.with_tooltip::<Assist>(
1,
"Assist".into(),
Some(Box::new(Assist)),
tooltip_style,
cx,
)
.with_tooltip::<Assist>(1, "Assist", Some(Box::new(Assist)), tooltip_style, cx)
}
fn render_quote_button(cx: &mut ViewContext<Self>) -> impl Element<Self> {
@ -446,7 +440,7 @@ impl AssistantPanel {
})
.with_tooltip::<QuoteSelection>(
1,
"Quote Selection".into(),
"Quote Selection",
Some(Box::new(QuoteSelection)),
tooltip_style,
cx,
@ -468,7 +462,7 @@ impl AssistantPanel {
})
.with_tooltip::<NewConversation>(
1,
"New Conversation".into(),
"New Conversation",
Some(Box::new(NewConversation)),
tooltip_style,
cx,
@ -498,11 +492,7 @@ impl AssistantPanel {
})
.with_tooltip::<ToggleZoom>(
0,
if self.zoomed {
"Zoom Out".into()
} else {
"Zoom In".into()
},
if self.zoomed { "Zoom Out" } else { "Zoom In" },
Some(Box::new(ToggleZoom)),
tooltip_style,
cx,

View File

@ -238,7 +238,7 @@ impl CollabTitlebarItem {
.left()
.with_tooltip::<RecentProjectsTooltip>(
0,
"Recent projects".into(),
"Recent projects",
Some(Box::new(recent_projects::OpenRecent)),
theme.tooltip.clone(),
cx,
@ -282,7 +282,7 @@ impl CollabTitlebarItem {
.left()
.with_tooltip::<BranchPopoverTooltip>(
0,
"Recent branches".into(),
"Recent branches",
Some(Box::new(ToggleVcsMenu)),
theme.tooltip.clone(),
cx,
@ -582,7 +582,7 @@ impl CollabTitlebarItem {
})
.with_tooltip::<ToggleContactsMenu>(
0,
"Show contacts menu".into(),
"Show contacts menu",
Some(Box::new(ToggleContactsMenu)),
theme.tooltip.clone(),
cx,
@ -633,7 +633,7 @@ impl CollabTitlebarItem {
})
.with_tooltip::<ToggleScreenSharing>(
0,
tooltip.into(),
tooltip,
Some(Box::new(ToggleScreenSharing)),
theme.tooltip.clone(),
cx,
@ -686,7 +686,7 @@ impl CollabTitlebarItem {
})
.with_tooltip::<ToggleMute>(
0,
tooltip.into(),
tooltip,
Some(Box::new(ToggleMute)),
theme.tooltip.clone(),
cx,
@ -734,7 +734,7 @@ impl CollabTitlebarItem {
})
.with_tooltip::<ToggleDeafen>(
0,
tooltip.into(),
tooltip,
Some(Box::new(ToggleDeafen)),
theme.tooltip.clone(),
cx,
@ -768,7 +768,7 @@ impl CollabTitlebarItem {
})
.with_tooltip::<LeaveCall>(
0,
tooltip.into(),
tooltip,
Some(Box::new(LeaveCall)),
theme.tooltip.clone(),
cx,

View File

@ -837,7 +837,7 @@ impl ContactList {
),
background: Some(tree_branch.color),
border: gpui::Border::default(),
corner_radius: 0.,
corner_radii: Default::default(),
});
scene.push_quad(gpui::Quad {
bounds: RectF::from_points(
@ -846,7 +846,7 @@ impl ContactList {
),
background: Some(tree_branch.color),
border: gpui::Border::default(),
corner_radius: 0.,
corner_radii: Default::default(),
});
}))
.constrained()
@ -934,7 +934,7 @@ impl ContactList {
),
background: Some(tree_branch.color),
border: gpui::Border::default(),
corner_radius: 0.,
corner_radii: Default::default(),
});
scene.push_quad(gpui::Quad {
bounds: RectF::from_points(
@ -943,7 +943,7 @@ impl ContactList {
),
background: Some(tree_branch.color),
border: gpui::Border::default(),
corner_radius: 0.,
corner_radii: Default::default(),
});
}))
.constrained()
@ -1345,7 +1345,7 @@ impl View for ContactList {
})
.with_tooltip::<AddContact>(
0,
"Search for new contact".into(),
"Search for new contact",
None,
theme.tooltip.clone(),
cx,

View File

@ -140,7 +140,7 @@ impl View for CopilotButton {
})
.with_tooltip::<Self>(
0,
"GitHub Copilot".into(),
"GitHub Copilot",
None,
theme.tooltip.clone(),
cx,

View File

@ -173,7 +173,7 @@ impl View for DiagnosticIndicator {
})
.with_tooltip::<Summary>(
0,
"Project Diagnostics".to_string(),
"Project Diagnostics",
Some(Box::new(crate::Deploy)),
tooltip_style,
cx,

View File

@ -8685,7 +8685,7 @@ pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> Rend
// We really need to rethink this ID system...
.with_tooltip::<BlockContextToolip>(
cx.block_id,
"Copy diagnostic message".to_string(),
"Copy diagnostic message",
None,
tooltip_style,
cx,

View File

@ -488,13 +488,13 @@ impl EditorElement {
bounds: gutter_bounds,
background: Some(self.style.gutter_background),
border: Border::new(0., Color::transparent_black()),
corner_radius: 0.,
corner_radii: Default::default(),
});
scene.push_quad(Quad {
bounds: text_bounds,
background: Some(self.style.background),
border: Border::new(0., Color::transparent_black()),
corner_radius: 0.,
corner_radii: Default::default(),
});
if let EditorMode::Full = layout.mode {
@ -522,7 +522,7 @@ impl EditorElement {
bounds: RectF::new(origin, size),
background: Some(self.style.active_line_background),
border: Border::default(),
corner_radius: 0.,
corner_radii: Default::default(),
});
}
}
@ -542,7 +542,7 @@ impl EditorElement {
bounds: RectF::new(origin, size),
background: Some(self.style.highlighted_line_background),
border: Border::default(),
corner_radius: 0.,
corner_radii: Default::default(),
});
}
@ -572,7 +572,7 @@ impl EditorElement {
),
background: Some(color),
border: Border::new(0., Color::transparent_black()),
corner_radius: 0.,
corner_radii: Default::default(),
});
}
}
@ -673,7 +673,7 @@ impl EditorElement {
bounds: highlight_bounds,
background: Some(diff_style.modified),
border: Border::new(0., Color::transparent_black()),
corner_radius: 1. * line_height,
corner_radii: (1. * line_height).into(),
});
continue;
@ -706,7 +706,7 @@ impl EditorElement {
bounds: highlight_bounds,
background: Some(diff_style.deleted),
border: Border::new(0., Color::transparent_black()),
corner_radius: 1. * line_height,
corner_radii: (1. * line_height).into(),
});
continue;
@ -728,7 +728,7 @@ impl EditorElement {
bounds: highlight_bounds,
background: Some(color),
border: Border::new(0., Color::transparent_black()),
corner_radius: diff_style.corner_radius * line_height,
corner_radii: (diff_style.corner_radius * line_height).into(),
});
}
}
@ -1129,7 +1129,7 @@ impl EditorElement {
bounds,
background: Some(color),
border,
corner_radius: style.thumb.corner_radius,
corner_radii: style.thumb.corner_radii.into(),
})
};
let background_ranges = editor
@ -1189,7 +1189,7 @@ impl EditorElement {
bounds,
background: Some(color),
border,
corner_radius: style.thumb.corner_radius,
corner_radii: style.thumb.corner_radii.into(),
})
}
}
@ -1198,7 +1198,7 @@ impl EditorElement {
bounds: thumb_bounds,
border: style.thumb.border,
background: style.thumb.background_color,
corner_radius: style.thumb.corner_radius,
corner_radii: style.thumb.corner_radii.into(),
});
}
@ -2725,14 +2725,14 @@ impl Cursor {
bounds,
background: None,
border: Border::all(1., self.color),
corner_radius: 0.,
corner_radii: Default::default(),
});
} else {
scene.push_quad(Quad {
bounds,
background: Some(self.color),
border: Default::default(),
corner_radius: 0.,
corner_radii: Default::default(),
});
}

View File

@ -599,7 +599,7 @@ impl InfoPopover {
bounds,
background: Some(code_span_background_color),
border: Default::default(),
corner_radius: 2.0,
corner_radii: (2.0).into(),
});
}
},

View File

@ -66,7 +66,7 @@ impl View for DeployFeedbackButton {
})
.with_tooltip::<Self>(
0,
"Send Feedback".into(),
"Send Feedback",
Some(Box::new(GiveFeedback)),
theme.tooltip.clone(),
cx,

View File

@ -80,7 +80,7 @@ impl View for SubmitFeedbackButton {
.with_margin_left(theme.feedback.button_margin)
.with_tooltip::<Self>(
0,
"cmd-s".into(),
"cmd-s",
Some(Box::new(SubmitFeedback)),
theme.tooltip.clone(),
cx,

View File

@ -0,0 +1,155 @@
use gpui::{
color::Color, geometry::rect::RectF, scene::Shadow, AnyElement, App, Element, Entity, Quad,
View,
};
use log::LevelFilter;
use pathfinder_geometry::vector::vec2f;
use simplelog::SimpleLogger;
fn main() {
SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
App::new(()).unwrap().run(|cx| {
cx.platform().activate(true);
cx.add_window(Default::default(), |_| CornersView);
});
}
struct CornersView;
impl Entity for CornersView {
type Event = ();
}
impl View for CornersView {
fn ui_name() -> &'static str {
"CornersView"
}
fn render(&mut self, _: &mut gpui::ViewContext<Self>) -> AnyElement<CornersView> {
CornersElement.into_any()
}
}
struct CornersElement;
impl<V: View> gpui::Element<V> for CornersElement {
type LayoutState = ();
type PaintState = ();
fn layout(
&mut self,
constraint: gpui::SizeConstraint,
_: &mut V,
_: &mut gpui::LayoutContext<V>,
) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
(constraint.max, ())
}
fn paint(
&mut self,
scene: &mut gpui::SceneBuilder,
bounds: pathfinder_geometry::rect::RectF,
_: pathfinder_geometry::rect::RectF,
_: &mut Self::LayoutState,
_: &mut V,
_: &mut gpui::PaintContext<V>,
) -> Self::PaintState {
scene.push_quad(Quad {
bounds,
background: Some(Color::white()),
..Default::default()
});
scene.push_layer(None);
scene.push_quad(Quad {
bounds: RectF::new(vec2f(100., 100.), vec2f(100., 100.)),
background: Some(Color::red()),
border: Default::default(),
corner_radii: gpui::scene::CornerRadii {
top_left: 20.,
..Default::default()
},
});
scene.push_quad(Quad {
bounds: RectF::new(vec2f(200., 100.), vec2f(100., 100.)),
background: Some(Color::green()),
border: Default::default(),
corner_radii: gpui::scene::CornerRadii {
top_right: 20.,
..Default::default()
},
});
scene.push_quad(Quad {
bounds: RectF::new(vec2f(100., 200.), vec2f(100., 100.)),
background: Some(Color::blue()),
border: Default::default(),
corner_radii: gpui::scene::CornerRadii {
bottom_left: 20.,
..Default::default()
},
});
scene.push_quad(Quad {
bounds: RectF::new(vec2f(200., 200.), vec2f(100., 100.)),
background: Some(Color::yellow()),
border: Default::default(),
corner_radii: gpui::scene::CornerRadii {
bottom_right: 20.,
..Default::default()
},
});
scene.push_shadow(Shadow {
bounds: RectF::new(vec2f(400., 100.), vec2f(100., 100.)),
corner_radii: gpui::scene::CornerRadii {
bottom_right: 20.,
..Default::default()
},
sigma: 20.0,
color: Color::black(),
});
scene.push_layer(None);
scene.push_quad(Quad {
bounds: RectF::new(vec2f(400., 100.), vec2f(100., 100.)),
background: Some(Color::red()),
border: Default::default(),
corner_radii: gpui::scene::CornerRadii {
bottom_right: 20.,
..Default::default()
},
});
scene.pop_layer();
scene.pop_layer();
}
fn rect_for_text_range(
&self,
_: std::ops::Range<usize>,
_: pathfinder_geometry::rect::RectF,
_: pathfinder_geometry::rect::RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
_: &V,
_: &gpui::ViewContext<V>,
) -> Option<pathfinder_geometry::rect::RectF> {
unimplemented!()
}
fn debug(
&self,
_: pathfinder_geometry::rect::RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
_: &V,
_: &gpui::ViewContext<V>,
) -> serde_json::Value {
unimplemented!()
}
}

View File

@ -52,8 +52,8 @@ pub struct Window {
cursor_regions: Vec<CursorRegion>,
mouse_regions: Vec<(MouseRegion, usize)>,
last_mouse_moved_event: Option<Event>,
pub(crate) hovered_region_ids: HashSet<MouseRegionId>,
pub(crate) clicked_region_ids: HashSet<MouseRegionId>,
pub(crate) hovered_region_ids: Vec<MouseRegionId>,
pub(crate) clicked_region_ids: Vec<MouseRegionId>,
pub(crate) clicked_region: Option<(MouseRegionId, MouseButton)>,
mouse_position: Vector2F,
text_layout_cache: TextLayoutCache,
@ -678,6 +678,7 @@ impl<'a> WindowContext<'a> {
let mut highest_z_index = None;
let mouse_position = self.window.mouse_position.clone();
let window = &mut *self.window;
let prev_hovered_regions = mem::take(&mut window.hovered_region_ids);
for (region, z_index) in window.mouse_regions.iter().rev() {
// Allow mouse regions to appear transparent to hovers
if !region.hoverable {
@ -696,7 +697,11 @@ impl<'a> WindowContext<'a> {
// highest_z_index is set.
if contains_mouse && z_index == highest_z_index.unwrap() {
//Ensure that hover entrance events aren't sent twice
if window.hovered_region_ids.insert(region.id()) {
if let Err(ix) = window.hovered_region_ids.binary_search(&region.id()) {
window.hovered_region_ids.insert(ix, region.id());
}
// window.hovered_region_ids.insert(region.id());
if !prev_hovered_regions.contains(&region.id()) {
valid_regions.push(region.clone());
if region.notify_on_hover {
notified_views.insert(region.id().view_id());
@ -704,7 +709,7 @@ impl<'a> WindowContext<'a> {
}
} else {
// Ensure that hover exit events aren't sent twice
if window.hovered_region_ids.remove(&region.id()) {
if prev_hovered_regions.contains(&region.id()) {
valid_regions.push(region.clone());
if region.notify_on_hover {
notified_views.insert(region.id().view_id());

View File

@ -170,7 +170,7 @@ pub trait Element<V: View>: 'static {
fn with_tooltip<Tag: 'static>(
self,
id: usize,
text: String,
text: impl Into<Cow<'static, str>>,
action: Option<Box<dyn Action>>,
style: TooltipStyle,
cx: &mut ViewContext<V>,
@ -178,7 +178,7 @@ pub trait Element<V: View>: 'static {
where
Self: 'static + Sized,
{
Tooltip::new::<Tag, V>(id, text, action, style, self.into_any(), cx)
Tooltip::new::<Tag>(id, text, action, style, self.into_any(), cx)
}
fn resizable(
@ -201,6 +201,10 @@ pub trait Element<V: View>: 'static {
}
}
pub trait RenderElement {
fn render<V: View>(&mut self, view: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V>;
}
trait AnyElementState<V: View> {
fn layout(
&mut self,

View File

@ -9,7 +9,7 @@ use crate::{
},
json::ToJson,
platform::CursorStyle,
scene::{self, Border, CursorRegion, Quad},
scene::{self, Border, CornerRadii, CursorRegion, Quad},
AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
ViewContext,
};
@ -30,7 +30,8 @@ pub struct ContainerStyle {
#[serde(default)]
pub border: Border,
#[serde(default)]
pub corner_radius: f32,
#[serde(alias = "corner_radius")]
pub corner_radii: CornerRadii,
#[serde(default)]
pub shadow: Option<Shadow>,
#[serde(default)]
@ -133,7 +134,10 @@ impl<V: View> Container<V> {
}
pub fn with_corner_radius(mut self, radius: f32) -> Self {
self.style.corner_radius = radius;
self.style.corner_radii.top_left = radius;
self.style.corner_radii.top_right = radius;
self.style.corner_radii.bottom_right = radius;
self.style.corner_radii.bottom_left = radius;
self
}
@ -225,7 +229,7 @@ impl<V: View> Element<V> for Container<V> {
if let Some(shadow) = self.style.shadow.as_ref() {
scene.push_shadow(scene::Shadow {
bounds: quad_bounds + shadow.offset,
corner_radius: self.style.corner_radius,
corner_radii: self.style.corner_radii,
sigma: shadow.blur,
color: shadow.color,
});
@ -248,7 +252,7 @@ impl<V: View> Element<V> for Container<V> {
bounds: quad_bounds,
background: self.style.background_color,
border: Default::default(),
corner_radius: self.style.corner_radius,
corner_radii: self.style.corner_radii.into(),
});
self.child
@ -259,7 +263,7 @@ impl<V: View> Element<V> for Container<V> {
bounds: quad_bounds,
background: self.style.overlay_color,
border: self.style.border,
corner_radius: self.style.corner_radius,
corner_radii: self.style.corner_radii.into(),
});
scene.pop_layer();
} else {
@ -267,7 +271,7 @@ impl<V: View> Element<V> for Container<V> {
bounds: quad_bounds,
background: self.style.background_color,
border: self.style.border,
corner_radius: self.style.corner_radius,
corner_radii: self.style.corner_radii.into(),
});
let child_origin = child_origin
@ -284,7 +288,7 @@ impl<V: View> Element<V> for Container<V> {
bounds: quad_bounds,
background: self.style.overlay_color,
border: Default::default(),
corner_radius: 0.,
corner_radii: self.style.corner_radii.into(),
});
scene.pop_layer();
}
@ -328,7 +332,7 @@ impl ToJson for ContainerStyle {
"padding": self.padding.to_json(),
"background_color": self.background_color.to_json(),
"border": self.border.to_json(),
"corner_radius": self.corner_radius,
"corner_radius": self.corner_radii,
"shadow": self.shadow.to_json(),
})
}

View File

@ -103,7 +103,7 @@ impl<V: View> Element<V> for Image {
scene.push_image(scene::Image {
bounds,
border: self.style.border,
corner_radius: self.style.corner_radius,
corner_radii: self.style.corner_radius.into(),
grayscale: self.style.grayscale,
data: data.clone(),
});

View File

@ -12,6 +12,7 @@ use crate::{
use schemars::JsonSchema;
use serde::Deserialize;
use std::{
borrow::Cow,
cell::{Cell, RefCell},
ops::Range,
rc::Rc,
@ -52,9 +53,9 @@ pub struct KeystrokeStyle {
}
impl<V: View> Tooltip<V> {
pub fn new<Tag: 'static, T: View>(
pub fn new<Tag: 'static>(
id: usize,
text: String,
text: impl Into<Cow<'static, str>>,
action: Option<Box<dyn Action>>,
style: TooltipStyle,
child: AnyElement<V>,
@ -66,6 +67,8 @@ impl<V: View> Tooltip<V> {
let state_handle = cx.default_element_state::<ElementState<Tag>, Rc<TooltipState>>(id);
let state = state_handle.read(cx).clone();
let text = text.into();
let tooltip = if state.visible.get() {
let mut collapsed_tooltip = Self::render_tooltip(
focused_view_id,
@ -127,7 +130,7 @@ impl<V: View> Tooltip<V> {
pub fn render_tooltip(
focused_view_id: Option<usize>,
text: String,
text: impl Into<Cow<'static, str>>,
style: TooltipStyle,
action: Option<Box<dyn Action>>,
measure: bool,

View File

@ -509,10 +509,14 @@ impl Renderer {
};
for (ix, shadow) in shadows.iter().enumerate() {
let shape_bounds = shadow.bounds * scale_factor;
let corner_radii = shadow.corner_radii * scale_factor;
let shader_shadow = shaders::GPUIShadow {
origin: shape_bounds.origin().to_float2(),
size: shape_bounds.size().to_float2(),
corner_radius: shadow.corner_radius * scale_factor,
corner_radius_top_left: corner_radii.top_left,
corner_radius_top_right: corner_radii.top_right,
corner_radius_bottom_right: corner_radii.bottom_right,
corner_radius_bottom_left: corner_radii.bottom_left,
sigma: shadow.sigma,
color: shadow.color.to_uchar4(),
};
@ -586,7 +590,10 @@ impl Renderer {
border_bottom: border_width * (quad.border.bottom as usize as f32),
border_left: border_width * (quad.border.left as usize as f32),
border_color: quad.border.color.to_uchar4(),
corner_radius: quad.corner_radius * scale_factor,
corner_radius_top_left: quad.corner_radii.top_left * scale_factor,
corner_radius_top_right: quad.corner_radii.top_right * scale_factor,
corner_radius_bottom_right: quad.corner_radii.bottom_right * scale_factor,
corner_radius_bottom_left: quad.corner_radii.bottom_left * scale_factor,
};
unsafe {
*(buffer_contents.add(ix)) = shader_quad;
@ -738,7 +745,7 @@ impl Renderer {
for image in images {
let origin = image.bounds.origin() * scale_factor;
let target_size = image.bounds.size() * scale_factor;
let corner_radius = image.corner_radius * scale_factor;
let corner_radii = image.corner_radii * scale_factor;
let border_width = image.border.width * scale_factor;
let (alloc_id, atlas_bounds) = self.image_cache.render(&image.data);
images_by_atlas
@ -754,7 +761,10 @@ impl Renderer {
border_bottom: border_width * (image.border.bottom as usize as f32),
border_left: border_width * (image.border.left as usize as f32),
border_color: image.border.color.to_uchar4(),
corner_radius,
corner_radius_top_left: corner_radii.top_left,
corner_radius_top_right: corner_radii.top_right,
corner_radius_bottom_right: corner_radii.bottom_right,
corner_radius_bottom_left: corner_radii.bottom_left,
grayscale: image.grayscale as u8,
});
}
@ -777,7 +787,10 @@ impl Renderer {
border_bottom: 0.,
border_left: 0.,
border_color: Default::default(),
corner_radius: 0.,
corner_radius_top_left: 0.,
corner_radius_top_right: 0.,
corner_radius_bottom_right: 0.,
corner_radius_bottom_left: 0.,
grayscale: false as u8,
});
} else {

View File

@ -19,7 +19,10 @@ typedef struct {
float border_bottom;
float border_left;
vector_uchar4 border_color;
float corner_radius;
float corner_radius_top_left;
float corner_radius_top_right;
float corner_radius_bottom_right;
float corner_radius_bottom_left;
} GPUIQuad;
typedef enum {
@ -31,7 +34,10 @@ typedef enum {
typedef struct {
vector_float2 origin;
vector_float2 size;
float corner_radius;
float corner_radius_top_left;
float corner_radius_top_right;
float corner_radius_bottom_right;
float corner_radius_bottom_left;
float sigma;
vector_uchar4 color;
} GPUIShadow;
@ -89,7 +95,10 @@ typedef struct {
float border_bottom;
float border_left;
vector_uchar4 border_color;
float corner_radius;
float corner_radius_top_left;
float corner_radius_top_right;
float corner_radius_bottom_right;
float corner_radius_bottom_left;
uint8_t grayscale;
} GPUIImage;

View File

@ -43,7 +43,10 @@ struct QuadFragmentInput {
float border_bottom;
float border_left;
float4 border_color;
float corner_radius;
float corner_radius_top_left;
float corner_radius_top_right;
float corner_radius_bottom_right;
float corner_radius_bottom_left;
uchar grayscale; // only used in image shader
};
@ -51,12 +54,27 @@ float4 quad_sdf(QuadFragmentInput input) {
float2 half_size = input.size / 2.;
float2 center = input.origin + half_size;
float2 center_to_point = input.position.xy - center;
float2 rounded_edge_to_point = abs(center_to_point) - half_size + input.corner_radius;
float distance = length(max(0., rounded_edge_to_point)) + min(0., max(rounded_edge_to_point.x, rounded_edge_to_point.y)) - input.corner_radius;
float corner_radius;
if (center_to_point.x < 0.) {
if (center_to_point.y < 0.) {
corner_radius = input.corner_radius_top_left;
} else {
corner_radius = input.corner_radius_bottom_left;
}
} else {
if (center_to_point.y < 0.) {
corner_radius = input.corner_radius_top_right;
} else {
corner_radius = input.corner_radius_bottom_right;
}
}
float2 rounded_edge_to_point = abs(center_to_point) - half_size + corner_radius;
float distance = length(max(0., rounded_edge_to_point)) + min(0., max(rounded_edge_to_point.x, rounded_edge_to_point.y)) - corner_radius;
float vertical_border = center_to_point.x <= 0. ? input.border_left : input.border_right;
float horizontal_border = center_to_point.y <= 0. ? input.border_top : input.border_bottom;
float2 inset_size = half_size - input.corner_radius - float2(vertical_border, horizontal_border);
float2 inset_size = half_size - corner_radius - float2(vertical_border, horizontal_border);
float2 point_to_inset_corner = abs(center_to_point) - inset_size;
float border_width;
if (point_to_inset_corner.x < 0. && point_to_inset_corner.y < 0.) {
@ -110,7 +128,10 @@ vertex QuadFragmentInput quad_vertex(
quad.border_bottom,
quad.border_left,
coloru_to_colorf(quad.border_color),
quad.corner_radius,
quad.corner_radius_top_left,
quad.corner_radius_top_right,
quad.corner_radius_bottom_right,
quad.corner_radius_bottom_left,
0,
};
}
@ -125,7 +146,10 @@ struct ShadowFragmentInput {
float4 position [[position]];
vector_float2 origin;
vector_float2 size;
float corner_radius;
float corner_radius_top_left;
float corner_radius_top_right;
float corner_radius_bottom_right;
float corner_radius_bottom_left;
float sigma;
vector_uchar4 color;
};
@ -148,7 +172,10 @@ vertex ShadowFragmentInput shadow_vertex(
device_position,
shadow.origin,
shadow.size,
shadow.corner_radius,
shadow.corner_radius_top_left,
shadow.corner_radius_top_right,
shadow.corner_radius_bottom_right,
shadow.corner_radius_bottom_left,
shadow.sigma,
shadow.color,
};
@ -158,10 +185,24 @@ fragment float4 shadow_fragment(
ShadowFragmentInput input [[stage_in]]
) {
float sigma = input.sigma;
float corner_radius = input.corner_radius;
float2 half_size = input.size / 2.;
float2 center = input.origin + half_size;
float2 point = input.position.xy - center;
float2 center_to_point = input.position.xy - center;
float corner_radius;
if (center_to_point.x < 0.) {
if (center_to_point.y < 0.) {
corner_radius = input.corner_radius_top_left;
} else {
corner_radius = input.corner_radius_bottom_left;
}
} else {
if (center_to_point.y < 0.) {
corner_radius = input.corner_radius_top_right;
} else {
corner_radius = input.corner_radius_bottom_right;
}
}
// The signal is only non-zero in a limited range, so don't waste samples
float low = point.y - half_size.y;
@ -252,7 +293,10 @@ vertex QuadFragmentInput image_vertex(
image.border_bottom,
image.border_left,
coloru_to_colorf(image.border_color),
image.corner_radius,
image.corner_radius_top_left,
image.corner_radius_top_right,
image.corner_radius_bottom_right,
image.corner_radius_bottom_left,
image.grayscale,
};
}
@ -266,7 +310,7 @@ fragment float4 image_fragment(
if (input.grayscale) {
float grayscale =
0.2126 * input.background_color.r +
0.7152 * input.background_color.g +
0.7152 * input.background_color.g +
0.0722 * input.background_color.b;
input.background_color = float4(grayscale, grayscale, grayscale, input.background_color.a);
}

View File

@ -1087,7 +1087,10 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
button: MouseButton::Left,
modifiers: Modifiers { ctrl: true, .. },
..
}) => return,
}) => {
window_state_borrow.synthetic_drag_counter += 1;
return;
}
_ => None,
};

View File

@ -3,8 +3,10 @@ mod mouse_region;
#[cfg(debug_assertions)]
use collections::HashSet;
use derive_more::Mul;
use schemars::JsonSchema;
use serde::Deserialize;
use serde_derive::Serialize;
use serde_json::json;
use std::{borrow::Cow, sync::Arc};
@ -65,13 +67,73 @@ pub struct Quad {
pub bounds: RectF,
pub background: Option<Color>,
pub border: Border,
pub corner_radius: f32,
pub corner_radii: CornerRadii,
}
#[derive(Default, Debug, Mul, Clone, Copy, Serialize, JsonSchema)]
pub struct CornerRadii {
pub top_left: f32,
pub top_right: f32,
pub bottom_right: f32,
pub bottom_left: f32,
}
impl<'de> Deserialize<'de> for CornerRadii {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(Deserialize)]
pub struct CornerRadiiHelper {
pub top_left: Option<f32>,
pub top_right: Option<f32>,
pub bottom_right: Option<f32>,
pub bottom_left: Option<f32>,
}
#[derive(Deserialize)]
#[serde(untagged)]
enum RadiusOrRadii {
Radius(f32),
Radii(CornerRadiiHelper),
}
let json = RadiusOrRadii::deserialize(deserializer)?;
let result = match json {
RadiusOrRadii::Radius(radius) => CornerRadii::from(radius),
RadiusOrRadii::Radii(CornerRadiiHelper {
top_left,
top_right,
bottom_right,
bottom_left,
}) => CornerRadii {
top_left: top_left.unwrap_or(0.0),
top_right: top_right.unwrap_or(0.0),
bottom_right: bottom_right.unwrap_or(0.0),
bottom_left: bottom_left.unwrap_or(0.0),
},
};
Ok(result)
}
}
impl From<f32> for CornerRadii {
fn from(radius: f32) -> Self {
Self {
top_left: radius,
top_right: radius,
bottom_right: radius,
bottom_left: radius,
}
}
}
#[derive(Debug)]
pub struct Shadow {
pub bounds: RectF,
pub corner_radius: f32,
pub corner_radii: CornerRadii,
pub sigma: f32,
pub color: Color,
}
@ -177,7 +239,7 @@ pub struct PathVertex {
pub struct Image {
pub bounds: RectF,
pub border: Border,
pub corner_radius: f32,
pub corner_radii: CornerRadii,
pub grayscale: bool,
pub data: Arc<ImageData>,
}

View File

@ -177,7 +177,7 @@ impl MouseRegion {
}
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, PartialOrd, Ord)]
pub struct MouseRegionId {
view_id: usize,
tag: TypeId,

View File

@ -14,3 +14,5 @@ syn = "1.0"
quote = "1.0"
proc-macro2 = "1.0"
[dev-dependencies]
gpui = { path = "../gpui" }

View File

@ -283,8 +283,12 @@ pub fn element_derive(input: TokenStream) -> TokenStream {
// The name of the struct/enum
let name = input.ident;
let must_implement = format_ident!("{}MustImplementRenderElement", name);
let expanded = quote! {
trait #must_implement : gpui::elements::RenderElement {}
impl #must_implement for #name {}
impl<V: gpui::View> gpui::elements::Element<V> for #name {
type LayoutState = gpui::elements::AnyElement<V>;
type PaintState = ();
@ -307,7 +311,7 @@ pub fn element_derive(input: TokenStream) -> TokenStream {
visible_bounds: gpui::geometry::rect::RectF,
element: &mut gpui::elements::AnyElement<V>,
view: &mut V,
cx: &mut gpui::ViewContext<V>,
cx: &mut gpui::PaintContext<V>,
) {
element.paint(scene, bounds.origin(), visible_bounds, view, cx);
}
@ -332,7 +336,7 @@ pub fn element_derive(input: TokenStream) -> TokenStream {
_: &(),
view: &V,
cx: &gpui::ViewContext<V>,
) -> serde_json::Value {
) -> gpui::serde_json::Value {
element.debug(view, cx)
}
}

View File

@ -0,0 +1,14 @@
use gpui::{elements::RenderElement, View, ViewContext};
use gpui_macros::Element;
#[test]
fn test_derive_render_element() {
#[derive(Element)]
struct TestElement {}
impl RenderElement for TestElement {
fn render<V: View>(&mut self, _: &mut V, _: &mut ViewContext<V>) -> gpui::AnyElement<V> {
unimplemented!()
}
}
}

View File

@ -1438,7 +1438,7 @@ impl View for ProjectSearchBar {
.with_cursor_style(CursorStyle::PointingHand)
.with_tooltip::<Self>(
0,
"Toggle filters".into(),
"Toggle filters",
Some(Box::new(ToggleFilters)),
tooltip_style,
cx,

View File

@ -65,8 +65,8 @@ pub(super) fn render_nav_button<V: View>(
MouseEventHandler::<NavButton, _>::new(direction as usize, cx, |state, cx| {
let theme = theme::current(cx);
let mut style = theme.search.nav_button.style_for(state).clone();
let button_side_width = style.container.corner_radius;
style.container.corner_radius = 0.;
let button_side_width = style.container.corner_radii.top_left;
style.container.corner_radii = (0.).into();
let label = Label::new(icon, style.label.clone())
.contained()
.with_style(style.container.clone());
@ -137,8 +137,8 @@ pub(crate) fn render_search_mode_button<V: View>(
.in_state(is_active)
.style_for(state)
.clone();
let side_width = style.container.corner_radius;
style.container.corner_radius = 0.;
let side_width = style.container.corner_radii.top_left;
style.container.corner_radii = (0.).into();
if mode.button_side().is_some() {
style.container.border.left = mode.border_left();
style.container.border.right = mode.border_right();

View File

@ -153,7 +153,7 @@ impl LayoutRect {
bounds: RectF::new(position, size),
background: Some(self.color),
border: Default::default(),
corner_radius: 0.,
corner_radii: Default::default(),
})
}
}
@ -763,7 +763,7 @@ impl Element<TerminalView> for TerminalElement {
bounds: RectF::new(bounds.origin(), bounds.size()),
background: Some(layout.background_color),
border: Default::default(),
corner_radius: 0.,
corner_radii: Default::default(),
});
for rect in &layout.rects {

View File

@ -72,10 +72,7 @@ impl TerminalPanel {
0,
"icons/plus_12.svg",
false,
Some((
"New Terminal".into(),
Some(Box::new(workspace::NewTerminal)),
)),
Some(("New Terminal", Some(Box::new(workspace::NewTerminal)))),
cx,
move |_, cx| {
let this = this.clone();

View File

@ -409,10 +409,10 @@ impl Pane {
let tooltip_label;
if pane.is_zoomed() {
icon_path = "icons/minimize_8.svg";
tooltip_label = "Zoom In".into();
tooltip_label = "Zoom In";
} else {
icon_path = "icons/maximize_8.svg";
tooltip_label = "Zoom In".into();
tooltip_label = "Zoom In";
}
Pane::render_tab_bar_button(
@ -1503,7 +1503,7 @@ impl Pane {
bounds: square,
background: Some(color),
border: Default::default(),
corner_radius: diameter / 2.,
corner_radii: (diameter / 2.).into(),
});
}
})
@ -1583,7 +1583,7 @@ impl Pane {
index: usize,
icon: &'static str,
is_active: bool,
tooltip: Option<(String, Option<Box<dyn Action>>)>,
tooltip: Option<(&'static str, Option<Box<dyn Action>>)>,
cx: &mut ViewContext<Pane>,
on_click: F1,
on_down: F2,

View File

@ -61,7 +61,7 @@ where
bounds: overlay_region,
background: Some(overlay_color(cx)),
border: Default::default(),
corner_radius: 0.,
corner_radii: Default::default(),
});
});
}

View File

@ -3614,7 +3614,7 @@ fn notify_of_new_dock(workspace: &WeakViewHandle<Workspace>, cx: &mut AsyncAppCo
bounds,
background: Some(code_span_background_color),
border: Default::default(),
corner_radius: 2.0,
corner_radii: (2.0).into(),
})
})
.into_any()
@ -4067,10 +4067,10 @@ pub fn restart(_: &Restart, cx: &mut AppContext) {
// If the user cancels any save prompt, then keep the app open.
for window in workspace_windows {
if let Some(close) = window.update_root(&mut cx, |workspace, cx| {
if let Some(should_close) = window.update_root(&mut cx, |workspace, cx| {
workspace.prepare_to_close(true, cx)
}) {
if !close.await? {
if !should_close.await? {
return Ok(());
}
}

View File

@ -3,7 +3,7 @@ authors = ["Nathan Sobo <nathansobo@gmail.com>"]
description = "The fast, collaborative code editor."
edition = "2021"
name = "zed"
version = "0.99.0"
version = "0.100.0"
publish = false
[lib]

View File

@ -654,6 +654,10 @@ fn load_embedded_fonts(app: &App) {
let embedded_fonts = Mutex::new(Vec::new());
smol::block_on(app.background().scoped(|scope| {
for font_path in &font_paths {
if !font_path.ends_with(".ttf") {
continue;
}
scope.spawn(async {
let font_path = &*font_path;
let font_bytes = Assets.load(font_path).unwrap().to_vec();

View File

@ -433,10 +433,10 @@ fn quit(_: &Quit, cx: &mut gpui::AppContext) {
// If the user cancels any save prompt, then keep the app open.
for window in workspace_windows {
if let Some(close) = window.update_root(&mut cx, |workspace, cx| {
workspace.prepare_to_close(false, cx)
if let Some(should_close) = window.update_root(&mut cx, |workspace, cx| {
workspace.prepare_to_close(true, cx)
}) {
if close.await? {
if !should_close.await? {
return Ok(());
}
}
@ -2327,6 +2327,11 @@ mod tests {
.unwrap()
.to_vec()
.into(),
Assets
.load("fonts/plex/IBMPlexSans-Regular.ttf")
.unwrap()
.to_vec()
.into(),
])
.unwrap();
let themes = ThemeRegistry::new(Assets, cx.font_cache().clone());

View File

@ -3,6 +3,7 @@ export * from "./theme"
export { chroma }
export const font_families = {
ui_sans: "IBM Plex Sans",
sans: "Zed Sans",
mono: "Zed Mono",
}