Oregami
Repositories/oxedyne/fe2o3

oxedyne/fe2o3/fe2o3_tui/src/lib_tui/draw/scrollbars.rs

5.8 KiB, 7 runs

created by r1870400018:1181, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1use crate::lib_tui::{
2 style::Style,
3 render::{
4 Drawable,
5 Drawer,
6 Renderer,
7 Sink,
8 When,
9 },
10};
11
12use oxedyne_fe2o3_core::prelude::*;
13use oxedyne_fe2o3_geom::{
14 dim::{
15 Coord,
16 Dim,
17 },
18};
19
20
21#[derive(Clone, Debug, Default)]
22pub struct ScrollBarsConfig {
23 pub style: Style,
24 pub horiz: char,
25 pub vert: char,
26 pub corner: Option<char>,
27 pub always: bool,
28}
29
30impl ScrollBarsConfig {
31 pub fn horiz(&self) -> String { self.horiz.to_string() }
32 pub fn vert(&self) -> String { self.vert.to_string() }
33}
34
35///```ignore
36///
37/// text extent
38/// +--------------------------------------+
39/// | ^ ^ |
40/// | | | |
41/// | | start | extent |
42/// | | | |
43/// | v text view | |
44/// | +----------+ | |
45/// | | ^ | | |
46/// |<----->|<--+----->|<----+------------>|
47/// | start | | len | | end |
48/// | | | | | |
49/// | | | len | | |
50/// | | v | | |
51/// | +----------+ | |
52/// | ^ | |
53/// | | | |
54/// |<----------+------------+------------>|
55/// | | | extent |
56/// | | | |
57/// | | end | |
58/// | | | |
59/// | | | |
60/// | | | |
61/// | v v |
62/// +--------------------------------------+
63///
64///```
65#[derive(Clone, Debug)]
66pub struct TextViewDim {
67 pub start: Dim,
68 pub len: Dim,
69 pub end: Dim,
70 pub extent: Dim,
71}
72
73impl TextViewDim {
74 pub fn new(
75 start: Dim,
76 len: Dim,
77 end: Dim,
78 )
79 -> Self
80 {
81 Self {
82 start,
83 len,
84 end,
85 extent: start + len + end,
86 }
87 }
88}
89
90#[derive(Clone, Debug)]
91pub struct ScrollBar {
92 pub top_left: Coord, // Terminal coordinates of the start of the scroll bar.
93 pub scroll_len: Dim, // Length of whole scrollbar structure in terminal.
94 pub tview: TextViewDim, // Text view lengths in the scrollbar direction.
95}
96
97impl ScrollBar {
98
99 /// Scale the text view metrics to the terminal coordinates.
100 pub fn term_lengths(&self) -> Outcome<(Dim, Dim, Dim)> {
101
102 let mut len_start = try_div!(self.tview.start * self.scroll_len, self.tview.extent);
103 let len_bar =
104 try_div!(self.tview.len.min(self.tview.extent) * self.scroll_len, self.tview.extent);
105 let mut len_end = self.scroll_len - len_start - len_bar;
106
107 // The scaling can result in the bar not appearing right up against the start or end when
108 // it should, so perform a correction if necessary.
109 if self.tview.start == Dim(0) {
110 while len_start > Dim(0) {
111 len_start = len_start - 1;
112 len_end = len_end + 1;
113 }
114 }
115 if self.tview.end == Dim(0) {
116 while len_end > Dim(0) {
117 len_end = len_end - 1;
118 len_start = len_start + 1;
119 }
120 }
121
122 Ok((len_start, len_bar, len_end))
123 }
124}
125
126#[derive(Clone, Debug)]
127pub struct ScrollBars {
128 pub cfg: ScrollBarsConfig,
129 pub x: Option<ScrollBar>,
130 pub y: Option<ScrollBar>,
131}
132
133impl Drawable for ScrollBars {
134 fn render<S: Sink, R: Renderer<S>>(
135 &mut self,
136 drawer: &mut Drawer<S, R>,
137 when: When,
138 )
139 -> Outcome<()>
140 {
141 res!(self.cfg.style.render(drawer, When::Later));
142 if let Some(bar) = &self.x {
143 let (len_start, len_bar, len_end) = res!(bar.term_lengths());
144 res!(drawer.rend.set_cursor(bar.top_left, When::Later));
145 res!(drawer.rend.print(&" ".to_string().repeat(*len_start), When::Later));
146 res!(drawer.rend.print(&self.cfg.horiz().repeat(*len_bar), When::Later));
147 res!(drawer.rend.print(&" ".to_string().repeat(*len_end), When::Later));
148 if let Some(corner) = &self.cfg.corner {
149 res!(drawer.rend.print(&corner.to_string(), When::Later));
150 }
151 }
152 if let Some(bar) = &self.y {
153 let (len_start, len_bar, len_end) = res!(bar.term_lengths());
154 let mut j = bar.top_left.y;
155 let mut count = len_start;
156 while count > Dim(0) {
157 res!(drawer.rend.set_cursor(Coord::new((bar.top_left.x, j)), When::Later));
158 res!(drawer.rend.print(&" ".to_string(), When::Later));
159 j += 1;
160 count -= 1;
161 }
162 count = len_bar;
163 while count > Dim(0) {
164 res!(drawer.rend.set_cursor(Coord::new((bar.top_left.x, j)), When::Later));
165 res!(drawer.rend.print(&self.cfg.vert(), When::Later));
166 j += 1;
167 count -= 1;
168 }
169 count = len_end;
170 while count > Dim(0) {
171 res!(drawer.rend.set_cursor(Coord::new((bar.top_left.x, j)), When::Later));
172 res!(drawer.rend.print(&" ".to_string(), When::Later));
173 j += 1;
174 count -= 1;
175 }
176 if let Some(corner) = &self.cfg.corner {
177 res!(drawer.rend.set_cursor(Coord::new((bar.top_left.x, j)), When::Later));
178 res!(drawer.rend.print(&corner.to_string(), When::Later));
179 }
180 }
181 res!(drawer.rend.reset_style(When::Later));
182
183 if when == When::Now {
184 res!(drawer.rend.flush());
185 }
186 Ok(())
187 }
188}