| 98 |
98 |
|
pub header_height: f32,
|
| 99 |
99 |
|
/// The height of a body row.
|
| 100 |
100 |
|
pub row_height: f32,
|
| 101 |
|
- |
/// Drawn after the heading of an ascending column.
|
|
101 |
+ |
/// The caret drawn after the heading of an ascending column.
|
|
102 |
+ |
///
|
|
103 |
+ |
/// Defaults to [`Sort::glyph`], which is where the spelling lives now:
|
|
104 |
+ |
/// three renderers holding the same literal agreed by coincidence. Bare,
|
|
105 |
+ |
/// with no leading space -- the gap is [`heading`]'s, written once for all
|
|
106 |
+ |
/// three states rather than baked into two strings and forgotten in the
|
|
107 |
+ |
/// third.
|
| 102 |
108 |
|
pub ascending: &'static str,
|
| 103 |
109 |
|
/// Drawn after the heading of a descending column.
|
| 104 |
110 |
|
pub descending: &'static str,
|
| 133 |
139 |
|
Self {
|
| 134 |
140 |
|
header_height: 20.0,
|
| 135 |
141 |
|
row_height: 18.0,
|
| 136 |
|
- |
// The pair audiofiles already draws, so a sorted column points the
|
| 137 |
|
- |
// same way here as it does in a terminal.
|
| 138 |
|
- |
ascending: " \u{25B2}",
|
| 139 |
|
- |
descending: " \u{25BC}",
|
|
142 |
+ |
ascending: Sort::Ascending.glyph(),
|
|
143 |
+ |
descending: Sort::Descending.glyph(),
|
| 140 |
144 |
|
striped: false,
|
| 141 |
145 |
|
resizable: false,
|
| 142 |
146 |
|
}
|
| 244 |
248 |
|
/// cannot change is a real thing, and the caret is how it says so.
|
| 245 |
249 |
|
#[must_use]
|
| 246 |
250 |
|
pub fn heading(column: &Column<'_>, style: &TableStyle) -> String {
|
| 247 |
|
- |
match column.sorted {
|
| 248 |
|
- |
Some(Sort::Ascending) => format!("{}{}", column.name, style.ascending),
|
| 249 |
|
- |
Some(Sort::Descending) => format!("{}{}", column.name, style.descending),
|
| 250 |
|
- |
None => column.name.to_owned(),
|
| 251 |
|
- |
}
|
|
251 |
+ |
let caret = match column.sorted {
|
|
252 |
+ |
Some(Sort::Ascending) => style.ascending,
|
|
253 |
+ |
Some(Sort::Descending) => style.descending,
|
|
254 |
+ |
// Sortable and not sorted draws the idle mark, in the ascending
|
|
255 |
+ |
// spelling because that is the direction a first press takes. What
|
|
256 |
+ |
// separates it from the column in force is the tone, which is
|
|
257 |
+ |
// [`press`]'s to pick.
|
|
258 |
+ |
None if column.sortable => style.ascending,
|
|
259 |
+ |
None => return column.name.to_owned(),
|
|
260 |
+ |
};
|
|
261 |
+ |
format!("{} {caret}", column.name)
|
| 252 |
262 |
|
}
|
| 253 |
263 |
|
|
| 254 |
264 |
|
/// How wide a column asks to be at its narrowest, in points.
|
| 391 |
401 |
|
pressed.get()
|
| 392 |
402 |
|
}
|
| 393 |
403 |
|
|
|
404 |
+ |
/// What one heading is drawn in.
|
|
405 |
+ |
///
|
|
406 |
+ |
/// Three states, three tones (wiki `three-tone-convention`). The column in force
|
|
407 |
+ |
/// is the emphasised thing; a column offering to reorder is inactive but usable,
|
|
408 |
+ |
/// because it answers a press; a column that is not a control at all is inert.
|
|
409 |
+ |
///
|
|
410 |
+ |
/// The middle one used to take `content_muted`, which is what
|
|
411 |
+ |
/// [`State::Disabled`](makeover_layout::State::Disabled) resolves to, so a
|
|
412 |
+ |
/// heading the user could press claimed it would not answer. The same lie the
|
|
413 |
+ |
/// unchosen option in a choice field was telling before 0.26.0.
|
|
414 |
+ |
fn heading_color(column: &Column<'_>, palette: &Palette) -> egui::Color32 {
|
|
415 |
+ |
match (column.sorted, column.sortable) {
|
|
416 |
+ |
(Some(_), _) => palette.content,
|
|
417 |
+ |
(None, true) => palette.content_secondary,
|
|
418 |
+ |
(None, false) => palette.content_muted,
|
|
419 |
+ |
}
|
|
420 |
+ |
}
|
|
421 |
+ |
|
| 394 |
422 |
|
/// One heading, and whether it was pressed.
|
| 395 |
423 |
|
fn press(ui: &mut Ui, column: &Column<'_>, palette: &Palette, style: &TableStyle) -> bool {
|
| 396 |
|
- |
let text = RichText::new(heading(column, style)).strong();
|
|
424 |
+ |
let text = RichText::new(heading(column, style)).color(heading_color(column, palette));
|
| 397 |
425 |
|
if !column.sortable {
|
| 398 |
|
- |
// Muted, and not sensed. A heading a user cannot press must not look
|
| 399 |
|
- |
// like one they can, which is the affordance `Column::sortable` exists
|
| 400 |
|
- |
// to carry.
|
| 401 |
|
- |
ui.label(text.color(palette.content_muted));
|
|
426 |
+ |
// Not sensed. A heading a user cannot press must not look like one they
|
|
427 |
+ |
// can, which is the affordance `Column::sortable` exists to carry, and
|
|
428 |
+ |
// the tone above is half of saying so.
|
|
429 |
+ |
ui.label(text.strong());
|
| 402 |
430 |
|
return false;
|
| 403 |
431 |
|
}
|
| 404 |
|
- |
let tone = if column.sorted.is_some() {
|
| 405 |
|
- |
palette.content
|
| 406 |
|
- |
} else {
|
| 407 |
|
- |
palette.content_muted
|
| 408 |
|
- |
};
|
| 409 |
432 |
|
let response: Response = ui
|
| 410 |
|
- |
.add(egui::Label::new(text.color(tone)).sense(Sense::click()))
|
|
433 |
+ |
.add(egui::Label::new(text.strong()).sense(Sense::click()))
|
| 411 |
434 |
|
.on_hover_cursor(egui::CursorIcon::PointingHand);
|
| 412 |
435 |
|
response.clicked()
|
| 413 |
436 |
|
}
|
| 566 |
589 |
|
}
|
| 567 |
590 |
|
|
| 568 |
591 |
|
#[test]
|
| 569 |
|
- |
fn the_ordered_column_draws_a_caret_and_the_others_do_not() {
|
|
592 |
+ |
fn a_heading_carries_a_caret_when_it_is_ordered_by_or_offers_to_be() {
|
| 570 |
593 |
|
let style = TableStyle::default();
|
| 571 |
594 |
|
let cols = columns();
|
| 572 |
595 |
|
assert_eq!(heading(&cols[0], &style), "name \u{25B2}");
|
| 573 |
|
- |
assert_eq!(heading(&cols[1], &style), "size");
|
|
596 |
+ |
// Sortable and idle. It draws the mark a first press would give, which
|
|
597 |
+ |
// is what stops the press from widening the column and shifting the
|
|
598 |
+ |
// ones after it.
|
|
599 |
+ |
assert_eq!(heading(&cols[1], &style), "size \u{25B2}");
|
|
600 |
+ |
// Not a control. Nothing to mark.
|
| 574 |
601 |
|
assert_eq!(heading(&cols[2], &style), "note");
|
| 575 |
602 |
|
}
|
| 576 |
603 |
|
|
|
604 |
+ |
#[test]
|
|
605 |
+ |
fn the_three_states_of_a_heading_are_three_tones() {
|
|
606 |
+ |
// wiki `three-tone-convention`. The middle state used to take
|
|
607 |
+ |
// content_muted, which is what `State::Disabled` resolves to, so a
|
|
608 |
+ |
// heading the user could press claimed it would not answer. The arm
|
|
609 |
+ |
// that keeps muted is the one where it is true.
|
|
610 |
+ |
let p = palette();
|
|
611 |
+ |
let cols = columns();
|
|
612 |
+ |
assert_eq!(heading_color(&cols[0], &p), p.content);
|
|
613 |
+ |
assert_eq!(heading_color(&cols[1], &p), p.content_secondary);
|
|
614 |
+ |
assert_eq!(heading_color(&cols[2], &p), p.content_muted);
|
|
615 |
+ |
}
|
|
616 |
+ |
|
| 577 |
617 |
|
#[test]
|
| 578 |
618 |
|
fn a_column_sorted_without_being_sortable_still_draws_its_caret() {
|
| 579 |
619 |
|
// A list ordered by a key the user cannot change is a real thing to
|
| 593 |
633 |
|
// Two crates, one glyph pair, and no dependency between them to enforce
|
| 594 |
634 |
|
// it. A description sorted ascending must not point up in a window and
|
| 595 |
635 |
|
// down in a terminal.
|
|
636 |
+ |
// Composition rather than agreement since makeover-layout 0.27.5: both
|
|
637 |
+ |
// read `Sort::glyph`, so a fourth spelling cannot appear in one crate.
|
| 596 |
638 |
|
let style = TableStyle::default();
|
| 597 |
|
- |
assert_eq!(style.ascending, " \u{25B2}");
|
| 598 |
|
- |
assert_eq!(style.descending, " \u{25BC}");
|
|
639 |
+ |
assert_eq!(style.ascending, Sort::Ascending.glyph());
|
|
640 |
+ |
assert_eq!(style.descending, Sort::Descending.glyph());
|
|
641 |
+ |
// Bare. The gap is `heading`'s, so a consumer swapping the glyph for an
|
|
642 |
+ |
// ASCII one does not have to remember to bring a space with it.
|
|
643 |
+ |
assert_eq!(style.ascending.trim(), style.ascending);
|
| 599 |
644 |
|
}
|
| 600 |
645 |
|
|
| 601 |
646 |
|
#[test]
|