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:
76 files changed
+10311
-173
No files matched your search
+39
-15
@@ -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 ¬es {
|
||||
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(),
|
||||
|
||||
@@ -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
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in new issue
Block a user