1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
use crossterm::Terminal;

pub trait AreaContent {
    fn height() -> u16;
}

/// a part of a screen
#[derive(Debug, PartialEq, Eq)]
pub struct Area {
    pub left: u16,
    pub top: u16,
    pub width: u16,
    pub height: u16,
}

impl Area {

    /// build a new area. You'll need to set the position and size
    /// before you can use it
    pub fn uninitialized() -> Area {
        Area { left: 0, top:0, height:1, width:5 } // width can't be less than 5
    }

    /// build a new area.
    pub fn new(
        left: u16,
        top: u16,
        width: u16,
        height: u16,
    ) -> Area {
        assert!(width > 4);
        Area {
            left,
            top,
            width,
            height,
        }
    }

    /// build an area covering the whole terminal
    pub fn full_screen() -> Area {
        let (width, height) = terminal_size();
        Area {
            left: 0,
            top: 0,
            width,
            height,
        }
    }

    pub fn pad(&mut self, dx: u16, dy: u16) {
        // this will crash if padding is too big. feature?
        self.left += dx;
        self.top += dy;
        self.width -= 2*dx;
        self.height -= 2*dy;
    }

    // return an option which when filled contains
    //  a tupple with the top and bottom of the vertical
    //  scrollbar. Return none when the content fits
    //  the available space.
    pub fn scrollbar(
        &self,
        scroll: i32, // 0 for no scroll, positive if scrolled
        content_height: i32,
    ) -> Option<(u16, u16)> {
        let h = self.height as i32;
        if content_height <= h {
            return None;
        }
        let sbh = h * h / content_height;
        let sc = scroll * h / content_height;
        Some((sc as u16, (sc + sbh + 1).min(h+1) as u16))
    }
}

/// return a (width, height) with the dimensions of the available
/// terminal in characters.
pub fn terminal_size() -> (u16, u16) {
    let (w, h) = Terminal::new().terminal_size();
    // there's a bug in crossterm 0.9.6. It reports a size smaller by
    //  one in both directions
    (w + 1, h + 1)
}