| 33 |
33 |
|
//! That is the constraint worth designing against. It does not improve, it is
|
| 34 |
34 |
|
//! not detectable, and it applies equally to the best terminal ever written.
|
| 35 |
35 |
|
//!
|
|
36 |
+ |
//! What it does not mean is that the shape inside the cell stops mattering.
|
|
37 |
+ |
//! Half of a cell is still addressable, and a bevel drawn in half-blocks reads
|
|
38 |
+ |
//! as a lit edge where the same bevel in box-drawing reads as a line: `─` and
|
|
39 |
+ |
//! `│` are one stroke through the middle, identical on all four sides, saying
|
|
40 |
+ |
//! nothing about where the light is. Half-blocks also make the two corners
|
|
41 |
+ |
//! where light meets shadow expressible, since a glyph that fills half a cell
|
|
42 |
+ |
//! leaves the other half to the second tone.
|
|
43 |
+ |
//!
|
| 36 |
44 |
|
//! # Where fidelity does matter
|
| 37 |
45 |
|
//!
|
| 38 |
46 |
|
//! At [`Fidelity::Ansi16`] the depth vocabulary collapses outright: a well
|
| 69 |
77 |
|
use ratatui::layout::Rect;
|
| 70 |
78 |
|
use ratatui::style::Color;
|
| 71 |
79 |
|
|
|
80 |
+ |
/// The description this crate renders, re-exported.
|
|
81 |
+ |
///
|
|
82 |
+ |
/// Every entry point here takes a type from it, so a consumer would otherwise
|
|
83 |
+ |
/// have to depend on the description separately and keep two version
|
|
84 |
+ |
/// requirements in step to name the argument it is already being handed.
|
|
85 |
+ |
pub use makeover_layout;
|
|
86 |
+ |
|
| 72 |
87 |
|
/// How many colours the terminal can actually show.
|
| 73 |
88 |
|
///
|
| 74 |
89 |
|
/// Only [`Fidelity::Ansi16`] changes what this crate draws. Above it, colour
|
| 207 |
222 |
|
}
|
| 208 |
223 |
|
}
|
| 209 |
224 |
|
|
| 210 |
|
- |
/// Box-drawing characters for a frame's edges and corners.
|
|
225 |
+ |
/// The characters a frame's edges and corners are drawn with.
|
| 211 |
226 |
|
///
|
| 212 |
|
- |
/// Two sets, because at sixteen colours the glyphs are the only thing left to
|
| 213 |
|
- |
/// carry depth: a well cannot be filled distinctly and a bevel loses an edge,
|
| 214 |
|
- |
/// so a raised card and a well would otherwise be the same single-tone box.
|
| 215 |
|
- |
/// A doubled line reads as standing off the page and a light one as cut into
|
| 216 |
|
- |
/// it, which is the same claim the fill and the bevel make in colour.
|
|
227 |
+ |
/// Per side rather than per axis, because the set that reads best as a bevel
|
|
228 |
+ |
/// does not use the same glyph on opposite sides: a half-block edge is only
|
|
229 |
+ |
/// half a cell, and which half it occupies is what says where the edge is.
|
|
230 |
+ |
/// Box-drawing sets fill `top`/`bottom` and `left`/`right` with the same
|
|
231 |
+ |
/// character and lose nothing by it.
|
|
232 |
+ |
///
|
|
233 |
+ |
/// Three sets. [`BEVEL`] is what a terminal that can show two tones gets. The
|
|
234 |
+ |
/// other two exist because at sixteen colours the glyphs are the only thing
|
|
235 |
+ |
/// left to carry depth: a well cannot be filled distinctly and a bevel loses
|
|
236 |
+ |
/// an edge, so a raised card and a well would otherwise be the same
|
|
237 |
+ |
/// single-tone box. A doubled line reads as standing off the page and a light
|
|
238 |
+ |
/// one as cut into it, which is the same claim the fill and the bevel make in
|
|
239 |
+ |
/// colour.
|
| 217 |
240 |
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
| 218 |
241 |
|
pub(crate) struct GlyphSet {
|
| 219 |
|
- |
pub(crate) horizontal: &'static str,
|
| 220 |
|
- |
pub(crate) vertical: &'static str,
|
|
242 |
+ |
pub(crate) top: &'static str,
|
|
243 |
+ |
pub(crate) bottom: &'static str,
|
|
244 |
+ |
pub(crate) left: &'static str,
|
|
245 |
+ |
pub(crate) right: &'static str,
|
| 221 |
246 |
|
pub(crate) top_left: &'static str,
|
| 222 |
247 |
|
pub(crate) top_right: &'static str,
|
| 223 |
248 |
|
pub(crate) bottom_left: &'static str,
|
| 224 |
249 |
|
pub(crate) bottom_right: &'static str,
|
|
250 |
+ |
/// Whether the two corners where light meets shadow carry both tones in
|
|
251 |
+ |
/// one cell, foreground over background.
|
|
252 |
+ |
///
|
|
253 |
+ |
/// Only a half-cell glyph can: it already divides the cell, so the split
|
|
254 |
+ |
/// costs nothing and the corner reads as a transition rather than as one
|
|
255 |
+ |
/// edge overrunning the other. A box-drawing corner is a single stroke
|
|
256 |
+ |
/// with no such division, so those sets say `false` and both shared
|
|
257 |
+ |
/// corners go to dark — see [`paint_bevel_with`] for why that particular
|
|
258 |
+ |
/// fallback and not the other one.
|
|
259 |
+ |
pub(crate) split_corners: bool,
|
| 225 |
260 |
|
}
|
| 226 |
261 |
|
|
|
262 |
+ |
/// Half-blocks, which is what a bevel actually wants.
|
|
263 |
+ |
///
|
|
264 |
+ |
/// A cell is roughly 8x17 device pixels, so a half-block along the top and a
|
|
265 |
+ |
/// half-cell column down the side are about the same number of pixels and the
|
|
266 |
+ |
/// edge reads as even thickness. Box-drawing cannot do that: `─` and `│` are
|
|
267 |
+ |
/// both a thin stroke through the middle of the cell, identical on all four
|
|
268 |
+ |
/// sides, which draws a *line* rather than a lit edge and gives up the light
|
|
269 |
+ |
/// model that makes a bevel legible.
|
|
270 |
+ |
///
|
|
271 |
+ |
/// Adopted from `alloy_tui`, which reached this independently and got there
|
|
272 |
+ |
/// first (2026-07-26, two days before this crate existed).
|
|
273 |
+ |
pub(crate) const BEVEL: GlyphSet = GlyphSet {
|
|
274 |
+ |
top: "▀",
|
|
275 |
+ |
bottom: "▄",
|
|
276 |
+ |
left: "▌",
|
|
277 |
+ |
right: "▐",
|
|
278 |
+ |
top_left: "▛",
|
|
279 |
+ |
// The two shared corners are the split ones: an upper half continues the
|
|
280 |
+ |
// lit top edge while the lower half starts the shaded right edge, and the
|
|
281 |
+ |
// mirror of that at bottom left.
|
|
282 |
+ |
top_right: "▀",
|
|
283 |
+ |
bottom_left: "▄",
|
|
284 |
+ |
bottom_right: "▟",
|
|
285 |
+ |
split_corners: true,
|
|
286 |
+ |
};
|
|
287 |
+ |
|
| 227 |
288 |
|
pub(crate) const LIGHT: GlyphSet = GlyphSet {
|
| 228 |
|
- |
horizontal: "─",
|
| 229 |
|
- |
vertical: "│",
|
|
289 |
+ |
top: "─",
|
|
290 |
+ |
bottom: "─",
|
|
291 |
+ |
left: "│",
|
|
292 |
+ |
right: "│",
|
| 230 |
293 |
|
top_left: "┌",
|
| 231 |
294 |
|
top_right: "┐",
|
| 232 |
295 |
|
bottom_left: "└",
|
| 233 |
296 |
|
bottom_right: "┘",
|
|
297 |
+ |
split_corners: false,
|
| 234 |
298 |
|
};
|
| 235 |
299 |
|
|
| 236 |
300 |
|
pub(crate) const DOUBLE: GlyphSet = GlyphSet {
|
| 237 |
|
- |
horizontal: "═",
|
| 238 |
|
- |
vertical: "║",
|
|
301 |
+ |
top: "═",
|
|
302 |
+ |
bottom: "═",
|
|
303 |
+ |
left: "║",
|
|
304 |
+ |
right: "║",
|
| 239 |
305 |
|
top_left: "╔",
|
| 240 |
306 |
|
top_right: "╗",
|
| 241 |
307 |
|
bottom_left: "╚",
|
| 242 |
308 |
|
bottom_right: "╝",
|
|
309 |
+ |
split_corners: false,
|
| 243 |
310 |
|
};
|
| 244 |
311 |
|
|
| 245 |
312 |
|
/// Paint a two-tone edge around the outside of `area`.
|
| 246 |
313 |
|
///
|
| 247 |
|
- |
/// Light takes the top and left, dark the bottom and right, and the two shared
|
| 248 |
|
- |
/// corners go to dark. That corner rule is not arbitrary: it is the same one
|
| 249 |
|
- |
/// `makeover-immediate` produces by drawing its dark polyline second, so a
|
| 250 |
|
- |
/// control does not change which corner is lit when it moves between a
|
| 251 |
|
- |
/// terminal and a window.
|
|
314 |
+ |
/// Light takes the top and left, dark the bottom and right. What happens at
|
|
315 |
+ |
/// the two corners where they meet depends on what the terminal can show.
|
|
316 |
+ |
/// Above sixteen colours the edge is drawn in half-blocks and those corners
|
|
317 |
+ |
/// carry both tones, one per half-cell. At sixteen it is box-drawing, whose
|
|
318 |
+ |
/// single stroke has no half to give, so both shared corners go to dark.
|
| 252 |
319 |
|
///
|
| 253 |
320 |
|
/// Costs a cell on each side, which a pixel renderer's bevel does not. Use the
|
| 254 |
321 |
|
/// [`Rect`] returned by [`frame`] rather than assuming the area is intact.
|
| 255 |
322 |
|
pub fn paint_bevel(buf: &mut Buffer, area: Rect, bevel: Bevel, palette: &Palette) {
|
| 256 |
|
- |
paint_bevel_with(buf, area, bevel, palette, LIGHT);
|
|
323 |
+ |
paint_bevel_with(buf, area, bevel, palette, set_for(palette, None));
|
|
324 |
+ |
}
|
|
325 |
+ |
|
|
326 |
+ |
/// Which glyphs to draw with, given what the terminal can show.
|
|
327 |
+ |
///
|
|
328 |
+ |
/// Above sixteen colours the two tones are available and [`BEVEL`] renders
|
|
329 |
+ |
/// them as light. At sixteen the tones collapse, so the box-drawing sets carry
|
|
330 |
+ |
/// the distinction in weight instead, and `depth` picks which: a doubled frame
|
|
331 |
+ |
/// for a raised card and a light one for everything else. `None` means the
|
|
332 |
+ |
/// caller is drawing a bevel with no depth behind it, which is never the
|
|
333 |
+ |
/// doubled case.
|
|
334 |
+ |
fn set_for(palette: &Palette, depth: Option<Depth>) -> GlyphSet {
|
|
335 |
+ |
if !palette.needs_glyph_depth() {
|
|
336 |
+ |
return BEVEL;
|
|
337 |
+ |
}
|
|
338 |
+ |
match depth {
|
|
339 |
+ |
Some(Depth::Raised) => DOUBLE,
|
|
340 |
+ |
_ => LIGHT,
|
|
341 |
+ |
}
|
| 257 |
342 |
|
}
|
| 258 |
343 |
|
|
| 259 |
344 |
|
fn paint_bevel_with(buf: &mut Buffer, area: Rect, bevel: Bevel, palette: &Palette, set: GlyphSet) {
|
| 269 |
354 |
|
|
| 270 |
355 |
|
// Light first: top edge and left edge, corners included.
|
| 271 |
356 |
|
for x in x0..=x1 {
|
| 272 |
|
- |
buf[(x, y0)].set_symbol(set.horizontal).set_fg(light);
|
|
357 |
+ |
buf[(x, y0)].set_symbol(set.top).set_fg(light);
|
| 273 |
358 |
|
}
|
| 274 |
359 |
|
for y in y0..=y1 {
|
| 275 |
|
- |
buf[(x0, y)].set_symbol(set.vertical).set_fg(light);
|
|
360 |
+ |
buf[(x0, y)].set_symbol(set.left).set_fg(light);
|
| 276 |
361 |
|
}
|
| 277 |
|
- |
// Dark second, so the two shared corners land on it.
|
|
362 |
+ |
// Dark second, so on a set without split corners the two shared ones land
|
|
363 |
+ |
// on it by draw order alone.
|
| 278 |
364 |
|
for x in x0..=x1 {
|
| 279 |
|
- |
buf[(x, y1)].set_symbol(set.horizontal).set_fg(dark);
|
|
365 |
+ |
buf[(x, y1)].set_symbol(set.bottom).set_fg(dark);
|
| 280 |
366 |
|
}
|
| 281 |
367 |
|
for y in y0..=y1 {
|
| 282 |
|
- |
buf[(x1, y)].set_symbol(set.vertical).set_fg(dark);
|
|
368 |
+ |
buf[(x1, y)].set_symbol(set.right).set_fg(dark);
|
| 283 |
369 |
|
}
|
| 284 |
370 |
|
|
| 285 |
371 |
|
buf[(x0, y0)].set_symbol(set.top_left).set_fg(light);
|
| 286 |
|
- |
buf[(x1, y0)].set_symbol(set.top_right).set_fg(dark);
|
| 287 |
|
- |
buf[(x0, y1)].set_symbol(set.bottom_left).set_fg(dark);
|
| 288 |
372 |
|
buf[(x1, y1)].set_symbol(set.bottom_right).set_fg(dark);
|
|
373 |
+ |
|
|
374 |
+ |
if set.split_corners {
|
|
375 |
+ |
// Where light meets shadow, both tones share the cell: the half the
|
|
376 |
+ |
// glyph fills is the foreground and the half it leaves is the
|
|
377 |
+ |
// background, so the corner is a transition rather than one edge
|
|
378 |
+ |
// overrunning the other.
|
|
379 |
+ |
buf[(x1, y0)]
|
|
380 |
+ |
.set_symbol(set.top_right)
|
|
381 |
+ |
.set_fg(light)
|
|
382 |
+ |
.set_bg(dark);
|
|
383 |
+ |
buf[(x0, y1)]
|
|
384 |
+ |
.set_symbol(set.bottom_left)
|
|
385 |
+ |
.set_fg(dark)
|
|
386 |
+ |
.set_bg(light);
|
|
387 |
+ |
} else {
|
|
388 |
+ |
// Both shared corners to dark. Not arbitrary: it is the same rule
|
|
389 |
+ |
// `makeover-immediate` produces by drawing its dark polyline second,
|
|
390 |
+ |
// so a control does not change which corner is lit when it moves
|
|
391 |
+ |
// between a terminal and a window. A single-stroke corner has no half
|
|
392 |
+ |
// to give the other tone, so this is the only rule available to these
|
|
393 |
+ |
// sets anyway.
|
|
394 |
+ |
buf[(x1, y0)].set_symbol(set.top_right).set_fg(dark);
|
|
395 |
+ |
buf[(x0, y1)].set_symbol(set.bottom_left).set_fg(dark);
|
|
396 |
+ |
}
|
| 289 |
397 |
|
}
|
| 290 |
398 |
|
|
| 291 |
399 |
|
/// Draw a region at a given [`Depth`] and return the area left for content.
|
| 314 |
422 |
|
// Colour separates raised from well wherever it can. Where it
|
| 315 |
423 |
|
// cannot, the glyphs do, and only then: a doubled frame on every
|
| 316 |
424 |
|
// terminal would be shouting.
|
| 317 |
|
- |
let set = match (palette.needs_glyph_depth(), depth) {
|
| 318 |
|
- |
(true, Depth::Raised) => DOUBLE,
|
| 319 |
|
- |
_ => LIGHT,
|
| 320 |
|
- |
};
|
|
425 |
+ |
let set = set_for(palette, Some(depth));
|
| 321 |
426 |
|
paint_bevel_with(buf, area, bevel, palette, set);
|
| 322 |
427 |
|
Rect::new(area.x + 1, area.y + 1, area.width - 2, area.height - 2)
|
| 323 |
428 |
|
}
|
| 352 |
457 |
|
let mut buf = buffer();
|
| 353 |
458 |
|
frame(&mut buf, Rect::new(0, 0, 6, 4), Depth::Well, &p);
|
| 354 |
459 |
|
// No fill was available, but the region still reads as recessed.
|
| 355 |
|
- |
assert_eq!(buf[(0, 0)].symbol(), LIGHT.top_left);
|
|
460 |
+ |
assert_eq!(buf[(0, 0)].symbol(), BEVEL.top_left);
|
| 356 |
461 |
|
assert_eq!(buf[(0, 0)].bg, Color::Reset);
|
| 357 |
462 |
|
}
|
| 358 |
463 |
|
|
| 370 |
475 |
|
frame(&mut buf, Rect::new(0, 0, 6, 4), Depth::Well, &p);
|
| 371 |
476 |
|
assert!(!Palette::shows(Color::Indexed(7), Color::Indexed(7)));
|
| 372 |
477 |
|
// The edge is what carries the meaning here.
|
| 373 |
|
- |
assert_eq!(buf[(5, 3)].symbol(), LIGHT.bottom_right);
|
|
478 |
+ |
assert_eq!(buf[(5, 3)].symbol(), BEVEL.bottom_right);
|
| 374 |
479 |
|
}
|
| 375 |
480 |
|
|
| 376 |
481 |
|
#[test]
|
| 382 |
487 |
|
}
|
| 383 |
488 |
|
|
| 384 |
489 |
|
#[test]
|
| 385 |
|
- |
fn the_light_falls_from_the_top_left_and_corners_go_dark() {
|
|
490 |
+ |
fn the_light_falls_from_the_top_left() {
|
| 386 |
491 |
|
let p = palette(None);
|
| 387 |
492 |
|
let mut buf = buffer();
|
| 388 |
493 |
|
paint_bevel(&mut buf, Rect::new(0, 0, 6, 4), Bevel::Raised, &p);
|
| 390 |
495 |
|
assert_eq!(buf[(3, 0)].fg, p.bevel_light); // top edge
|
| 391 |
496 |
|
assert_eq!(buf[(0, 2)].fg, p.bevel_light); // left edge
|
| 392 |
497 |
|
assert_eq!(buf[(5, 3)].fg, p.bevel_dark); // bottom-right
|
| 393 |
|
- |
// The two shared corners go to dark, matching what the immediate
|
| 394 |
|
- |
// renderer produces by drawing its dark polyline second.
|
|
498 |
+ |
assert_eq!(buf[(3, 3)].fg, p.bevel_dark); // bottom edge
|
|
499 |
+ |
assert_eq!(buf[(5, 2)].fg, p.bevel_dark); // right edge
|
|
500 |
+ |
}
|
|
501 |
+ |
|
|
502 |
+ |
// Half-cell glyphs divide the cell already, so the corner where light
|
|
503 |
+ |
// meets shadow can hold both rather than picking one.
|
|
504 |
+ |
#[test]
|
|
505 |
+ |
fn the_shared_corners_carry_both_tones_when_the_glyph_can_split() {
|
|
506 |
+ |
let p = palette(None);
|
|
507 |
+ |
let mut buf = buffer();
|
|
508 |
+ |
paint_bevel(&mut buf, Rect::new(0, 0, 6, 4), Bevel::Raised, &p);
|
|
509 |
+ |
let top_right = &buf[(5, 0)];
|
|
510 |
+ |
assert_eq!(top_right.fg, p.bevel_light);
|
|
511 |
+ |
assert_eq!(top_right.bg, p.bevel_dark);
|
|
512 |
+ |
let bottom_left = &buf[(0, 3)];
|
|
513 |
+ |
assert_eq!(bottom_left.fg, p.bevel_dark);
|
|
514 |
+ |
assert_eq!(bottom_left.bg, p.bevel_light);
|
|
515 |
+ |
}
|
|
516 |
+ |
|
|
517 |
+ |
// A single-stroke corner has no half to give the second tone, so the
|
|
518 |
+ |
// box-drawing sets keep the old rule: both shared corners to dark, which
|
|
519 |
+ |
// is what makeover-immediate produces by drawing its dark polyline second.
|
|
520 |
+ |
// Changing that would move the lit corner between a terminal and a window.
|
|
521 |
+ |
#[test]
|
|
522 |
+ |
fn box_drawing_corners_stay_dark_and_match_the_immediate_renderer() {
|
|
523 |
+ |
let p = Palette {
|
|
524 |
+ |
fidelity: Fidelity::Ansi16,
|
|
525 |
+ |
..palette(None)
|
|
526 |
+ |
};
|
|
527 |
+ |
let mut buf = buffer();
|
|
528 |
+ |
paint_bevel(&mut buf, Rect::new(0, 0, 6, 4), Bevel::Raised, &p);
|
|
529 |
+ |
assert_eq!(buf[(5, 0)].symbol(), LIGHT.top_right);
|
| 395 |
530 |
|
assert_eq!(buf[(5, 0)].fg, p.bevel_dark);
|
|
531 |
+ |
assert_eq!(buf[(5, 0)].bg, Color::Reset, "a stroke has no second tone");
|
| 396 |
532 |
|
assert_eq!(buf[(0, 3)].fg, p.bevel_dark);
|
| 397 |
533 |
|
}
|
| 398 |
534 |
|
|
|
535 |
+ |
// The whole outline, as a reader sees it. Asserted as glyphs because the
|
|
536 |
+ |
// shape is the point: an even-weight edge on all four sides, which is what
|
|
537 |
+ |
// box-drawing could not give.
|
|
538 |
+ |
#[test]
|
|
539 |
+ |
fn a_bevel_draws_an_even_outline_and_leaves_the_middle_alone() {
|
|
540 |
+ |
let p = palette(None);
|
|
541 |
+ |
let mut buf = Buffer::empty(Rect::new(0, 0, 5, 4));
|
|
542 |
+ |
paint_bevel(&mut buf, Rect::new(0, 0, 5, 4), Bevel::Raised, &p);
|
|
543 |
+ |
let rows: Vec<String> = (0..4)
|
|
544 |
+ |
.map(|y| (0..5).map(|x| buf[(x, y)].symbol()).collect())
|
|
545 |
+ |
.collect();
|
|
546 |
+ |
assert_eq!(rows, vec!["▛▀▀▀▀", "▌ ▐", "▌ ▐", "▄▄▄▄▟"]);
|
|
547 |
+ |
}
|
|
548 |
+ |
|
| 399 |
549 |
|
#[test]
|
| 400 |
550 |
|
fn pressing_swaps_the_lit_side() {
|
| 401 |
551 |
|
let p = palette(None);
|
| 448 |
598 |
|
|
| 449 |
599 |
|
#[test]
|
| 450 |
600 |
|
fn above_sixteen_colours_the_glyphs_stay_out_of_it() {
|
| 451 |
|
- |
// The fallback must not fire where colour already works, or every
|
| 452 |
|
- |
// modern terminal gets a doubled frame it did not need.
|
|
601 |
+ |
// The doubled fallback must not fire where colour already works, or
|
|
602 |
+ |
// every modern terminal gets a heavier frame it did not need. What it
|
|
603 |
+ |
// gets instead is the half-block bevel.
|
| 453 |
604 |
|
for f in [Fidelity::Ansi256, Fidelity::TrueColor] {
|
| 454 |
605 |
|
let p = Palette {
|
| 455 |
606 |
|
fidelity: f,
|
| 460 |
611 |
|
frame(&mut buf, Rect::new(0, 0, 6, 4), Depth::Raised, &p);
|
| 461 |
612 |
|
assert_eq!(
|
| 462 |
613 |
|
buf[(0, 0)].symbol(),
|
| 463 |
|
- |
LIGHT.top_left,
|
|
614 |
+ |
BEVEL.top_left,
|
| 464 |
615 |
|
"{f:?} got a heavier frame"
|
| 465 |
616 |
|
);
|
|
617 |
+ |
assert_ne!(buf[(0, 0)].symbol(), DOUBLE.top_left);
|
| 466 |
618 |
|
}
|
| 467 |
619 |
|
}
|
| 468 |
620 |
|
|
|
621 |
+ |
// Raised and well are both bevels and differ only in which way they are
|
|
622 |
+ |
// lit, so above sixteen colours they draw the same glyphs and the tones
|
|
623 |
+ |
// carry the difference. That is exactly what stops holding at Ansi16, and
|
|
624 |
+ |
// why the doubled set exists.
|
|
625 |
+ |
#[test]
|
|
626 |
+ |
fn colour_alone_separates_raised_from_well_where_it_can() {
|
|
627 |
+ |
let p = palette(Some(Color::Indexed(4)));
|
|
628 |
+ |
let mut raised = buffer();
|
|
629 |
+ |
frame(&mut raised, Rect::new(0, 0, 6, 4), Depth::Raised, &p);
|
|
630 |
+ |
let mut well = buffer();
|
|
631 |
+ |
frame(&mut well, Rect::new(0, 0, 6, 4), Depth::Well, &p);
|
|
632 |
+ |
assert_eq!(raised[(0, 0)].symbol(), well[(0, 0)].symbol());
|
|
633 |
+ |
assert_eq!(raised[(0, 0)].fg, p.bevel_light);
|
|
634 |
+ |
assert_eq!(well[(0, 0)].fg, p.bevel_dark);
|
|
635 |
+ |
}
|
|
636 |
+ |
|
| 469 |
637 |
|
#[test]
|
| 470 |
638 |
|
fn detection_defaults_generously_and_only_downgrades_on_evidence() {
|
| 471 |
639 |
|
assert!(Fidelity::default().separates_depth());
|