diff --git a/src/data_display/records.rs b/src/data_display/records.rs index df1c7a9..17e3dab 100644 --- a/src/data_display/records.rs +++ b/src/data_display/records.rs @@ -1,6 +1,6 @@ use gpui::{ AnyElement, App, ElementId, FontWeight, InteractiveElement, IntoElement, ParentElement, - RenderOnce, SharedString, StatefulInteractiveElement, Styled, Window, div, prelude::*, + RenderOnce, Role, SharedString, StatefulInteractiveElement, Styled, Window, div, prelude::*, }; use crate::{ @@ -9,10 +9,11 @@ use crate::{ theme::{ActiveTheme, ControlSize, TextSize}, }; -/// Labels and their values, one pair a row with the labels in a quiet column, or stacked with each label above its value. A value short of room drops under its label. +/// Labels and their values, side by side or stacked. #[derive(IntoElement, Default)] pub struct DescriptionList { - items: Vec<(SharedString, AnyElement)>, + /// Label, value, and the value as words when text. + items: Vec<(SharedString, AnyElement, Option)>, stacked: bool, lined: bool, } @@ -22,9 +23,18 @@ impl DescriptionList { Self::default() } - /// A label, and its value: text, or any element such as a badge. + /// A label and its value, any element. pub fn item(mut self, label: impl Into, value: impl IntoElement) -> Self { - self.items.push((label.into(), value.into_any_element())); + self.items + .push((label.into(), value.into_any_element(), None)); + self + } + + /// A label and its text, both spoken. + pub fn text(mut self, label: impl Into, value: impl Into) -> Self { + let value = value.into(); + self.items + .push((label.into(), value.clone().into_any_element(), Some(value))); self } @@ -54,9 +64,16 @@ impl RenderOnce for DescriptionList { self.items .into_iter() .enumerate() - .map(|(ix, (label, value))| { + .map(|(ix, (label, value, spoken))| { + let said = + spoken.map(|spoken| SharedString::from(format!("{label}: {spoken}"))); let label = div().text_color(colors.fg_muted).child(label); div() + .id(ElementId::Name( + said.clone() + .unwrap_or_else(|| format!("description-{ix}").into()), + )) + .when_some(said, |row, said| row.role(Role::Label).aria_label(said)) .flex() .when(stacked, |row| row.flex_col().gap_0p5()) .when(!stacked, |row| { @@ -114,7 +131,7 @@ impl PropertyGroup { } } -/// An inspector's table: groups that fold, each row a name and its editor. The owner supplies the editors and keeps the values. +/// An inspector's table of folding groups of named editors. #[derive(IntoElement)] pub struct PropertyGrid { id: ElementId, diff --git a/src/git/blame.rs b/src/git/blame.rs index e9df5ec..aeba889 100644 --- a/src/git/blame.rs +++ b/src/git/blame.rs @@ -14,7 +14,7 @@ use crate::{ typography::{Ellipsis, LEADING}, }; -/// Who last changed lines: the commit, its author, when and its subject, and its age from 0, newest, to 1, oldest. +/// Who last changed lines; age 0 newest, 1 oldest. #[derive(Clone, Debug, PartialEq)] pub struct Blame { pub commit: SharedString, @@ -24,7 +24,7 @@ pub struct Blame { pub age: f32, } -/// Consecutive lines from one change, as the change's index and the lines. +/// Consecutive lines from one change, with that change's index. pub(crate) fn runs(owners: &[Option]) -> Vec<(Option, Range)> { let mut out: Vec<(Option, Range)> = Vec::new(); for (line, owner) in owners.iter().enumerate() { @@ -36,7 +36,7 @@ pub(crate) fn runs(owners: &[Option]) -> Vec<(Option, Range out } -/// Who last changed a line, set after its code in quiet words: the author, when, and the subject. +/// Who last changed a line, quietly after its code. #[derive(IntoElement)] pub struct GitBlameAnnotation { blame: Blame, @@ -65,7 +65,7 @@ impl RenderOnce for GitBlameAnnotation { type OnCommit = Rc; -/// Code with who last changed each run of lines beside it: author, when and subject on a run's first line, a bar that fades with age. The current line shows its annotation; a press on a run picks its commit. +/// Code beside each run's last author; a press picks it. #[derive(IntoElement)] pub struct BlameView { id: ElementId, @@ -77,7 +77,7 @@ pub struct BlameView { } impl BlameView { - /// `owners` holds, for each line of `code`, its entry in `blames`. + /// `owners`: each line's entry in `blames`. pub fn new( id: impl Into, code: impl Into, diff --git a/src/git/changes.rs b/src/git/changes.rs index 161c1b7..3b151d5 100644 --- a/src/git/changes.rs +++ b/src/git/changes.rs @@ -16,7 +16,7 @@ use crate::{ typography::{Ellipsis, tabular}, }; -/// A changed file: its path, its status, and the lines it added and removed. +/// A changed file: path, status, and lines changed. #[derive(Clone, Debug, PartialEq)] pub struct Changed { pub path: SharedString, @@ -37,7 +37,7 @@ pub enum ChangeAction { type OnAction = Rc; type OnAll = Rc; -/// Which part of the work tree a file is listed in. +/// The part of the work tree listing a file. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum ChangeSection { Conflicted, @@ -59,7 +59,7 @@ impl ChangeSection { } } -/// Files changed in the work tree, kept apart as conflicted, staged, changed, untracked and ignored: each with its status, its folder and what it changed; open, discard, stage or unstage it, or all at once. +/// Work tree files in five sections, each with its actions. #[derive(IntoElement)] pub struct ChangesList { id: ElementId, @@ -98,7 +98,7 @@ impl ChangesList { self } - /// Files Git does not track yet; staged or discarded one by one. + /// Untracked files, staged one by one. pub fn untracked(mut self, files: impl IntoIterator) -> Self { self.untracked = files.into_iter().collect(); self @@ -124,7 +124,7 @@ impl ChangesList { self } - /// Called with true to stage every change, false to unstage every staged one. + /// True stages every change; false unstages every staged one. pub fn on_all(mut self, handler: impl Fn(bool, &mut Window, &mut App) + 'static) -> Self { self.on_all = Some(Rc::new(handler)); self @@ -330,12 +330,12 @@ impl RenderOnce for ChangesList { } } -/// Characters a subject reads well within, and the most it should take. +/// A subject's comfortable length, and its most. const SUBJECT: (usize, usize) = (50, 72); type Run = Rc; -/// A commit message: its subject counted against fifty, a body, and the commit button, which waits for a subject and something staged. Cmd-Enter commits; amend takes the last commit's place. +/// A commit message, its counted subject, body, and button. #[derive(IntoElement)] pub struct CommitInput { id: ElementId, diff --git a/src/git/commits.rs b/src/git/commits.rs index 7b46303..1105515 100644 --- a/src/git/commits.rs +++ b/src/git/commits.rs @@ -14,7 +14,7 @@ use crate::{ typography::Ellipsis, }; -/// A commit as lists show it: id, parents first-parent first, subject, author, when, and the refs on it. +/// A commit as lists show it; first parent first. #[derive(Clone, Debug, PartialEq)] pub struct Commit { pub id: SharedString, @@ -27,7 +27,7 @@ pub struct Commit { type OnPick = Rc; -/// One commit in a row: its author's initials, its subject and the refs on it, its short id, and when. +/// One commit in a row: author, subject, refs, id, when. #[derive(IntoElement)] pub struct CommitItem { id: ElementId, @@ -67,10 +67,15 @@ impl RenderOnce for CommitItem { let commit = self.commit; let short: String = commit.id.chars().take(7).collect(); let (pick, picked) = (self.on_pick, commit.id.clone()); + // Refs are tags drawn only; the name carries them. + let spoken: SharedString = match commit.refs.is_empty() { + true => commit.subject.clone(), + false => format!("{} ({})", commit.subject, commit.refs.join(", ")).into(), + }; div() .id(self.id.clone()) .role(Role::ListItem) - .aria_label(commit.subject.clone()) + .aria_label(spoken) .aria_selected(self.selected) .tab_index(0) .focus_ring(cx) @@ -185,7 +190,7 @@ impl CommitList { /// Rows from the end that ask for more. const NEAR_END: usize = 20; -/// Paints one row's lanes, `span` lanes wide so every row lines up: strokes into and out of its commit, lanes passing it, and the commit's dot. +/// Paints one row's lanes, `span` wide so rows line up. fn lanes_cell( row: GraphRow, span: usize,