chart: Draw dashed lines using gpui PathBuilder (#927)

https://github.com/zed-industries/zed/pull/31678
This commit is contained in:
Floyd Wang 2025-06-09 11:55:32 +08:00 committed by GitHub
parent 01197769df
commit 44ec74f1fb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 36 additions and 83 deletions

31
Cargo.lock generated
View file

@ -623,7 +623,7 @@ dependencies = [
"bitflags 2.9.1",
"cexpr",
"clang-sys",
"itertools 0.12.1",
"itertools 0.10.5",
"lazy_static",
"lazycell",
"log",
@ -646,7 +646,7 @@ dependencies = [
"bitflags 2.9.1",
"cexpr",
"clang-sys",
"itertools 0.13.0",
"itertools 0.10.5",
"log",
"prettyplease",
"proc-macro2",
@ -1226,7 +1226,7 @@ dependencies = [
[[package]]
name = "collections"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed.git#06a199da4d8a147c7867f4c82ffafed4974bea24"
source = "git+https://github.com/zed-industries/zed.git#b15aef4310e86aa31c2ceab74184ec7e5627a2c5"
dependencies = [
"indexmap 2.7.1",
"rustc-hash 2.1.0",
@ -1739,7 +1739,7 @@ dependencies = [
[[package]]
name = "derive_refineable"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed.git#06a199da4d8a147c7867f4c82ffafed4974bea24"
source = "git+https://github.com/zed-industries/zed.git#b15aef4310e86aa31c2ceab74184ec7e5627a2c5"
dependencies = [
"proc-macro2",
"quote",
@ -2777,7 +2777,7 @@ dependencies = [
[[package]]
name = "gpui"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed.git#06a199da4d8a147c7867f4c82ffafed4974bea24"
source = "git+https://github.com/zed-industries/zed.git#b15aef4310e86aa31c2ceab74184ec7e5627a2c5"
dependencies = [
"anyhow",
"as-raw-xcb-connection",
@ -2881,7 +2881,6 @@ dependencies = [
"image",
"indoc",
"itertools 0.13.0",
"lyon",
"markdown",
"markup5ever_rcdom",
"minify-html",
@ -2946,7 +2945,7 @@ dependencies = [
[[package]]
name = "gpui_macros"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed.git#06a199da4d8a147c7867f4c82ffafed4974bea24"
source = "git+https://github.com/zed-industries/zed.git#b15aef4310e86aa31c2ceab74184ec7e5627a2c5"
dependencies = [
"heck 0.5.0",
"proc-macro2",
@ -3216,7 +3215,7 @@ dependencies = [
[[package]]
name = "http_client"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed.git#06a199da4d8a147c7867f4c82ffafed4974bea24"
source = "git+https://github.com/zed-industries/zed.git#b15aef4310e86aa31c2ceab74184ec7e5627a2c5"
dependencies = [
"anyhow",
"bytes",
@ -3233,7 +3232,7 @@ dependencies = [
[[package]]
name = "http_client_tls"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed.git#06a199da4d8a147c7867f4c82ffafed4974bea24"
source = "git+https://github.com/zed-industries/zed.git#b15aef4310e86aa31c2ceab74184ec7e5627a2c5"
dependencies = [
"rustls",
"rustls-platform-verifier",
@ -4003,7 +4002,7 @@ dependencies = [
[[package]]
name = "media"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed.git#06a199da4d8a147c7867f4c82ffafed4974bea24"
source = "git+https://github.com/zed-industries/zed.git#b15aef4310e86aa31c2ceab74184ec7e5627a2c5"
dependencies = [
"anyhow",
"bindgen 0.71.1",
@ -4396,7 +4395,7 @@ version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
dependencies = [
"proc-macro-crate 3.2.0",
"proc-macro-crate 1.3.1",
"proc-macro2",
"quote",
"syn 2.0.101",
@ -5720,7 +5719,7 @@ dependencies = [
[[package]]
name = "refineable"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed.git#06a199da4d8a147c7867f4c82ffafed4974bea24"
source = "git+https://github.com/zed-industries/zed.git#b15aef4310e86aa31c2ceab74184ec7e5627a2c5"
dependencies = [
"derive_refineable",
"workspace-hack",
@ -5830,7 +5829,7 @@ dependencies = [
[[package]]
name = "reqwest_client"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed.git#06a199da4d8a147c7867f4c82ffafed4974bea24"
source = "git+https://github.com/zed-industries/zed.git#b15aef4310e86aa31c2ceab74184ec7e5627a2c5"
dependencies = [
"anyhow",
"bytes",
@ -6379,7 +6378,7 @@ checksum = "d369a96f978623eb3dc28807c4852d6cc617fed53da5d3c400feff1ef34a714a"
[[package]]
name = "semantic_version"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed.git#06a199da4d8a147c7867f4c82ffafed4974bea24"
source = "git+https://github.com/zed-industries/zed.git#b15aef4310e86aa31c2ceab74184ec7e5627a2c5"
dependencies = [
"anyhow",
"serde",
@ -6876,7 +6875,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "sum_tree"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed.git#06a199da4d8a147c7867f4c82ffafed4974bea24"
source = "git+https://github.com/zed-industries/zed.git#b15aef4310e86aa31c2ceab74184ec7e5627a2c5"
dependencies = [
"arrayvec",
"log",
@ -8043,7 +8042,7 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "util"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed.git#06a199da4d8a147c7867f4c82ffafed4974bea24"
source = "git+https://github.com/zed-industries/zed.git#b15aef4310e86aa31c2ceab74184ec7e5627a2c5"
dependencies = [
"anyhow",
"async-fs 2.1.2",

View file

@ -19,7 +19,7 @@ UI components for building fantastic desktop applications using [GPUI](https://g
Here is the first application: [Longbridge Pro](https://longbridge.com/desktop), built using GPUI Component.
<img width="1763" alt="Image" src="https://i.postimg.cc/qBh6GCvp/Longbridge-Pro.png" />
<img width="1763" alt="Image" src="https://wp-cdn.4ce.cn/v2/hZRGaJm.png" />
We built multi-theme support in the application. This feature is not included in GPUI Component itself, but is based on the `Theme` feature, so it's easy to implement.

View file

@ -25,7 +25,6 @@ enum-iterator = "2.1.0"
futures-util = "0.3.31"
image = "0.25.1"
itertools = "0.13.0"
lyon = "1.0"
once_cell = "1.19.0"
paste = "1"
regex = "1"

View file

@ -136,7 +136,7 @@ where
Grid::new()
.y((0..=3).map(|i| height * i as f64 / 4.0).collect())
.stroke(cx.theme().border)
.dash_array([px(4.), px(2.)])
.dash_array(&[px(4.), px(2.)])
.paint(&bounds, window);
// Draw area

View file

@ -135,7 +135,7 @@ where
Grid::new()
.y((0..=3).map(|i| height * i as f64 / 4.0).collect())
.stroke(cx.theme().border)
.dash_array([px(4.), px(2.)])
.dash_array(&[px(4.), px(2.)])
.paint(&bounds, window);
// Draw bars

View file

@ -128,7 +128,7 @@ where
Grid::new()
.y((0..=3).map(|i| height * i as f64 / 4.0).collect())
.stroke(cx.theme().border)
.dash_array([px(4.), px(2.)])
.dash_array(&[px(4.), px(2.)])
.paint(&bounds, window);
// Draw line

View file

@ -1,12 +1,12 @@
use gpui::{px, Bounds, Hsla, PathBuilder, Pixels, Point, Window};
use super::{dash_line, origin_point};
use super::origin_point;
pub struct Grid {
x: Vec<Pixels>,
y: Vec<Pixels>,
stroke: Hsla,
dash_array: Option<[Pixels; 2]>,
dash_array: Option<Vec<Pixels>>,
}
impl Grid {
@ -39,8 +39,8 @@ impl Grid {
}
/// Set the dash array of the Grid.
pub fn dash_array(mut self, dash_array: [Pixels; 2]) -> Self {
self.dash_array = Some(dash_array);
pub fn dash_array(mut self, dash_array: &[Pixels]) -> Self {
self.dash_array = Some(dash_array.to_vec());
self
}
@ -78,20 +78,17 @@ impl Grid {
pub fn paint(&self, bounds: &Bounds<Pixels>, window: &mut Window) {
let points = self.points(bounds);
if let Some(dash_array) = self.dash_array {
for (start, end) in points {
if let Some(line) = dash_line(start, end, dash_array) {
window.paint_path(line, self.stroke);
}
for (start, end) in points {
let mut builder = PathBuilder::stroke(px(1.));
if let Some(dash_array) = &self.dash_array {
builder = builder.dash_array(&dash_array);
}
} else {
for (start, end) in points {
let mut builder = PathBuilder::stroke(px(1.));
builder.move_to(start);
builder.line_to(end);
if let Ok(line) = builder.build() {
window.paint_path(line, self.stroke);
}
builder.move_to(start);
builder.line_to(end);
if let Ok(line) = builder.build() {
window.paint_path(line, self.stroke);
}
}
}

View file

@ -9,10 +9,7 @@ pub use gpui_component_macros::IntoPlot;
use std::{fmt::Debug, ops::Add};
use gpui::{
point, px, App, Bounds, IntoElement, Path, PathBuilder, PathStyle, Pixels, Point,
StrokeOptions, Window,
};
use gpui::{point, px, App, Bounds, IntoElement, Path, PathBuilder, Pixels, Point, Window};
pub use axis::{Axis, AxisText, AXIS_GAP};
pub use grid::Grid;
@ -36,45 +33,6 @@ where
point(x, y) + origin
}
// TODO: Move into gpui
//
// https://github.com/zed-industries/zed/pull/31678
pub fn dash_line<T>(start: Point<T>, end: Point<T>, dash_array: [T; 2]) -> Option<Path<Pixels>>
where
T: Default + Clone + Copy + Debug + PartialEq + Add<Output = T> + Into<f64>,
{
let mut path = lyon::path::Path::builder();
path.begin(lyon::geom::point(
start.x.into() as f32,
start.y.into() as f32,
));
path.line_to(lyon::geom::point(end.x.into() as f32, end.y.into() as f32));
path.end(false);
let path = path.build();
// Make path dashable.
let measure = lyon::algorithms::measure::PathMeasurements::from_path(&path, 0.01);
let mut sampler =
measure.create_sampler(&path, lyon::algorithms::measure::SampleType::Normalized);
let mut dashes = lyon::path::Path::builder();
let length = sampler.length();
let dash_length = dash_array[0].into() as f32;
let gap_length = dash_array[1].into() as f32;
let pattern_length = dash_length + gap_length;
let num_patterns = (length / pattern_length).ceil() as usize;
for i in 0..num_patterns {
let start = i as f32 * pattern_length / length;
let end = (i as f32 * pattern_length + dash_length) / length;
sampler.split_range(start..end.min(1.), &mut dashes);
}
let mut path: PathBuilder = dashes.into();
path = path.with_style(PathStyle::Stroke(
StrokeOptions::default().with_line_width(1.),
));
path.build().ok()
}
pub fn polygon<T>(points: &[Point<T>], bounds: &Bounds<Pixels>) -> Option<Path<Pixels>>
where
T: Default + Clone + Copy + Debug + Into<f64> + PartialEq,

View file

@ -676,7 +676,7 @@ impl Element for Scrollbar {
let margin_end = state.margin_end;
let is_vertical = axis.is_vertical();
window.set_cursor_style(CursorStyle::default(), Some(&state.bar_hitbox));
window.set_cursor_style(CursorStyle::default(), &state.bar_hitbox);
window.paint_layer(hitbox_bounds, |cx| {
cx.paint_quad(fill(state.bounds, state.bg));

View file

@ -107,7 +107,7 @@ impl RenderOnce for WindowBorder {
CursorStyle::ResizeUpRightDownLeft
}
},
Some(&hitbox),
&hitbox,
);
},
)