ui: move box drawing to src/terminal

No logical reason for it not to be in the terminal module anymore (the
set_and_join* functions predate the terminal module which is why they
weren't there to begin with).
async
Manos Pitsidianakis 2020-02-06 21:51:13 +02:00
parent f131e01bfc
commit d6e3c51b07
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
3 changed files with 416 additions and 353 deletions

View File

@ -27,6 +27,7 @@
use super::*;
use crate::melib::text_processing::{TextProcessing, Truncate};
use crate::terminal::boundaries::*;
pub mod mail;
pub use crate::mail::*;
@ -46,32 +47,6 @@ use fnv::FnvHashMap;
use uuid::Uuid;
use super::{Key, StatusEvent, UIEvent};
/// The upper and lower boundary char.
const HORZ_BOUNDARY: char = '─';
/// The left and right boundary char.
const VERT_BOUNDARY: char = 'β”‚';
/// The top-left corner
const _TOP_LEFT_CORNER: char = 'β”Œ';
/// The top-right corner
const _TOP_RIGHT_CORNER: char = '┐';
/// The bottom-left corner
const _BOTTOM_LEFT_CORNER: char = 'β””';
/// The bottom-right corner
const _BOTTOM_RIGHT_CORNER: char = 'β”˜';
const LIGHT_VERTICAL_AND_RIGHT: char = 'β”œ';
const _LIGHT_VERTICAL_AND_LEFT: char = '─';
const LIGHT_DOWN_AND_HORIZONTAL: char = '┬';
const LIGHT_UP_AND_HORIZONTAL: char = 'β”΄';
const _DOUBLE_DOWN_AND_RIGHT: char = 'β•”';
const _DOUBLE_DOWN_AND_LEFT: char = 'β•—';
const _DOUBLE_UP_AND_LEFT: char = '╝';
const _DOUBLE_UP_AND_RIGHT: char = 'β•š';
type ComponentId = Uuid;
@ -104,312 +79,3 @@ pub trait Component: Display + Debug + Send {
None
}
}
fn bin_to_ch(b: u32) -> char {
match b {
0b0001 => 'β•Ά',
0b0010 => 'β•΅',
0b0011 => 'β””',
0b0100 => 'β•΄',
0b0101 => '─',
0b0110 => 'β”˜',
0b0111 => 'β”΄',
0b1000 => 'β•·',
0b1001 => 'β”Œ',
0b1010 => 'β”‚',
0b1011 => 'β”œ',
0b1100 => '┐',
0b1101 => '┬',
0b1110 => '─',
0b1111 => 'β”Ό',
_ => unsafe { std::hint::unreachable_unchecked() },
}
}
fn ch_to_bin(ch: char) -> Option<u32> {
match ch {
'β””' => Some(0b0011),
'─' => Some(0b0101),
'β”˜' => Some(0b0110),
'β”΄' => Some(0b0111),
'β”Œ' => Some(0b1001),
'β”‚' => Some(0b1010),
'β”œ' => Some(0b1011),
'┐' => Some(0b1100),
'┬' => Some(0b1101),
'─' => Some(0b1110),
'β”Ό' => Some(0b1111),
'β•·' => Some(0b1000),
'β•΅' => Some(0b0010),
'β•΄' => Some(0b0100),
'β•Ά' => Some(0b0001),
_ => None,
}
}
#[allow(clippy::never_loop)]
fn set_and_join_vert(grid: &mut CellBuffer, idx: Pos) -> u32 {
let (x, y) = idx;
let mut bin_set = 0b1010;
/* Check left side
*
* 1
* -> 2 β”‚ 0
* 3
*/
loop {
if x > 0 {
if let Some(cell) = grid.get_mut(x - 1, y) {
if let Some(adj) = ch_to_bin(cell.ch()) {
if (adj & 0b0001) > 0 {
bin_set |= 0b0100;
break;
} else if adj == 0b0100 {
cell.set_ch(bin_to_ch(0b0101));
cell.set_fg(Color::Byte(240));
bin_set |= 0b0100;
break;
}
}
}
}
bin_set &= 0b1011;
break;
}
/* Check right side
*
* 1
* 2 β”‚ 0 <-
* 3
*/
loop {
if let Some(cell) = grid.get_mut(x + 1, y) {
if let Some(adj) = ch_to_bin(cell.ch()) {
if (adj & 0b0100) > 0 {
bin_set |= 0b0001;
break;
}
}
}
bin_set &= 0b1110;
break;
}
/* Set upper side
*
* 1 <-
* 2 β”‚ 0
* 3
*/
loop {
if y > 0 {
if let Some(cell) = grid.get_mut(x, y - 1) {
if let Some(adj) = ch_to_bin(cell.ch()) {
cell.set_ch(bin_to_ch(adj | 0b1000));
cell.set_fg(Color::Byte(240));
} else {
bin_set &= 0b1101;
}
}
}
break;
}
/* Set bottom side
*
* 1
* 2 β”‚ 0
* 3 <-
*/
loop {
if let Some(cell) = grid.get_mut(x, y + 1) {
if let Some(adj) = ch_to_bin(cell.ch()) {
cell.set_ch(bin_to_ch(adj | 0b0010));
cell.set_fg(Color::Byte(240));
} else {
bin_set &= 0b0111;
}
}
break;
}
if bin_set == 0 {
bin_set = 0b1010;
}
bin_set
}
#[allow(clippy::never_loop)]
fn set_and_join_horz(grid: &mut CellBuffer, idx: Pos) -> u32 {
let (x, y) = idx;
let mut bin_set = 0b0101;
/* Check upper side
*
* 1 <-
* 2 ─ 0
* 3
*/
loop {
if y > 0 {
if let Some(cell) = grid.get_mut(x, y - 1) {
if let Some(adj) = ch_to_bin(cell.ch()) {
if (adj & 0b1000) > 0 {
bin_set |= 0b0010;
break;
} else if adj == 0b0010 {
bin_set |= 0b0010;
cell.set_ch(bin_to_ch(0b1010));
cell.set_fg(Color::Byte(240));
break;
}
}
}
}
bin_set &= 0b1101;
break;
}
/* Check bottom side
*
* 1
* 2 ─ 0
* 3 <-
*/
loop {
if let Some(cell) = grid.get_mut(x, y + 1) {
if let Some(adj) = ch_to_bin(cell.ch()) {
if (adj & 0b0010) > 0 {
bin_set |= 0b1000;
break;
} else if adj == 0b1000 {
bin_set |= 0b1000;
cell.set_ch(bin_to_ch(0b1010));
cell.set_fg(Color::Byte(240));
break;
}
}
}
bin_set &= 0b0111;
break;
}
/* Set left side
*
* 1
* -> 2 ─ 0
* 3
*/
loop {
if x > 0 {
if let Some(cell) = grid.get_mut(x - 1, y) {
if let Some(adj) = ch_to_bin(cell.ch()) {
cell.set_ch(bin_to_ch(adj | 0b0001));
cell.set_fg(Color::Byte(240));
} else {
bin_set &= 0b1011;
}
}
}
break;
}
/* Set right side
*
* 1
* 2 ─ 0 <-
* 3
*/
loop {
if let Some(cell) = grid.get_mut(x + 1, y) {
if let Some(adj) = ch_to_bin(cell.ch()) {
cell.set_ch(bin_to_ch(adj | 0b0100));
cell.set_fg(Color::Byte(240));
} else {
bin_set &= 0b1110;
}
}
break;
}
if bin_set == 0 {
bin_set = 0b0101;
}
bin_set
}
pub enum BoxBoundary {
Horizontal,
Vertical,
}
pub(crate) fn set_and_join_box(grid: &mut CellBuffer, idx: Pos, ch: BoxBoundary) {
/* Connected sides:
*
* 1
* 2 c 0
* 3
*
* #3210
* 0b____
*/
if grid.ascii_drawing {
grid[idx].set_ch(match ch {
BoxBoundary::Vertical => '|',
BoxBoundary::Horizontal => '-',
});
grid[idx].set_fg(Color::Byte(240));
return;
}
let bin_set = match ch {
BoxBoundary::Vertical => set_and_join_vert(grid, idx),
BoxBoundary::Horizontal => set_and_join_horz(grid, idx),
};
grid[idx].set_ch(bin_to_ch(bin_set));
grid[idx].set_fg(Color::Byte(240));
}
pub fn create_box(grid: &mut CellBuffer, area: Area) {
if !is_valid_area!(area) {
return;
}
let upper_left = upper_left!(area);
let bottom_right = bottom_right!(area);
if !grid.ascii_drawing {
for x in get_x(upper_left)..get_x(bottom_right) {
grid[(x, get_y(upper_left))].set_ch(HORZ_BOUNDARY);
grid[(x, get_y(bottom_right))].set_ch(HORZ_BOUNDARY);
grid[(x, get_y(bottom_right))].set_fg(Color::Byte(240));
}
for y in get_y(upper_left)..get_y(bottom_right) {
grid[(get_x(upper_left), y)].set_ch(VERT_BOUNDARY);
grid[(get_x(bottom_right), y)].set_ch(VERT_BOUNDARY);
grid[(get_x(bottom_right), y)].set_fg(Color::Byte(240));
}
set_and_join_box(grid, upper_left, BoxBoundary::Horizontal);
set_and_join_box(
grid,
set_x(upper_left, get_x(bottom_right)),
BoxBoundary::Horizontal,
);
set_and_join_box(
grid,
set_y(upper_left, get_y(bottom_right)),
BoxBoundary::Vertical,
);
set_and_join_box(grid, bottom_right, BoxBoundary::Vertical);
}
}

View File

@ -1758,24 +1758,6 @@ pub fn clear_area(grid: &mut CellBuffer, area: Area) {
}
}
pub fn center_area(area: Area, (width, height): (usize, usize)) -> Area {
let mid_x = { std::cmp::max(width!(area) / 2, width / 2) - width / 2 };
let mid_y = { std::cmp::max(height!(area) / 2, height / 2) - height / 2 };
let (upper_x, upper_y) = upper_left!(area);
let (max_x, max_y) = bottom_right!(area);
(
(
std::cmp::min(max_x, upper_x + mid_x),
std::cmp::min(max_y, upper_y + mid_y),
),
(
std::cmp::min(max_x, upper_x + mid_x + width),
std::cmp::min(max_y, upper_y + mid_y + height),
),
)
}
pub mod ansi {
//! Create a `CellBuffer` from a string slice containing ANSI escape codes.
use super::{Cell, CellBuffer, Color};
@ -2065,3 +2047,374 @@ impl RowIterator {
}
}
}
pub use boundaries::create_box;
pub mod boundaries {
use super::*;
/// The upper and lower boundary char.
pub(crate) const HORZ_BOUNDARY: char = '─';
/// The left and right boundary char.
pub(crate) const VERT_BOUNDARY: char = 'β”‚';
/// The top-left corner
pub(crate) const _TOP_LEFT_CORNER: char = 'β”Œ';
/// The top-right corner
pub(crate) const _TOP_RIGHT_CORNER: char = '┐';
/// The bottom-left corner
pub(crate) const _BOTTOM_LEFT_CORNER: char = 'β””';
/// The bottom-right corner
pub(crate) const _BOTTOM_RIGHT_CORNER: char = 'β”˜';
pub(crate) const LIGHT_VERTICAL_AND_RIGHT: char = 'β”œ';
pub(crate) const _LIGHT_VERTICAL_AND_LEFT: char = '─';
pub(crate) const LIGHT_DOWN_AND_HORIZONTAL: char = '┬';
pub(crate) const LIGHT_UP_AND_HORIZONTAL: char = 'β”΄';
pub(crate) const _DOUBLE_DOWN_AND_RIGHT: char = 'β•”';
pub(crate) const _DOUBLE_DOWN_AND_LEFT: char = 'β•—';
pub(crate) const _DOUBLE_UP_AND_LEFT: char = '╝';
pub(crate) const _DOUBLE_UP_AND_RIGHT: char = 'β•š';
fn bin_to_ch(b: u32) -> char {
match b {
0b0001 => 'β•Ά',
0b0010 => 'β•΅',
0b0011 => 'β””',
0b0100 => 'β•΄',
0b0101 => '─',
0b0110 => 'β”˜',
0b0111 => 'β”΄',
0b1000 => 'β•·',
0b1001 => 'β”Œ',
0b1010 => 'β”‚',
0b1011 => 'β”œ',
0b1100 => '┐',
0b1101 => '┬',
0b1110 => '─',
0b1111 => 'β”Ό',
_ => unsafe { std::hint::unreachable_unchecked() },
}
}
fn ch_to_bin(ch: char) -> Option<u32> {
match ch {
'β””' => Some(0b0011),
'─' => Some(0b0101),
'β”˜' => Some(0b0110),
'β”΄' => Some(0b0111),
'β”Œ' => Some(0b1001),
'β”‚' => Some(0b1010),
'β”œ' => Some(0b1011),
'┐' => Some(0b1100),
'┬' => Some(0b1101),
'─' => Some(0b1110),
'β”Ό' => Some(0b1111),
'β•·' => Some(0b1000),
'β•΅' => Some(0b0010),
'β•΄' => Some(0b0100),
'β•Ά' => Some(0b0001),
_ => None,
}
}
#[allow(clippy::never_loop)]
fn set_and_join_vert(grid: &mut CellBuffer, idx: Pos) -> u32 {
let (x, y) = idx;
let mut bin_set = 0b1010;
/* Check left side
*
* 1
* -> 2 β”‚ 0
* 3
*/
loop {
if x > 0 {
if let Some(cell) = grid.get_mut(x - 1, y) {
if let Some(adj) = ch_to_bin(cell.ch()) {
if (adj & 0b0001) > 0 {
bin_set |= 0b0100;
break;
} else if adj == 0b0100 {
cell.set_ch(bin_to_ch(0b0101));
cell.set_fg(Color::Byte(240));
bin_set |= 0b0100;
break;
}
}
}
}
bin_set &= 0b1011;
break;
}
/* Check right side
*
* 1
* 2 β”‚ 0 <-
* 3
*/
loop {
if let Some(cell) = grid.get_mut(x + 1, y) {
if let Some(adj) = ch_to_bin(cell.ch()) {
if (adj & 0b0100) > 0 {
bin_set |= 0b0001;
break;
}
}
}
bin_set &= 0b1110;
break;
}
/* Set upper side
*
* 1 <-
* 2 β”‚ 0
* 3
*/
loop {
if y > 0 {
if let Some(cell) = grid.get_mut(x, y - 1) {
if let Some(adj) = ch_to_bin(cell.ch()) {
cell.set_ch(bin_to_ch(adj | 0b1000));
cell.set_fg(Color::Byte(240));
} else {
bin_set &= 0b1101;
}
}
}
break;
}
/* Set bottom side
*
* 1
* 2 β”‚ 0
* 3 <-
*/
loop {
if let Some(cell) = grid.get_mut(x, y + 1) {
if let Some(adj) = ch_to_bin(cell.ch()) {
cell.set_ch(bin_to_ch(adj | 0b0010));
cell.set_fg(Color::Byte(240));
} else {
bin_set &= 0b0111;
}
}
break;
}
if bin_set == 0 {
bin_set = 0b1010;
}
bin_set
}
#[allow(clippy::never_loop)]
fn set_and_join_horz(grid: &mut CellBuffer, idx: Pos) -> u32 {
let (x, y) = idx;
let mut bin_set = 0b0101;
/* Check upper side
*
* 1 <-
* 2 ─ 0
* 3
*/
loop {
if y > 0 {
if let Some(cell) = grid.get_mut(x, y - 1) {
if let Some(adj) = ch_to_bin(cell.ch()) {
if (adj & 0b1000) > 0 {
bin_set |= 0b0010;
break;
} else if adj == 0b0010 {
bin_set |= 0b0010;
cell.set_ch(bin_to_ch(0b1010));
cell.set_fg(Color::Byte(240));
break;
}
}
}
}
bin_set &= 0b1101;
break;
}
/* Check bottom side
*
* 1
* 2 ─ 0
* 3 <-
*/
loop {
if let Some(cell) = grid.get_mut(x, y + 1) {
if let Some(adj) = ch_to_bin(cell.ch()) {
if (adj & 0b0010) > 0 {
bin_set |= 0b1000;
break;
} else if adj == 0b1000 {
bin_set |= 0b1000;
cell.set_ch(bin_to_ch(0b1010));
cell.set_fg(Color::Byte(240));
break;
}
}
}
bin_set &= 0b0111;
break;
}
/* Set left side
*
* 1
* -> 2 ─ 0
* 3
*/
loop {
if x > 0 {
if let Some(cell) = grid.get_mut(x - 1, y) {
if let Some(adj) = ch_to_bin(cell.ch()) {
cell.set_ch(bin_to_ch(adj | 0b0001));
cell.set_fg(Color::Byte(240));
} else {
bin_set &= 0b1011;
}
}
}
break;
}
/* Set right side
*
* 1
* 2 ─ 0 <-
* 3
*/
loop {
if let Some(cell) = grid.get_mut(x + 1, y) {
if let Some(adj) = ch_to_bin(cell.ch()) {
cell.set_ch(bin_to_ch(adj | 0b0100));
cell.set_fg(Color::Byte(240));
} else {
bin_set &= 0b1110;
}
}
break;
}
if bin_set == 0 {
bin_set = 0b0101;
}
bin_set
}
pub(crate) enum BoxBoundary {
Horizontal,
Vertical,
}
pub(crate) fn set_and_join_box(grid: &mut CellBuffer, idx: Pos, ch: BoxBoundary) {
/* Connected sides:
*
* 1
* 2 c 0
* 3
*
* #3210
* 0b____
*/
if grid.ascii_drawing {
grid[idx].set_ch(match ch {
BoxBoundary::Vertical => '|',
BoxBoundary::Horizontal => '-',
});
grid[idx].set_fg(Color::Byte(240));
return;
}
let bin_set = match ch {
BoxBoundary::Vertical => set_and_join_vert(grid, idx),
BoxBoundary::Horizontal => set_and_join_horz(grid, idx),
};
grid[idx].set_ch(bin_to_ch(bin_set));
grid[idx].set_fg(Color::Byte(240));
}
/// Puts boundaries in `area`.
/// Returns the inner area of the created box.
pub fn create_box(grid: &mut CellBuffer, area: Area) -> Area {
if !is_valid_area!(area) {
return ((0, 0), (0, 0));
}
let upper_left = upper_left!(area);
let bottom_right = bottom_right!(area);
if !grid.ascii_drawing {
for x in get_x(upper_left)..get_x(bottom_right) {
grid[(x, get_y(upper_left))]
.set_ch(HORZ_BOUNDARY)
.set_fg(Color::Byte(240));
grid[(x, get_y(bottom_right))]
.set_ch(HORZ_BOUNDARY)
.set_fg(Color::Byte(240));
}
for y in get_y(upper_left)..get_y(bottom_right) {
grid[(get_x(upper_left), y)]
.set_ch(VERT_BOUNDARY)
.set_fg(Color::Byte(240));
grid[(get_x(bottom_right), y)]
.set_ch(VERT_BOUNDARY)
.set_fg(Color::Byte(240));
}
set_and_join_box(grid, upper_left, BoxBoundary::Horizontal);
set_and_join_box(
grid,
set_x(upper_left, get_x(bottom_right)),
BoxBoundary::Horizontal,
);
set_and_join_box(
grid,
set_y(upper_left, get_y(bottom_right)),
BoxBoundary::Vertical,
);
set_and_join_box(grid, bottom_right, BoxBoundary::Vertical);
}
(
(
std::cmp::min(
get_x(upper_left) + 2,
std::cmp::min(get_x(upper_left) + 1, get_x(bottom_right)),
),
std::cmp::min(
get_y(upper_left) + 2,
std::cmp::min(get_y(upper_left) + 1, get_y(bottom_right)),
),
),
(
std::cmp::max(
get_x(bottom_right).saturating_sub(2),
std::cmp::max(get_x(bottom_right).saturating_sub(1), get_x(upper_left)),
),
std::cmp::max(
get_y(bottom_right).saturating_sub(2),
std::cmp::max(get_y(bottom_right).saturating_sub(1), get_y(upper_left)),
),
),
)
}
}

View File

@ -137,3 +137,47 @@ macro_rules! is_valid_area {
!(get_y(upper_left) > get_y(bottom_right) || get_x(upper_left) > get_x(bottom_right))
}};
}
/// Place box given by `(width, height)` in center of `area`
pub fn center_area(area: Area, (width, height): (usize, usize)) -> Area {
let mid_x = { std::cmp::max(width!(area) / 2, width / 2) - width / 2 };
let mid_y = { std::cmp::max(height!(area) / 2, height / 2) - height / 2 };
let (upper_x, upper_y) = upper_left!(area);
let (max_x, max_y) = bottom_right!(area);
(
(
std::cmp::min(max_x, upper_x + mid_x),
std::cmp::min(max_y, upper_y + mid_y),
),
(
std::cmp::min(max_x, upper_x + mid_x + width),
std::cmp::min(max_y, upper_y + mid_y + height),
),
)
}
/// Place box given by `(width, height)` in corner of `area`
pub fn place_in_area(area: Area, (width, height): (usize, usize), upper: bool, left: bool) -> Area {
let (upper_x, upper_y) = upper_left!(area);
let (max_x, max_y) = bottom_right!(area);
let x = if upper {
upper_x + 2
} else {
max_x.saturating_sub(2).saturating_sub(width)
};
let y = if left {
upper_y + 2
} else {
max_y.saturating_sub(2).saturating_sub(height)
};
(
(std::cmp::min(x, max_x), std::cmp::min(y, max_y)),
(
std::cmp::min(x + width, max_x),
std::cmp::min(y + height, max_y),
),
)
}