T16 Finance: market charts with kinds, volume, profile, studies, drawings, compare, alerts and sync (T16a–b); quotes, book, tape and DOM (T16c); order entry, blotter, risk, options and payoff (T16d); markets, calendars, portfolio and wallet (T16e); typography::Ellipsis ends lines that gpui's truncate clipped; chart notes stack and ticks label round values

This commit is contained in:
ZacharyZhang-NY committed 2026-09-25 23:30:04 -04:00
1 parent efde9d64b0
commit 8d43b7a880
76 files changed
+10311 -173

No files matched your search

+39 -15
View File
@@ -1,6 +1,6 @@
use gpui::{
App, Div, IntoElement, ParentElement, Pixels, Rems, SharedString, Styled, Window, div,
prelude::*,
prelude::*, relative,
};
use super::{
@@ -9,8 +9,8 @@ use super::{
scale::Linear,
};
use crate::{
theme::{ActiveTheme, TextSize},
typography::tabular,
theme::{ActiveTheme, Radius, TextSize},
typography::{LEADING, tabular, text_width},
};
/// A label centered under `x`, below the frame.
@@ -99,14 +99,18 @@ pub(crate) fn marks(
.collect();
let along = if plot.horizontal { rect.h } else { rect.w };
let room = f32::from(sizes.label.to_pixels(rem));
let named: Vec<&(f32, SharedString)> = geometry
.labels
.iter()
.filter(|(_, label)| !label.is_empty())
.collect();
let every = if plot.horizontal {
1
} else {
((geometry.labels.len() as f32 * room / along.max(1.0)).ceil() as usize).max(1)
((named.len() as f32 * room / along.max(1.0)).ceil() as usize).max(1)
};
labels.extend(
geometry
.labels
named
.iter()
.enumerate()
.filter(|(ix, _)| ix % every == 0)
@@ -148,7 +152,7 @@ pub(crate) fn marks(
.child(text(label.clone()).pb_0p5())
}
}));
let notes: Vec<(f32, SharedString)> = plot
let mut notes: Vec<(f32, SharedString)> = plot
.notes
.iter()
.filter(|(ix, _)| !plot.horizontal && (span.0..=span.1).contains(ix))
@@ -159,14 +163,34 @@ pub(crate) fn marks(
.map(|(at, _)| (*at, label.clone()))
})
.collect();
labels.extend(notes.iter().map(|(at, label)| {
div()
.absolute()
.left(Pixels::from(*at))
.top(Pixels::from(rect.y))
.pl_1p5()
.child(text(label.clone()))
}));
notes.sort_by(|a, b| a.0.total_cmp(&b.0));
let size = theme.text_size(TextSize::Xs).to_pixels(rem);
let mut ends: Vec<f32> = Vec::new();
for (at, label) in &notes {
let row = ends.iter().position(|end| end <= at).unwrap_or(ends.len());
let end = at + f32::from(text_width(label, size, window) + size);
if row == ends.len() {
ends.push(end);
} else {
ends[row] = end;
}
labels.push(
div()
.absolute()
.left(Pixels::from(*at))
.top(Pixels::from(
rect.y + f32::from(size) * LEADING * row as f32,
))
.pl_1p5()
.line_height(relative(LEADING))
.child(
text(label.clone())
.px_1()
.rounded(theme.radius(Radius::Sm))
.bg(colors.bg),
),
);
}
Marks {
labels,
rules: rules.iter().map(|(at, _)| *at).collect(),
+8
View File
@@ -45,3 +45,11 @@ pub use series::{Points, Series};
pub use sunburst::{Slice, Sunburst};
pub use targets::{Bullet, BulletChart, ProgressChart};
pub use tiles::{HeatmapChart, Treemap};
pub(crate) use axes::anchored;
pub(crate) use geometry::{Geometry, Ink, Line, Rect};
pub(crate) use layout::squarify;
pub(crate) use paint::{at, finish, measure, place, ring, tint};
pub(crate) use plot::{Layout, Pick, Plot, Scene, Tips, plot};
pub(crate) use scale::{Linear, compact, nice, nice_step};
pub(crate) use tiles::{Tiles, tracked};
+2 -2
View File
@@ -59,8 +59,8 @@ impl Ink {
pub(crate) fn color(self, colors: &Palette) -> Hsla {
match self {
Ink::Series(ix) => tint(colors, ix),
Ink::Rise => tint(colors, 1),
Ink::Fall => tint(colors, 3),
Ink::Rise => colors.success,
Ink::Fall => colors.danger,
Ink::Rule => colors.fg_subtle,
Ink::Strong => colors.fg,
}
+12
View File
@@ -14,6 +14,17 @@ impl Linear {
Self { domain, range }
}
/// The value at pixel `at`; a flat range reads the middle of the domain.
pub(crate) fn value(&self, at: f32) -> f64 {
let span = self.range.1 - self.range.0;
let share = if span == 0.0 {
0.5
} else {
(f64::from(at) - f64::from(self.range.0)) / f64::from(span)
};
self.domain.0 + (self.domain.1 - self.domain.0) * share
}
/// Where `value` lands; a flat domain lands in the middle.
pub(crate) fn at(&self, value: f64) -> f32 {
let span = self.domain.1 - self.domain.0;
@@ -120,6 +131,7 @@ mod tests {
let scale = Linear::new((0.0, 50.0), (100.0, 0.0));
assert_eq!(scale.at(25.0), 50.0);
assert_eq!(scale.at(50.0), 0.0);
assert_eq!(scale.value(20.0), 40.0, "and back");
assert_eq!(
Linear::new((3.0, 3.0), (0.0, 10.0)).at(3.0),
5.0,