T12 review fixes: upload shape and formats, accent dot, test gating, value wrap, gallery clamp, Code 128 set switches, float sums, dense QR
This commit is contained in:
12 files changed
+131
-32
No files matched your search
@@ -92,6 +92,11 @@ impl Avatar {
|
||||
self
|
||||
}
|
||||
|
||||
/// Whether it is a rounded square rather than a circle.
|
||||
pub(crate) fn is_square(&self) -> bool {
|
||||
self.square
|
||||
}
|
||||
|
||||
/// A rounded square, as teams and apps take.
|
||||
pub fn square(mut self) -> Self {
|
||||
self.square = true;
|
||||
|
||||
@@ -102,7 +102,10 @@ impl RenderOnce for Badge {
|
||||
fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let theme = cx.theme();
|
||||
let (fill, text) = self.tone.colors(&theme.colors);
|
||||
let dot = self.tone.dot(&theme.colors);
|
||||
let dot = match self.tone {
|
||||
Tone::Accent => theme.colors.on_accent,
|
||||
tone => tone.dot(&theme.colors),
|
||||
};
|
||||
div()
|
||||
.flex()
|
||||
.flex_none()
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use barcoders::sym::{code128::Code128, ean13::EAN13};
|
||||
use gpui::{
|
||||
App, Bounds, Div, IntoElement, ParentElement, Pixels, Refineable, RenderOnce, SharedString,
|
||||
@@ -9,9 +11,24 @@ pub use qrcode::types::QrError;
|
||||
|
||||
use crate::theme::{ActiveTheme, Radius, TextSize};
|
||||
|
||||
/// Whether a QR code has already said its tile is too small to draw it sharply.
|
||||
static SMALL: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
/// The quiet margin around a QR code, in modules, as the standard asks.
|
||||
const QUIET: usize = 4;
|
||||
|
||||
/// A module's side: whole pixels for crisp edges, or the exact share when whole pixels would floor to nothing.
|
||||
fn module(side: Pixels, span: f32) -> Pixels {
|
||||
let fit = side / span;
|
||||
if f32::from(fit) >= 1.0 {
|
||||
return fit.floor();
|
||||
}
|
||||
if !SMALL.swap(true, Ordering::Relaxed) {
|
||||
log::warn!("qr code: {span} modules share {fit:?} each; give the tile more room");
|
||||
}
|
||||
fit
|
||||
}
|
||||
|
||||
/// The dark runs in a row of modules, as start and length.
|
||||
fn runs(row: &[bool]) -> Vec<(usize, usize)> {
|
||||
let mut runs = Vec::new();
|
||||
@@ -82,9 +99,16 @@ impl RenderOnce for QrCode {
|
||||
let theme = cx.theme();
|
||||
let (paper, ink) = (theme.colors.paper, theme.colors.ink);
|
||||
let rows = self.rows;
|
||||
let span = (rows.len() + QUIET * 2) as f32;
|
||||
let need = theme.barcode_module() * span;
|
||||
let side = if need.0 > theme.qr_code().0 {
|
||||
need
|
||||
} else {
|
||||
theme.qr_code()
|
||||
};
|
||||
let mut tile = div()
|
||||
.flex_none()
|
||||
.size(theme.qr_code())
|
||||
.size(side)
|
||||
.rounded(theme.radius(Radius::Sm))
|
||||
.bg(paper);
|
||||
tile.style().refine(self.base.style());
|
||||
@@ -92,8 +116,7 @@ impl RenderOnce for QrCode {
|
||||
canvas(
|
||||
|_, _, _| {},
|
||||
move |bounds, _, window, _| {
|
||||
let span = (rows.len() + QUIET * 2) as f32;
|
||||
let module = (bounds.size.width.min(bounds.size.height) / span).floor();
|
||||
let module = module(bounds.size.width.min(bounds.size.height), span);
|
||||
let inset = (rows.len() as f32 * module) / 2.0;
|
||||
let origin = bounds.center() - point(inset, inset);
|
||||
paint_runs(&rows, origin, module, module, ink, window);
|
||||
@@ -139,6 +162,9 @@ impl Barcode {
|
||||
|
||||
/// Code 128 in its set B: letters, digits and printable ASCII marks.
|
||||
pub fn code128(text: &str) -> Result<Self, BarcodeError> {
|
||||
if !text.chars().all(|letter| (' '..='~').contains(&letter)) {
|
||||
return Err(BarcodeError::Character);
|
||||
}
|
||||
let modules = Code128::new(format!("\u{0181}{text}"))?.encode();
|
||||
Ok(Self::new(modules, text.to_string()))
|
||||
}
|
||||
@@ -207,7 +233,9 @@ impl RenderOnce for Barcode {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Barcode, BarcodeError, check_digit, runs};
|
||||
use gpui::px;
|
||||
|
||||
use super::{Barcode, BarcodeError, check_digit, module, runs};
|
||||
|
||||
#[test]
|
||||
fn dark_modules_merge_into_runs() {
|
||||
@@ -232,6 +260,23 @@ mod tests {
|
||||
Barcode::code128("caf\u{e9}"),
|
||||
Err(BarcodeError::Character)
|
||||
));
|
||||
assert!(
|
||||
matches!(Barcode::code128("\u{c0}123"), Err(BarcodeError::Character)),
|
||||
"barcoders reads \u{c0} as a switch to set A"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_dense_qr_keeps_modules_it_can_draw() {
|
||||
assert_eq!(
|
||||
module(px(254.0), 25.0),
|
||||
px(10.0),
|
||||
"whole pixels when they fit"
|
||||
);
|
||||
assert!(
|
||||
module(px(100.0), 185.0) > px(0.0),
|
||||
"a share under a pixel stays drawn"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -9,6 +9,11 @@ use crate::{
|
||||
primitives::{FocusRing, Image, tab_stop},
|
||||
};
|
||||
|
||||
/// The open picture if it is still there, the last when it went past the end, none when all are gone.
|
||||
fn clamp(open: Option<usize>, count: usize) -> Option<usize> {
|
||||
open.and_then(|at| count.checked_sub(1).map(|last| at.min(last)))
|
||||
}
|
||||
|
||||
/// Pictures in square tiles. A press, or Enter on a focused tile, opens the lightbox at that picture.
|
||||
#[derive(IntoElement)]
|
||||
pub struct Gallery {
|
||||
@@ -87,7 +92,15 @@ impl RenderOnce for Gallery {
|
||||
),
|
||||
);
|
||||
}
|
||||
let lightbox = (*open.read(cx)).map(|at| {
|
||||
let at = clamp(*open.read(cx), self.slides.len());
|
||||
if at != *open.read(cx) {
|
||||
log::info!(
|
||||
"gallery {:?}: pictures went away; open is now {at:?}",
|
||||
self.id
|
||||
);
|
||||
open.update(cx, |open, _| *open = at);
|
||||
}
|
||||
let lightbox = at.map(|at| {
|
||||
let (close, step) = (show(None), open.clone());
|
||||
Lightbox::new(
|
||||
(self.id.clone(), "lightbox"),
|
||||
@@ -110,3 +123,16 @@ impl RenderOnce for Gallery {
|
||||
.children(lightbox)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::clamp;
|
||||
|
||||
#[test]
|
||||
fn an_open_picture_clamps_when_pictures_go_away() {
|
||||
assert_eq!(clamp(Some(1), 3), Some(1));
|
||||
assert_eq!(clamp(Some(2), 1), Some(0), "past the end opens the last");
|
||||
assert_eq!(clamp(Some(0), 0), None, "no pictures closes it");
|
||||
assert_eq!(clamp(None, 3), None);
|
||||
}
|
||||
}
|
||||
@@ -309,16 +309,24 @@ impl UsageBar {
|
||||
}
|
||||
}
|
||||
|
||||
impl UsageBar {
|
||||
/// What the parts use and whether they fill the total, within float error.
|
||||
fn used(&self) -> (f64, bool) {
|
||||
let used: f64 = self.parts.iter().map(|(_, amount)| amount).sum();
|
||||
assert!(
|
||||
used <= self.total * (1.0 + 1e-9),
|
||||
"the parts ({used}) pass the total ({})",
|
||||
self.total
|
||||
);
|
||||
(used, used >= self.total * (1.0 - 1e-9))
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for UsageBar {
|
||||
fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let theme = cx.theme();
|
||||
let colors = &theme.colors;
|
||||
let used: f64 = self.parts.iter().map(|(_, amount)| amount).sum();
|
||||
assert!(
|
||||
used <= self.total,
|
||||
"the parts ({used}) pass the total ({})",
|
||||
self.total
|
||||
);
|
||||
let (used, full) = self.used();
|
||||
assert!(
|
||||
self.parts.len() <= colors.chart.len(),
|
||||
"a usage bar has colors for {} parts",
|
||||
@@ -339,9 +347,9 @@ impl RenderOnce for UsageBar {
|
||||
grown(amount / total)
|
||||
.bg(colors.chart[ix])
|
||||
.when(ix == 0, |part| part.rounded_l_full())
|
||||
.when(ix == last && used == total, |part| part.rounded_r_full())
|
||||
.when(ix == last && full, |part| part.rounded_r_full())
|
||||
}))
|
||||
.when(used < total, |bar| {
|
||||
.when(!full, |bar| {
|
||||
bar.child(
|
||||
grown((total - used) / total)
|
||||
.bg(colors.border)
|
||||
@@ -382,7 +390,13 @@ impl RenderOnce for UsageBar {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Level;
|
||||
use super::{Level, UsageBar};
|
||||
|
||||
#[test]
|
||||
fn parts_that_sum_to_the_total_fill_it_despite_float_error() {
|
||||
assert!(UsageBar::new(0.3).part("a", 0.1).part("b", 0.2).used().1);
|
||||
assert!(!UsageBar::new(1.0).part("a", 0.5).used().1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_share_turns_amber_then_red() {
|
||||
|
||||
@@ -12,7 +12,7 @@ mod records;
|
||||
mod spark;
|
||||
mod stars;
|
||||
mod stat;
|
||||
#[cfg(test)]
|
||||
#[cfg(all(test, feature = "test-support"))]
|
||||
mod tests;
|
||||
mod timeline;
|
||||
mod watermark;
|
||||
|
||||
@@ -75,7 +75,7 @@ impl RenderOnce for DescriptionList {
|
||||
.flex_1()
|
||||
.min_w_0()
|
||||
.text_color(colors.fg)
|
||||
.child(value),
|
||||
.child(div().min_w_0().child(value)),
|
||||
)
|
||||
}),
|
||||
)
|
||||
|
||||
Reference in new issue
Block a user