pub struct Rect {
pub x: u16,
pub y: u16,
pub width: u16,
pub height: u16,
}
Expand description
A Rectangular area.
A simple rectangle used in the computation of the layout and to give widgets a hint about the area they are supposed to render to.
Fields§
§x: u16
The x coordinate of the top left corner of the Rect
.
y: u16
The y coordinate of the top left corner of the Rect
.
width: u16
The width of the Rect
.
height: u16
The height of the Rect
.
Implementations§
Source§impl Rect
impl Rect
Sourcepub const fn new(x: u16, y: u16, width: u16, height: u16) -> Self
pub const fn new(x: u16, y: u16, width: u16, height: u16) -> Self
Creates a new Rect
, with width and height limited to keep both bounds within u16
.
If the width or height would cause the right or bottom coordinate to be larger than the
maximum value of u16
, the width or height will be clamped to keep the right or bottom
coordinate within u16
.
§Examples
use ratatui::layout::Rect;
let rect = Rect::new(1, 2, 3, 4);
Examples found in repository?
64 const fn new() -> Self {
65 Self {
66 exit: false,
67 x: 0.0,
68 y: 0.0,
69 ball: Circle {
70 x: 20.0,
71 y: 40.0,
72 radius: 10.0,
73 color: Color::Yellow,
74 },
75 playground: Rect::new(10, 10, 200, 100),
76 vx: 1.0,
77 vy: 1.0,
78 tick_count: 0,
79 marker: Marker::Dot,
80 points: vec![],
81 is_drawing: false,
82 }
83 }
More examples
43fn main() -> Result<()> {
44 color_eyre::install()?;
45 // this size is to match the size of the terminal when running the demo
46 // using vhs in a 1280x640 sized window (github social preview size)
47 let viewport = Viewport::Fixed(Rect::new(0, 0, 81, 18));
48 let terminal = ratatui::init_with_options(TerminalOptions { viewport });
49 execute!(stdout(), EnterAlternateScreen).expect("failed to enter alternate screen");
50 let app_result = App::default().run(terminal);
51 execute!(stdout(), LeaveAlternateScreen).expect("failed to leave alternate screen");
52 ratatui::restore();
53 app_result
54}
307 fn render_demo(self, area: Rect, buf: &mut Buffer) -> bool {
308 // render demo content into a separate buffer so all examples fit we add an extra
309 // area.height to make sure the last example is fully visible even when the scroll offset is
310 // at the max
311 let height = example_height();
312 let demo_area = Rect::new(0, 0, area.width, height);
313 let mut demo_buf = Buffer::empty(demo_area);
314
315 let scrollbar_needed = self.scroll_offset != 0 || height > area.height;
316 let content_area = if scrollbar_needed {
317 Rect {
318 width: demo_area.width - 1,
319 ..demo_area
320 }
321 } else {
322 demo_area
323 };
324
325 let mut spacing = self.spacing;
326 self.selected_tab
327 .render(content_area, &mut demo_buf, &mut spacing);
328
329 let visible_content = demo_buf
330 .content
331 .into_iter()
332 .skip((area.width * self.scroll_offset) as usize)
333 .take(area.area() as usize);
334 for (i, cell) in visible_content.enumerate() {
335 let x = i as u16 % area.width;
336 let y = i as u16 / area.width;
337 buf[(area.x + x, area.y + y)] = cell;
338 }
339
340 if scrollbar_needed {
341 let area = area.intersection(buf.area);
342 let mut state = ScrollbarState::new(max_scroll_offset() as usize)
343 .position(self.scroll_offset as usize);
344 Scrollbar::new(ScrollbarOrientation::VerticalRight).render(area, buf, &mut state);
345 }
346 scrollbar_needed
347 }
Sourcepub const fn area(self) -> u32
pub const fn area(self) -> u32
The area of the Rect
. If the area is larger than the maximum value of u16
, it will be
clamped to u16::MAX
.
Examples found in repository?
307 fn render_demo(self, area: Rect, buf: &mut Buffer) -> bool {
308 // render demo content into a separate buffer so all examples fit we add an extra
309 // area.height to make sure the last example is fully visible even when the scroll offset is
310 // at the max
311 let height = example_height();
312 let demo_area = Rect::new(0, 0, area.width, height);
313 let mut demo_buf = Buffer::empty(demo_area);
314
315 let scrollbar_needed = self.scroll_offset != 0 || height > area.height;
316 let content_area = if scrollbar_needed {
317 Rect {
318 width: demo_area.width - 1,
319 ..demo_area
320 }
321 } else {
322 demo_area
323 };
324
325 let mut spacing = self.spacing;
326 self.selected_tab
327 .render(content_area, &mut demo_buf, &mut spacing);
328
329 let visible_content = demo_buf
330 .content
331 .into_iter()
332 .skip((area.width * self.scroll_offset) as usize)
333 .take(area.area() as usize);
334 for (i, cell) in visible_content.enumerate() {
335 let x = i as u16 % area.width;
336 let y = i as u16 / area.width;
337 buf[(area.x + x, area.y + y)] = cell;
338 }
339
340 if scrollbar_needed {
341 let area = area.intersection(buf.area);
342 let mut state = ScrollbarState::new(max_scroll_offset() as usize)
343 .position(self.scroll_offset as usize);
344 Scrollbar::new(ScrollbarOrientation::VerticalRight).render(area, buf, &mut state);
345 }
346 scrollbar_needed
347 }
Sourcepub const fn left(self) -> u16
pub const fn left(self) -> u16
Returns the left coordinate of the Rect
.
Examples found in repository?
More examples
13 fn render(self, area: Rect, buf: &mut Buffer) {
14 for (yi, y) in (area.top()..area.bottom()).enumerate() {
15 let value = f32::from(area.height) - yi as f32;
16 let value_fg = value / f32::from(area.height);
17 let value_bg = (value - 0.5) / f32::from(area.height);
18 for (xi, x) in (area.left()..area.right()).enumerate() {
19 let hue = xi as f32 * 360.0 / f32::from(area.width);
20 let fg = color_from_oklab(hue, Okhsv::max_saturation(), value_fg);
21 let bg = color_from_oklab(hue, Okhsv::max_saturation(), value_bg);
22 buf[(x, y)].set_char('▀').set_fg(fg).set_bg(bg);
23 }
24 }
25 }
206 fn render(self, area: Rect, buf: &mut Buffer) {
207 self.setup_colors(area);
208 let colors = &self.colors;
209 for (xi, x) in (area.left()..area.right()).enumerate() {
210 // animate the colors by shifting the x index by the frame number
211 let xi = (xi + self.frame_count) % (area.width as usize);
212 for (yi, y) in (area.top()..area.bottom()).enumerate() {
213 // render a half block character for each row of pixels with the foreground color
214 // set to the color of the pixel and the background color set to the color of the
215 // pixel below it
216 let fg = colors[yi * 2][xi];
217 let bg = colors[yi * 2 + 1][xi];
218 buf[Position::new(x, y)].set_char('▀').set_fg(fg).set_bg(bg);
219 }
220 }
221 self.frame_count += 1;
222 }
132 fn on_tick(&mut self) {
133 self.tick_count += 1;
134 // only change marker every 180 ticks (3s) to avoid stroboscopic effect
135 if (self.tick_count % 180) == 0 {
136 self.marker = match self.marker {
137 Marker::Dot => Marker::Braille,
138 Marker::Braille => Marker::Block,
139 Marker::Block => Marker::HalfBlock,
140 Marker::HalfBlock => Marker::Bar,
141 Marker::Bar => Marker::Dot,
142 };
143 }
144 // bounce the ball by flipping the velocity vector
145 let ball = &self.ball;
146 let playground = self.playground;
147 if ball.x - ball.radius < f64::from(playground.left())
148 || ball.x + ball.radius > f64::from(playground.right())
149 {
150 self.vx = -self.vx;
151 }
152 if ball.y - ball.radius < f64::from(playground.top())
153 || ball.y + ball.radius > f64::from(playground.bottom())
154 {
155 self.vy = -self.vy;
156 }
157
158 self.ball.x += self.vx;
159 self.ball.y += self.vy;
160 }
161
162 fn draw(&self, frame: &mut Frame) {
163 let horizontal =
164 Layout::horizontal([Constraint::Percentage(50), Constraint::Percentage(50)]);
165 let vertical = Layout::vertical([Constraint::Percentage(50), Constraint::Percentage(50)]);
166 let [left, right] = horizontal.areas(frame.area());
167 let [draw, map] = vertical.areas(left);
168 let [pong, boxes] = vertical.areas(right);
169
170 frame.render_widget(self.map_canvas(), map);
171 frame.render_widget(self.draw_canvas(draw), draw);
172 frame.render_widget(self.pong_canvas(), pong);
173 frame.render_widget(self.boxes_canvas(boxes), boxes);
174 }
175
176 fn map_canvas(&self) -> impl Widget + '_ {
177 Canvas::default()
178 .block(Block::bordered().title("World"))
179 .marker(self.marker)
180 .paint(|ctx| {
181 ctx.draw(&Map {
182 color: Color::Green,
183 resolution: MapResolution::High,
184 });
185 ctx.print(self.x, -self.y, "You are here".yellow());
186 })
187 .x_bounds([-180.0, 180.0])
188 .y_bounds([-90.0, 90.0])
189 }
190
191 fn draw_canvas(&self, area: Rect) -> impl Widget + '_ {
192 Canvas::default()
193 .block(Block::bordered().title("Draw here"))
194 .marker(self.marker)
195 .x_bounds([0.0, f64::from(area.width)])
196 .y_bounds([0.0, f64::from(area.height)])
197 .paint(move |ctx| {
198 let points = self
199 .points
200 .iter()
201 .map(|p| {
202 (
203 f64::from(p.x) - f64::from(area.left()),
204 f64::from(area.bottom()) - f64::from(p.y),
205 )
206 })
207 .collect_vec();
208 ctx.draw(&Points {
209 coords: &points,
210 color: Color::White,
211 });
212 })
213 }
42fn drip(frame_count: usize, area: Rect, buf: &mut Buffer) {
43 // a seeded rng as we have to move the same random pixels each frame
44 let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(10);
45 let ramp_frames = 450;
46 let fractional_speed = frame_count as f64 / f64::from(ramp_frames);
47 let variable_speed = DRIP_SPEED as f64 * fractional_speed * fractional_speed * fractional_speed;
48 let pixel_count = (frame_count as f64 * variable_speed).floor() as usize;
49 for _ in 0..pixel_count {
50 let src_x = rng.gen_range(0..area.width);
51 let src_y = rng.gen_range(1..area.height - 2);
52 let src = buf[(src_x, src_y)].clone();
53 // 1% of the time, move a blank or pixel (10:1) to the top line of the screen
54 if rng.gen_ratio(1, 100) {
55 let dest_x = rng
56 .gen_range(src_x.saturating_sub(5)..src_x.saturating_add(5))
57 .clamp(area.left(), area.right() - 1);
58 let dest_y = area.top() + 1;
59
60 let dest = &mut buf[(dest_x, dest_y)];
61 // copy the cell to the new location about 1/10 of the time blank out the cell the rest
62 // of the time. This has the effect of gradually removing the pixels from the screen.
63 if rng.gen_ratio(1, 10) {
64 *dest = src;
65 } else {
66 dest.reset();
67 }
68 } else {
69 // move the pixel down one row
70 let dest_x = src_x;
71 let dest_y = src_y.saturating_add(1).min(area.bottom() - 2);
72 // copy the cell to the new location
73 buf[(dest_x, dest_y)] = src;
74 }
75 }
76}
105pub fn render_logo(selected_row: usize, area: Rect, buf: &mut Buffer) {
106 let eye_color = if selected_row % 2 == 0 {
107 THEME.logo.rat_eye
108 } else {
109 THEME.logo.rat_eye_alt
110 };
111 let area = area.inner(Margin {
112 vertical: 0,
113 horizontal: 2,
114 });
115 for (y, (line1, line2)) in RATATUI_LOGO.iter().tuples().enumerate() {
116 for (x, (ch1, ch2)) in line1.chars().zip(line2.chars()).enumerate() {
117 let x = area.left() + x as u16;
118 let y = area.top() + y as u16;
119 let cell = &mut buf[(x, y)];
120 let rat_color = THEME.logo.rat;
121 let term_color = THEME.logo.term;
122 match (ch1, ch2) {
123 ('█', '█') => {
124 cell.set_char('█');
125 cell.fg = rat_color;
126 cell.bg = rat_color;
127 }
128 ('█', ' ') => {
129 cell.set_char('▀');
130 cell.fg = rat_color;
131 }
132 (' ', '█') => {
133 cell.set_char('▄');
134 cell.fg = rat_color;
135 }
136 ('█', 'x') => {
137 cell.set_char('▀');
138 cell.fg = rat_color;
139 cell.bg = term_color;
140 }
141 ('x', '█') => {
142 cell.set_char('▄');
143 cell.fg = rat_color;
144 cell.bg = term_color;
145 }
146 ('x', 'x') => {
147 cell.set_char(' ');
148 cell.fg = term_color;
149 cell.bg = term_color;
150 }
151 ('█', 'e') => {
152 cell.set_char('▀');
153 cell.fg = rat_color;
154 cell.bg = eye_color;
155 }
156 ('e', '█') => {
157 cell.set_char('▄');
158 cell.fg = rat_color;
159 cell.bg = eye_color;
160 }
161 (_, _) => {}
162 };
163 }
164 }
165}
Sourcepub const fn right(self) -> u16
pub const fn right(self) -> u16
Returns the right coordinate of the Rect
. This is the first coordinate outside of the
Rect
.
If the right coordinate is larger than the maximum value of u16, it will be clamped to
u16::MAX
.
Examples found in repository?
237 fn render(self, area: Rect, buf: &mut Buffer) {
238 const WIDTH: u16 = 4;
239 let x = area.right() - WIDTH; // Align to the right
240 self.last_position = Position { x, y: area.y };
241 let size = Size::new(WIDTH, area.height);
242 let area = Rect::from((self.last_position, size));
243 fill(area, buf, "█", Color::Green);
244 }
245}
246
247/// Fill the area with the specified symbol and style.
248///
249/// This probably should be a method on the `Buffer` type, but it is defined here for simplicity.
250/// <https://github.com/ratatui-org/ratatui/issues/1146>
251fn fill<S: Into<Style>>(area: Rect, buf: &mut Buffer, symbol: &str, style: S) {
252 let style = style.into();
253 for y in area.top()..area.bottom() {
254 for x in area.left()..area.right() {
255 buf[(x, y)].set_symbol(symbol).set_style(style);
256 }
257 }
258}
More examples
13 fn render(self, area: Rect, buf: &mut Buffer) {
14 for (yi, y) in (area.top()..area.bottom()).enumerate() {
15 let value = f32::from(area.height) - yi as f32;
16 let value_fg = value / f32::from(area.height);
17 let value_bg = (value - 0.5) / f32::from(area.height);
18 for (xi, x) in (area.left()..area.right()).enumerate() {
19 let hue = xi as f32 * 360.0 / f32::from(area.width);
20 let fg = color_from_oklab(hue, Okhsv::max_saturation(), value_fg);
21 let bg = color_from_oklab(hue, Okhsv::max_saturation(), value_bg);
22 buf[(x, y)].set_char('▀').set_fg(fg).set_bg(bg);
23 }
24 }
25 }
206 fn render(self, area: Rect, buf: &mut Buffer) {
207 self.setup_colors(area);
208 let colors = &self.colors;
209 for (xi, x) in (area.left()..area.right()).enumerate() {
210 // animate the colors by shifting the x index by the frame number
211 let xi = (xi + self.frame_count) % (area.width as usize);
212 for (yi, y) in (area.top()..area.bottom()).enumerate() {
213 // render a half block character for each row of pixels with the foreground color
214 // set to the color of the pixel and the background color set to the color of the
215 // pixel below it
216 let fg = colors[yi * 2][xi];
217 let bg = colors[yi * 2 + 1][xi];
218 buf[Position::new(x, y)].set_char('▀').set_fg(fg).set_bg(bg);
219 }
220 }
221 self.frame_count += 1;
222 }
132 fn on_tick(&mut self) {
133 self.tick_count += 1;
134 // only change marker every 180 ticks (3s) to avoid stroboscopic effect
135 if (self.tick_count % 180) == 0 {
136 self.marker = match self.marker {
137 Marker::Dot => Marker::Braille,
138 Marker::Braille => Marker::Block,
139 Marker::Block => Marker::HalfBlock,
140 Marker::HalfBlock => Marker::Bar,
141 Marker::Bar => Marker::Dot,
142 };
143 }
144 // bounce the ball by flipping the velocity vector
145 let ball = &self.ball;
146 let playground = self.playground;
147 if ball.x - ball.radius < f64::from(playground.left())
148 || ball.x + ball.radius > f64::from(playground.right())
149 {
150 self.vx = -self.vx;
151 }
152 if ball.y - ball.radius < f64::from(playground.top())
153 || ball.y + ball.radius > f64::from(playground.bottom())
154 {
155 self.vy = -self.vy;
156 }
157
158 self.ball.x += self.vx;
159 self.ball.y += self.vy;
160 }
42fn drip(frame_count: usize, area: Rect, buf: &mut Buffer) {
43 // a seeded rng as we have to move the same random pixels each frame
44 let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(10);
45 let ramp_frames = 450;
46 let fractional_speed = frame_count as f64 / f64::from(ramp_frames);
47 let variable_speed = DRIP_SPEED as f64 * fractional_speed * fractional_speed * fractional_speed;
48 let pixel_count = (frame_count as f64 * variable_speed).floor() as usize;
49 for _ in 0..pixel_count {
50 let src_x = rng.gen_range(0..area.width);
51 let src_y = rng.gen_range(1..area.height - 2);
52 let src = buf[(src_x, src_y)].clone();
53 // 1% of the time, move a blank or pixel (10:1) to the top line of the screen
54 if rng.gen_ratio(1, 100) {
55 let dest_x = rng
56 .gen_range(src_x.saturating_sub(5)..src_x.saturating_add(5))
57 .clamp(area.left(), area.right() - 1);
58 let dest_y = area.top() + 1;
59
60 let dest = &mut buf[(dest_x, dest_y)];
61 // copy the cell to the new location about 1/10 of the time blank out the cell the rest
62 // of the time. This has the effect of gradually removing the pixels from the screen.
63 if rng.gen_ratio(1, 10) {
64 *dest = src;
65 } else {
66 dest.reset();
67 }
68 } else {
69 // move the pixel down one row
70 let dest_x = src_x;
71 let dest_y = src_y.saturating_add(1).min(area.bottom() - 2);
72 // copy the cell to the new location
73 buf[(dest_x, dest_y)] = src;
74 }
75 }
76}
Sourcepub const fn top(self) -> u16
pub const fn top(self) -> u16
Returns the top coordinate of the Rect
.
Examples found in repository?
More examples
13 fn render(self, area: Rect, buf: &mut Buffer) {
14 for (yi, y) in (area.top()..area.bottom()).enumerate() {
15 let value = f32::from(area.height) - yi as f32;
16 let value_fg = value / f32::from(area.height);
17 let value_bg = (value - 0.5) / f32::from(area.height);
18 for (xi, x) in (area.left()..area.right()).enumerate() {
19 let hue = xi as f32 * 360.0 / f32::from(area.width);
20 let fg = color_from_oklab(hue, Okhsv::max_saturation(), value_fg);
21 let bg = color_from_oklab(hue, Okhsv::max_saturation(), value_bg);
22 buf[(x, y)].set_char('▀').set_fg(fg).set_bg(bg);
23 }
24 }
25 }
206 fn render(self, area: Rect, buf: &mut Buffer) {
207 self.setup_colors(area);
208 let colors = &self.colors;
209 for (xi, x) in (area.left()..area.right()).enumerate() {
210 // animate the colors by shifting the x index by the frame number
211 let xi = (xi + self.frame_count) % (area.width as usize);
212 for (yi, y) in (area.top()..area.bottom()).enumerate() {
213 // render a half block character for each row of pixels with the foreground color
214 // set to the color of the pixel and the background color set to the color of the
215 // pixel below it
216 let fg = colors[yi * 2][xi];
217 let bg = colors[yi * 2 + 1][xi];
218 buf[Position::new(x, y)].set_char('▀').set_fg(fg).set_bg(bg);
219 }
220 }
221 self.frame_count += 1;
222 }
132 fn on_tick(&mut self) {
133 self.tick_count += 1;
134 // only change marker every 180 ticks (3s) to avoid stroboscopic effect
135 if (self.tick_count % 180) == 0 {
136 self.marker = match self.marker {
137 Marker::Dot => Marker::Braille,
138 Marker::Braille => Marker::Block,
139 Marker::Block => Marker::HalfBlock,
140 Marker::HalfBlock => Marker::Bar,
141 Marker::Bar => Marker::Dot,
142 };
143 }
144 // bounce the ball by flipping the velocity vector
145 let ball = &self.ball;
146 let playground = self.playground;
147 if ball.x - ball.radius < f64::from(playground.left())
148 || ball.x + ball.radius > f64::from(playground.right())
149 {
150 self.vx = -self.vx;
151 }
152 if ball.y - ball.radius < f64::from(playground.top())
153 || ball.y + ball.radius > f64::from(playground.bottom())
154 {
155 self.vy = -self.vy;
156 }
157
158 self.ball.x += self.vx;
159 self.ball.y += self.vy;
160 }
42fn drip(frame_count: usize, area: Rect, buf: &mut Buffer) {
43 // a seeded rng as we have to move the same random pixels each frame
44 let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(10);
45 let ramp_frames = 450;
46 let fractional_speed = frame_count as f64 / f64::from(ramp_frames);
47 let variable_speed = DRIP_SPEED as f64 * fractional_speed * fractional_speed * fractional_speed;
48 let pixel_count = (frame_count as f64 * variable_speed).floor() as usize;
49 for _ in 0..pixel_count {
50 let src_x = rng.gen_range(0..area.width);
51 let src_y = rng.gen_range(1..area.height - 2);
52 let src = buf[(src_x, src_y)].clone();
53 // 1% of the time, move a blank or pixel (10:1) to the top line of the screen
54 if rng.gen_ratio(1, 100) {
55 let dest_x = rng
56 .gen_range(src_x.saturating_sub(5)..src_x.saturating_add(5))
57 .clamp(area.left(), area.right() - 1);
58 let dest_y = area.top() + 1;
59
60 let dest = &mut buf[(dest_x, dest_y)];
61 // copy the cell to the new location about 1/10 of the time blank out the cell the rest
62 // of the time. This has the effect of gradually removing the pixels from the screen.
63 if rng.gen_ratio(1, 10) {
64 *dest = src;
65 } else {
66 dest.reset();
67 }
68 } else {
69 // move the pixel down one row
70 let dest_x = src_x;
71 let dest_y = src_y.saturating_add(1).min(area.bottom() - 2);
72 // copy the cell to the new location
73 buf[(dest_x, dest_y)] = src;
74 }
75 }
76}
105pub fn render_logo(selected_row: usize, area: Rect, buf: &mut Buffer) {
106 let eye_color = if selected_row % 2 == 0 {
107 THEME.logo.rat_eye
108 } else {
109 THEME.logo.rat_eye_alt
110 };
111 let area = area.inner(Margin {
112 vertical: 0,
113 horizontal: 2,
114 });
115 for (y, (line1, line2)) in RATATUI_LOGO.iter().tuples().enumerate() {
116 for (x, (ch1, ch2)) in line1.chars().zip(line2.chars()).enumerate() {
117 let x = area.left() + x as u16;
118 let y = area.top() + y as u16;
119 let cell = &mut buf[(x, y)];
120 let rat_color = THEME.logo.rat;
121 let term_color = THEME.logo.term;
122 match (ch1, ch2) {
123 ('█', '█') => {
124 cell.set_char('█');
125 cell.fg = rat_color;
126 cell.bg = rat_color;
127 }
128 ('█', ' ') => {
129 cell.set_char('▀');
130 cell.fg = rat_color;
131 }
132 (' ', '█') => {
133 cell.set_char('▄');
134 cell.fg = rat_color;
135 }
136 ('█', 'x') => {
137 cell.set_char('▀');
138 cell.fg = rat_color;
139 cell.bg = term_color;
140 }
141 ('x', '█') => {
142 cell.set_char('▄');
143 cell.fg = rat_color;
144 cell.bg = term_color;
145 }
146 ('x', 'x') => {
147 cell.set_char(' ');
148 cell.fg = term_color;
149 cell.bg = term_color;
150 }
151 ('█', 'e') => {
152 cell.set_char('▀');
153 cell.fg = rat_color;
154 cell.bg = eye_color;
155 }
156 ('e', '█') => {
157 cell.set_char('▄');
158 cell.fg = rat_color;
159 cell.bg = eye_color;
160 }
161 (_, _) => {}
162 };
163 }
164 }
165}
Sourcepub const fn bottom(self) -> u16
pub const fn bottom(self) -> u16
Returns the bottom coordinate of the Rect
. This is the first coordinate outside of the
Rect
.
If the bottom coordinate is larger than the maximum value of u16, it will be clamped to
u16::MAX
.
Examples found in repository?
More examples
13 fn render(self, area: Rect, buf: &mut Buffer) {
14 for (yi, y) in (area.top()..area.bottom()).enumerate() {
15 let value = f32::from(area.height) - yi as f32;
16 let value_fg = value / f32::from(area.height);
17 let value_bg = (value - 0.5) / f32::from(area.height);
18 for (xi, x) in (area.left()..area.right()).enumerate() {
19 let hue = xi as f32 * 360.0 / f32::from(area.width);
20 let fg = color_from_oklab(hue, Okhsv::max_saturation(), value_fg);
21 let bg = color_from_oklab(hue, Okhsv::max_saturation(), value_bg);
22 buf[(x, y)].set_char('▀').set_fg(fg).set_bg(bg);
23 }
24 }
25 }
206 fn render(self, area: Rect, buf: &mut Buffer) {
207 self.setup_colors(area);
208 let colors = &self.colors;
209 for (xi, x) in (area.left()..area.right()).enumerate() {
210 // animate the colors by shifting the x index by the frame number
211 let xi = (xi + self.frame_count) % (area.width as usize);
212 for (yi, y) in (area.top()..area.bottom()).enumerate() {
213 // render a half block character for each row of pixels with the foreground color
214 // set to the color of the pixel and the background color set to the color of the
215 // pixel below it
216 let fg = colors[yi * 2][xi];
217 let bg = colors[yi * 2 + 1][xi];
218 buf[Position::new(x, y)].set_char('▀').set_fg(fg).set_bg(bg);
219 }
220 }
221 self.frame_count += 1;
222 }
132 fn on_tick(&mut self) {
133 self.tick_count += 1;
134 // only change marker every 180 ticks (3s) to avoid stroboscopic effect
135 if (self.tick_count % 180) == 0 {
136 self.marker = match self.marker {
137 Marker::Dot => Marker::Braille,
138 Marker::Braille => Marker::Block,
139 Marker::Block => Marker::HalfBlock,
140 Marker::HalfBlock => Marker::Bar,
141 Marker::Bar => Marker::Dot,
142 };
143 }
144 // bounce the ball by flipping the velocity vector
145 let ball = &self.ball;
146 let playground = self.playground;
147 if ball.x - ball.radius < f64::from(playground.left())
148 || ball.x + ball.radius > f64::from(playground.right())
149 {
150 self.vx = -self.vx;
151 }
152 if ball.y - ball.radius < f64::from(playground.top())
153 || ball.y + ball.radius > f64::from(playground.bottom())
154 {
155 self.vy = -self.vy;
156 }
157
158 self.ball.x += self.vx;
159 self.ball.y += self.vy;
160 }
161
162 fn draw(&self, frame: &mut Frame) {
163 let horizontal =
164 Layout::horizontal([Constraint::Percentage(50), Constraint::Percentage(50)]);
165 let vertical = Layout::vertical([Constraint::Percentage(50), Constraint::Percentage(50)]);
166 let [left, right] = horizontal.areas(frame.area());
167 let [draw, map] = vertical.areas(left);
168 let [pong, boxes] = vertical.areas(right);
169
170 frame.render_widget(self.map_canvas(), map);
171 frame.render_widget(self.draw_canvas(draw), draw);
172 frame.render_widget(self.pong_canvas(), pong);
173 frame.render_widget(self.boxes_canvas(boxes), boxes);
174 }
175
176 fn map_canvas(&self) -> impl Widget + '_ {
177 Canvas::default()
178 .block(Block::bordered().title("World"))
179 .marker(self.marker)
180 .paint(|ctx| {
181 ctx.draw(&Map {
182 color: Color::Green,
183 resolution: MapResolution::High,
184 });
185 ctx.print(self.x, -self.y, "You are here".yellow());
186 })
187 .x_bounds([-180.0, 180.0])
188 .y_bounds([-90.0, 90.0])
189 }
190
191 fn draw_canvas(&self, area: Rect) -> impl Widget + '_ {
192 Canvas::default()
193 .block(Block::bordered().title("Draw here"))
194 .marker(self.marker)
195 .x_bounds([0.0, f64::from(area.width)])
196 .y_bounds([0.0, f64::from(area.height)])
197 .paint(move |ctx| {
198 let points = self
199 .points
200 .iter()
201 .map(|p| {
202 (
203 f64::from(p.x) - f64::from(area.left()),
204 f64::from(area.bottom()) - f64::from(p.y),
205 )
206 })
207 .collect_vec();
208 ctx.draw(&Points {
209 coords: &points,
210 color: Color::White,
211 });
212 })
213 }
42fn drip(frame_count: usize, area: Rect, buf: &mut Buffer) {
43 // a seeded rng as we have to move the same random pixels each frame
44 let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(10);
45 let ramp_frames = 450;
46 let fractional_speed = frame_count as f64 / f64::from(ramp_frames);
47 let variable_speed = DRIP_SPEED as f64 * fractional_speed * fractional_speed * fractional_speed;
48 let pixel_count = (frame_count as f64 * variable_speed).floor() as usize;
49 for _ in 0..pixel_count {
50 let src_x = rng.gen_range(0..area.width);
51 let src_y = rng.gen_range(1..area.height - 2);
52 let src = buf[(src_x, src_y)].clone();
53 // 1% of the time, move a blank or pixel (10:1) to the top line of the screen
54 if rng.gen_ratio(1, 100) {
55 let dest_x = rng
56 .gen_range(src_x.saturating_sub(5)..src_x.saturating_add(5))
57 .clamp(area.left(), area.right() - 1);
58 let dest_y = area.top() + 1;
59
60 let dest = &mut buf[(dest_x, dest_y)];
61 // copy the cell to the new location about 1/10 of the time blank out the cell the rest
62 // of the time. This has the effect of gradually removing the pixels from the screen.
63 if rng.gen_ratio(1, 10) {
64 *dest = src;
65 } else {
66 dest.reset();
67 }
68 } else {
69 // move the pixel down one row
70 let dest_x = src_x;
71 let dest_y = src_y.saturating_add(1).min(area.bottom() - 2);
72 // copy the cell to the new location
73 buf[(dest_x, dest_y)] = src;
74 }
75 }
76}
231fn draw(frame: &mut Frame, downloads: &Downloads) {
232 let area = frame.area();
233
234 let block = Block::new().title(Line::from("Progress").centered());
235 frame.render_widget(block, area);
236
237 let vertical = Layout::vertical([Constraint::Length(2), Constraint::Length(4)]).margin(1);
238 let horizontal = Layout::horizontal([Constraint::Percentage(20), Constraint::Percentage(80)]);
239 let [progress_area, main] = vertical.areas(area);
240 let [list_area, gauge_area] = horizontal.areas(main);
241
242 // total progress
243 let done = NUM_DOWNLOADS - downloads.pending.len() - downloads.in_progress.len();
244 #[allow(clippy::cast_precision_loss)]
245 let progress = LineGauge::default()
246 .filled_style(Style::default().fg(Color::Blue))
247 .label(format!("{done}/{NUM_DOWNLOADS}"))
248 .ratio(done as f64 / NUM_DOWNLOADS as f64);
249 frame.render_widget(progress, progress_area);
250
251 // in progress downloads
252 let items: Vec<ListItem> = downloads
253 .in_progress
254 .values()
255 .map(|download| {
256 ListItem::new(Line::from(vec![
257 Span::raw(symbols::DOT),
258 Span::styled(
259 format!(" download {:>2}", download.id),
260 Style::default()
261 .fg(Color::LightGreen)
262 .add_modifier(Modifier::BOLD),
263 ),
264 Span::raw(format!(
265 " ({}ms)",
266 download.started_at.elapsed().as_millis()
267 )),
268 ]))
269 })
270 .collect();
271 let list = List::new(items);
272 frame.render_widget(list, list_area);
273
274 #[allow(clippy::cast_possible_truncation)]
275 for (i, (_, download)) in downloads.in_progress.iter().enumerate() {
276 let gauge = Gauge::default()
277 .gauge_style(Style::default().fg(Color::Yellow))
278 .ratio(download.progress / 100.0);
279 if gauge_area.top().saturating_add(i as u16) > area.bottom() {
280 continue;
281 }
282 frame.render_widget(
283 gauge,
284 Rect {
285 x: gauge_area.left(),
286 y: gauge_area.top().saturating_add(i as u16),
287 width: gauge_area.width,
288 height: 1,
289 },
290 );
291 }
292}
Sourcepub const fn inner(self, margin: Margin) -> Self
pub const fn inner(self, margin: Margin) -> Self
Returns a new Rect
inside the current one, with the given margin on each side.
If the margin is larger than the Rect
, the returned Rect
will have no area.
Examples found in repository?
269 fn render_scrollbar(&mut self, frame: &mut Frame, area: Rect) {
270 frame.render_stateful_widget(
271 Scrollbar::default()
272 .orientation(ScrollbarOrientation::VerticalRight)
273 .begin_symbol(None)
274 .end_symbol(None),
275 area.inner(Margin {
276 vertical: 1,
277 horizontal: 1,
278 }),
279 &mut self.scroll_state,
280 );
281 }
More examples
69 fn render(self, area: Rect, buf: &mut Buffer) {
70 RgbSwatch.render(area, buf);
71 let area = area.inner(Margin {
72 vertical: 1,
73 horizontal: 2,
74 });
75 Clear.render(area, buf);
76 let vertical = Layout::vertical([Constraint::Length(5), Constraint::Min(0)]);
77 let [inbox, email] = vertical.areas(area);
78 render_inbox(self.row_index, inbox, buf);
79 render_email(self.row_index, email, buf);
80 }
34 fn render(self, area: Rect, buf: &mut Buffer) {
35 RgbSwatch.render(area, buf);
36 let area = area.inner(Margin {
37 vertical: 1,
38 horizontal: 2,
39 });
40 Clear.render(area, buf);
41 Block::new().style(THEME.content).render(area, buf);
42 let horizontal = Layout::horizontal([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)]);
43 let vertical = Layout::vertical([Constraint::Min(0), Constraint::Length(3)]);
44 let [left, map] = horizontal.areas(area);
45 let [hops, pings] = vertical.areas(left);
46
47 render_hops(self.row_index, hops, buf);
48 render_ping(self.row_index, pings, buf);
49 render_map(self.row_index, map, buf);
50 }
45fn draw(frame: &mut Frame) {
46 let area = frame.area().inner(Margin {
47 vertical: 1,
48 horizontal: 1,
49 });
50
51 let mut start = OffsetDateTime::now_local()
52 .unwrap()
53 .date()
54 .replace_month(Month::January)
55 .unwrap()
56 .replace_day(1)
57 .unwrap();
58
59 let list = make_dates(start.year());
60
61 let rows = Layout::vertical([Constraint::Ratio(1, 3); 3]).split(area);
62 let cols = rows.iter().flat_map(|row| {
63 Layout::horizontal([Constraint::Ratio(1, 4); 4])
64 .split(*row)
65 .to_vec()
66 });
67 for col in cols {
68 let cal = cals::get_cal(start.month(), start.year(), &list);
69 frame.render_widget(cal, col);
70 start = start.replace_month(start.month().next()).unwrap();
71 }
72}
70fn render_crate_description(area: Rect, buf: &mut Buffer) {
71 let area = area.inner(Margin {
72 vertical: 4,
73 horizontal: 2,
74 });
75 Clear.render(area, buf); // clear out the color swatches
76 Block::new().style(THEME.content).render(area, buf);
77 let area = area.inner(Margin {
78 vertical: 1,
79 horizontal: 2,
80 });
81 let text = "- cooking up terminal user interfaces -
82
83 Ratatui is a Rust crate that provides widgets (e.g. Paragraph, Table) and draws them to the \
84 screen efficiently every frame.";
85 Paragraph::new(text)
86 .style(THEME.description)
87 .block(
88 Block::new()
89 .title(" Ratatui ")
90 .title_alignment(Alignment::Center)
91 .borders(Borders::TOP)
92 .border_style(THEME.description_title)
93 .padding(Padding::new(0, 0, 0, 0)),
94 )
95 .wrap(Wrap { trim: true })
96 .scroll((0, 0))
97 .render(area, buf);
98}
99
100/// Use half block characters to render a logo based on the `RATATUI_LOGO` const.
101///
102/// The logo is rendered in three colors, one for the rat, one for the terminal, and one for the
103/// rat's eye. The eye color alternates between two colors based on the selected row.
104#[allow(clippy::cast_possible_truncation)]
105pub fn render_logo(selected_row: usize, area: Rect, buf: &mut Buffer) {
106 let eye_color = if selected_row % 2 == 0 {
107 THEME.logo.rat_eye
108 } else {
109 THEME.logo.rat_eye_alt
110 };
111 let area = area.inner(Margin {
112 vertical: 0,
113 horizontal: 2,
114 });
115 for (y, (line1, line2)) in RATATUI_LOGO.iter().tuples().enumerate() {
116 for (x, (ch1, ch2)) in line1.chars().zip(line2.chars()).enumerate() {
117 let x = area.left() + x as u16;
118 let y = area.top() + y as u16;
119 let cell = &mut buf[(x, y)];
120 let rat_color = THEME.logo.rat;
121 let term_color = THEME.logo.term;
122 match (ch1, ch2) {
123 ('█', '█') => {
124 cell.set_char('█');
125 cell.fg = rat_color;
126 cell.bg = rat_color;
127 }
128 ('█', ' ') => {
129 cell.set_char('▀');
130 cell.fg = rat_color;
131 }
132 (' ', '█') => {
133 cell.set_char('▄');
134 cell.fg = rat_color;
135 }
136 ('█', 'x') => {
137 cell.set_char('▀');
138 cell.fg = rat_color;
139 cell.bg = term_color;
140 }
141 ('x', '█') => {
142 cell.set_char('▄');
143 cell.fg = rat_color;
144 cell.bg = term_color;
145 }
146 ('x', 'x') => {
147 cell.set_char(' ');
148 cell.fg = term_color;
149 cell.bg = term_color;
150 }
151 ('█', 'e') => {
152 cell.set_char('▀');
153 cell.fg = rat_color;
154 cell.bg = eye_color;
155 }
156 ('e', '█') => {
157 cell.set_char('▄');
158 cell.fg = rat_color;
159 cell.bg = eye_color;
160 }
161 (_, _) => {}
162 };
163 }
164 }
165}
115 fn render(self, area: Rect, buf: &mut Buffer) {
116 RgbSwatch.render(area, buf);
117 let area = area.inner(Margin {
118 vertical: 1,
119 horizontal: 2,
120 });
121 Clear.render(area, buf);
122 Block::new()
123 .title("Ratatouille Recipe".bold().white())
124 .title_alignment(Alignment::Center)
125 .style(THEME.content)
126 .padding(Padding::new(1, 1, 2, 1))
127 .render(area, buf);
128
129 let scrollbar_area = Rect {
130 y: area.y + 2,
131 height: area.height - 3,
132 ..area
133 };
134 render_scrollbar(self.row_index, scrollbar_area, buf);
135
136 let area = area.inner(Margin {
137 horizontal: 2,
138 vertical: 1,
139 });
140 let [recipe, ingredients] =
141 Layout::horizontal([Constraint::Length(44), Constraint::Min(0)]).areas(area);
142
143 render_recipe(recipe, buf);
144 render_ingredients(self.row_index, ingredients, buf);
145 }
Sourcepub fn union(self, other: Self) -> Self
pub fn union(self, other: Self) -> Self
Returns a new Rect
that contains both the current one and the given one.
Sourcepub fn intersection(self, other: Self) -> Self
pub fn intersection(self, other: Self) -> Self
Returns a new Rect
that is the intersection of the current one and the given one.
If the two Rect
s do not intersect, the returned Rect
will have no area.
Examples found in repository?
307 fn render_demo(self, area: Rect, buf: &mut Buffer) -> bool {
308 // render demo content into a separate buffer so all examples fit we add an extra
309 // area.height to make sure the last example is fully visible even when the scroll offset is
310 // at the max
311 let height = example_height();
312 let demo_area = Rect::new(0, 0, area.width, height);
313 let mut demo_buf = Buffer::empty(demo_area);
314
315 let scrollbar_needed = self.scroll_offset != 0 || height > area.height;
316 let content_area = if scrollbar_needed {
317 Rect {
318 width: demo_area.width - 1,
319 ..demo_area
320 }
321 } else {
322 demo_area
323 };
324
325 let mut spacing = self.spacing;
326 self.selected_tab
327 .render(content_area, &mut demo_buf, &mut spacing);
328
329 let visible_content = demo_buf
330 .content
331 .into_iter()
332 .skip((area.width * self.scroll_offset) as usize)
333 .take(area.area() as usize);
334 for (i, cell) in visible_content.enumerate() {
335 let x = i as u16 % area.width;
336 let y = i as u16 / area.width;
337 buf[(area.x + x, area.y + y)] = cell;
338 }
339
340 if scrollbar_needed {
341 let area = area.intersection(buf.area);
342 let mut state = ScrollbarState::new(max_scroll_offset() as usize)
343 .position(self.scroll_offset as usize);
344 Scrollbar::new(ScrollbarOrientation::VerticalRight).render(area, buf, &mut state);
345 }
346 scrollbar_needed
347 }
Sourcepub const fn intersects(self, other: Self) -> bool
pub const fn intersects(self, other: Self) -> bool
Returns true if the two Rect
s intersect.
Sourcepub const fn contains(self, position: Position) -> bool
pub const fn contains(self, position: Position) -> bool
Returns true if the given position is inside the Rect
.
The position is considered inside the Rect
if it is on the Rect
’s border.
§Examples
use ratatui::layout::{Position, Rect};
let rect = Rect::new(1, 2, 3, 4);
assert!(rect.contains(Position { x: 1, y: 2 }));
Sourcepub fn clamp(self, other: Self) -> Self
pub fn clamp(self, other: Self) -> Self
Clamp this Rect
to fit inside the other Rect
.
If the width or height of this Rect
is larger than the other Rect
, it will be clamped to
the other Rect
’s width or height.
If the left or top coordinate of this Rect
is smaller than the other Rect
, it will be
clamped to the other Rect
’s left or top coordinate.
If the right or bottom coordinate of this Rect
is larger than the other Rect
, it will be
clamped to the other Rect
’s right or bottom coordinate.
This is different from Rect::intersection
because it will move this Rect
to fit inside
the other Rect
, while Rect::intersection
instead would keep this Rect
’s position and
truncate its size to only that which is inside the other Rect
.
§Examples
use ratatui::{layout::Rect, Frame};
let area = frame.area();
let rect = Rect::new(0, 0, 100, 100).clamp(area);
Sourcepub const fn rows(self) -> Rows ⓘ
pub const fn rows(self) -> Rows ⓘ
An iterator over rows within the Rect
.
§Example
use ratatui::{buffer::Buffer, layout::Rect, text::Line, widgets::Widget};
fn render(area: Rect, buf: &mut Buffer) {
for row in area.rows() {
Line::raw("Hello, world!").render(row, buf);
}
}
Examples found in repository?
448 fn render_4px(&self, area: Rect, buf: &mut Buffer) {
449 let lighter_color = ConstraintName::from(self.constraint).lighter_color();
450 let main_color = ConstraintName::from(self.constraint).color();
451 let selected_color = if self.selected {
452 lighter_color
453 } else {
454 main_color
455 };
456 let color = if self.legend {
457 selected_color
458 } else {
459 main_color
460 };
461 let label = self.label(area.width);
462 let block = Block::bordered()
463 .border_set(symbols::border::QUADRANT_OUTSIDE)
464 .border_style(Style::reset().fg(color).reversed())
465 .fg(Self::TEXT_COLOR)
466 .bg(color);
467 Paragraph::new(label)
468 .centered()
469 .fg(Self::TEXT_COLOR)
470 .bg(color)
471 .block(block)
472 .render(area, buf);
473
474 if !self.legend {
475 let border_color = if self.selected {
476 lighter_color
477 } else {
478 main_color
479 };
480 if let Some(last_row) = area.rows().last() {
481 buf.set_style(last_row, border_color);
482 }
483 }
484 }
485}
486
487impl Widget for SpacerBlock {
488 fn render(self, area: Rect, buf: &mut Buffer) {
489 match area.height {
490 1 => (),
491 2 => Self::render_2px(area, buf),
492 3 => Self::render_3px(area, buf),
493 _ => Self::render_4px(area, buf),
494 }
495 }
496}
497
498impl SpacerBlock {
499 const TEXT_COLOR: Color = SLATE.c500;
500 const BORDER_COLOR: Color = SLATE.c600;
501
502 /// A block with a corner borders
503 fn block() -> impl Widget {
504 let corners_only = symbols::border::Set {
505 top_left: line::NORMAL.top_left,
506 top_right: line::NORMAL.top_right,
507 bottom_left: line::NORMAL.bottom_left,
508 bottom_right: line::NORMAL.bottom_right,
509 vertical_left: " ",
510 vertical_right: " ",
511 horizontal_top: " ",
512 horizontal_bottom: " ",
513 };
514 Block::bordered()
515 .border_set(corners_only)
516 .border_style(Self::BORDER_COLOR)
517 }
518
519 /// A vertical line used if there is not enough space to render the block
520 fn line() -> impl Widget {
521 Paragraph::new(Text::from(vec![
522 Line::from(""),
523 Line::from("│"),
524 Line::from("│"),
525 Line::from(""),
526 ]))
527 .style(Self::BORDER_COLOR)
528 }
529
530 /// A label that says "Spacer" if there is enough space
531 fn spacer_label(width: u16) -> impl Widget {
532 let label = if width >= 6 { "Spacer" } else { "" };
533 label.fg(Self::TEXT_COLOR).into_centered_line()
534 }
535
536 /// A label that says "8 px" if there is enough space
537 fn label(width: u16) -> impl Widget {
538 let long_label = format!("{width} px");
539 let short_label = format!("{width}");
540 let label = if long_label.len() < width as usize {
541 long_label
542 } else if short_label.len() < width as usize {
543 short_label
544 } else {
545 String::new()
546 };
547 Line::styled(label, Self::TEXT_COLOR).centered()
548 }
549
550 fn render_2px(area: Rect, buf: &mut Buffer) {
551 if area.width > 1 {
552 Self::block().render(area, buf);
553 } else {
554 Self::line().render(area, buf);
555 }
556 }
557
558 fn render_3px(area: Rect, buf: &mut Buffer) {
559 if area.width > 1 {
560 Self::block().render(area, buf);
561 } else {
562 Self::line().render(area, buf);
563 }
564
565 let row = area.rows().nth(1).unwrap_or_default();
566 Self::spacer_label(area.width).render(row, buf);
567 }
568
569 fn render_4px(area: Rect, buf: &mut Buffer) {
570 if area.width > 1 {
571 Self::block().render(area, buf);
572 } else {
573 Self::line().render(area, buf);
574 }
575
576 let row = area.rows().nth(1).unwrap_or_default();
577 Self::spacer_label(area.width).render(row, buf);
578
579 let row = area.rows().nth(2).unwrap_or_default();
580 Self::label(area.width).render(row, buf);
581 }
More examples
80fn text(frame_count: usize, area: Rect, buf: &mut Buffer) {
81 let sub_frame = frame_count.saturating_sub(TEXT_DELAY);
82 if sub_frame == 0 {
83 return;
84 }
85
86 let logo = indoc::indoc! {"
87 ██████ ████ ██████ ████ ██████ ██ ██ ██
88 ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
89 ██████ ████████ ██ ████████ ██ ██ ██ ██
90 ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
91 ██ ██ ██ ██ ██ ██ ██ ██ ████ ██
92 "};
93 let logo_text = Text::styled(logo, Color::Rgb(255, 255, 255));
94 let area = centered_rect(area, logo_text.width() as u16, logo_text.height() as u16);
95
96 let mask_buf = &mut Buffer::empty(area);
97 logo_text.render(area, mask_buf);
98
99 let percentage = (sub_frame as f64 / 480.0).clamp(0.0, 1.0);
100
101 for row in area.rows() {
102 for col in row.columns() {
103 let cell = &mut buf[(col.x, col.y)];
104 let mask_cell = &mut mask_buf[(col.x, col.y)];
105 cell.set_symbol(mask_cell.symbol());
106
107 // blend the mask cell color with the cell color
108 let cell_color = cell.style().bg.unwrap_or(Color::Rgb(0, 0, 0));
109 let mask_color = mask_cell.style().fg.unwrap_or(Color::Rgb(255, 0, 0));
110
111 let color = blend(mask_color, cell_color, percentage);
112 cell.set_style(Style::new().fg(color));
113 }
114 }
115}
Sourcepub const fn columns(self) -> Columns ⓘ
pub const fn columns(self) -> Columns ⓘ
An iterator over columns within the Rect
.
§Example
use ratatui::{
buffer::Buffer,
layout::Rect,
widgets::{Block, Borders, Widget},
};
fn render(area: Rect, buf: &mut Buffer) {
if let Some(left) = area.columns().next() {
Block::new().borders(Borders::LEFT).render(left, buf);
}
}
Examples found in repository?
80fn text(frame_count: usize, area: Rect, buf: &mut Buffer) {
81 let sub_frame = frame_count.saturating_sub(TEXT_DELAY);
82 if sub_frame == 0 {
83 return;
84 }
85
86 let logo = indoc::indoc! {"
87 ██████ ████ ██████ ████ ██████ ██ ██ ██
88 ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
89 ██████ ████████ ██ ████████ ██ ██ ██ ██
90 ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
91 ██ ██ ██ ██ ██ ██ ██ ██ ████ ██
92 "};
93 let logo_text = Text::styled(logo, Color::Rgb(255, 255, 255));
94 let area = centered_rect(area, logo_text.width() as u16, logo_text.height() as u16);
95
96 let mask_buf = &mut Buffer::empty(area);
97 logo_text.render(area, mask_buf);
98
99 let percentage = (sub_frame as f64 / 480.0).clamp(0.0, 1.0);
100
101 for row in area.rows() {
102 for col in row.columns() {
103 let cell = &mut buf[(col.x, col.y)];
104 let mask_cell = &mut mask_buf[(col.x, col.y)];
105 cell.set_symbol(mask_cell.symbol());
106
107 // blend the mask cell color with the cell color
108 let cell_color = cell.style().bg.unwrap_or(Color::Rgb(0, 0, 0));
109 let mask_color = mask_cell.style().fg.unwrap_or(Color::Rgb(255, 0, 0));
110
111 let color = blend(mask_color, cell_color, percentage);
112 cell.set_style(Style::new().fg(color));
113 }
114 }
115}
Sourcepub const fn positions(self) -> Positions ⓘ
pub const fn positions(self) -> Positions ⓘ
An iterator over the positions within the Rect
.
The positions are returned in a row-major order (left-to-right, top-to-bottom).
§Example
use ratatui::{buffer::Buffer, layout::Rect};
fn render(area: Rect, buf: &mut Buffer) {
for position in area.positions() {
buf[(position.x, position.y)].set_symbol("x");
}
}
Sourcepub const fn as_position(self) -> Position
pub const fn as_position(self) -> Position
Trait Implementations§
Source§impl<'de> Deserialize<'de> for Rect
impl<'de> Deserialize<'de> for Rect
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
impl Copy for Rect
impl Eq for Rect
impl StructuralPartialEq for Rect
Auto Trait Implementations§
impl Freeze for Rect
impl RefUnwindSafe for Rect
impl Send for Rect
impl Sync for Rect
impl Unpin for Rect
impl UnwindSafe for Rect
Blanket Implementations§
Source§impl<S, D, Swp, Dwp, T> AdaptInto<D, Swp, Dwp, T> for Swhere
T: Real + Zero + Arithmetics + Clone,
Swp: WhitePoint<T>,
Dwp: WhitePoint<T>,
D: AdaptFrom<S, Swp, Dwp, T>,
impl<S, D, Swp, Dwp, T> AdaptInto<D, Swp, Dwp, T> for Swhere
T: Real + Zero + Arithmetics + Clone,
Swp: WhitePoint<T>,
Dwp: WhitePoint<T>,
D: AdaptFrom<S, Swp, Dwp, T>,
Source§fn adapt_into_using<M>(self, method: M) -> Dwhere
M: TransformMatrix<T>,
fn adapt_into_using<M>(self, method: M) -> Dwhere
M: TransformMatrix<T>,
Source§fn adapt_into(self) -> D
fn adapt_into(self) -> D
Source§impl<T, C> ArraysFrom<C> for Twhere
C: IntoArrays<T>,
impl<T, C> ArraysFrom<C> for Twhere
C: IntoArrays<T>,
Source§fn arrays_from(colors: C) -> T
fn arrays_from(colors: C) -> T
Source§impl<T, C> ArraysInto<C> for Twhere
C: FromArrays<T>,
impl<T, C> ArraysInto<C> for Twhere
C: FromArrays<T>,
Source§fn arrays_into(self) -> C
fn arrays_into(self) -> C
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<WpParam, T, U> Cam16IntoUnclamped<WpParam, T> for Uwhere
T: FromCam16Unclamped<WpParam, U>,
impl<WpParam, T, U> Cam16IntoUnclamped<WpParam, T> for Uwhere
T: FromCam16Unclamped<WpParam, U>,
Source§type Scalar = <T as FromCam16Unclamped<WpParam, U>>::Scalar
type Scalar = <T as FromCam16Unclamped<WpParam, U>>::Scalar
parameters
when converting.Source§fn cam16_into_unclamped(
self,
parameters: BakedParameters<WpParam, <U as Cam16IntoUnclamped<WpParam, T>>::Scalar>,
) -> T
fn cam16_into_unclamped( self, parameters: BakedParameters<WpParam, <U as Cam16IntoUnclamped<WpParam, T>>::Scalar>, ) -> T
self
into C
, using the provided parameters.Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T, C> ComponentsFrom<C> for Twhere
C: IntoComponents<T>,
impl<T, C> ComponentsFrom<C> for Twhere
C: IntoComponents<T>,
Source§fn components_from(colors: C) -> T
fn components_from(colors: C) -> T
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key
and return true
if they are equal.Source§impl<T> FromAngle<T> for T
impl<T> FromAngle<T> for T
Source§fn from_angle(angle: T) -> T
fn from_angle(angle: T) -> T
angle
.Source§impl<T, U> FromStimulus<U> for Twhere
U: IntoStimulus<T>,
impl<T, U> FromStimulus<U> for Twhere
U: IntoStimulus<T>,
Source§fn from_stimulus(other: U) -> T
fn from_stimulus(other: U) -> T
other
into Self
, while performing the appropriate scaling,
rounding and clamping.Source§impl<T, U> IntoAngle<U> for Twhere
U: FromAngle<T>,
impl<T, U> IntoAngle<U> for Twhere
U: FromAngle<T>,
Source§fn into_angle(self) -> U
fn into_angle(self) -> U
T
.Source§impl<WpParam, T, U> IntoCam16Unclamped<WpParam, T> for Uwhere
T: Cam16FromUnclamped<WpParam, U>,
impl<WpParam, T, U> IntoCam16Unclamped<WpParam, T> for Uwhere
T: Cam16FromUnclamped<WpParam, U>,
Source§type Scalar = <T as Cam16FromUnclamped<WpParam, U>>::Scalar
type Scalar = <T as Cam16FromUnclamped<WpParam, U>>::Scalar
parameters
when converting.Source§fn into_cam16_unclamped(
self,
parameters: BakedParameters<WpParam, <U as IntoCam16Unclamped<WpParam, T>>::Scalar>,
) -> T
fn into_cam16_unclamped( self, parameters: BakedParameters<WpParam, <U as IntoCam16Unclamped<WpParam, T>>::Scalar>, ) -> T
self
into C
, using the provided parameters.Source§impl<T, U> IntoColor<U> for Twhere
U: FromColor<T>,
impl<T, U> IntoColor<U> for Twhere
U: FromColor<T>,
Source§fn into_color(self) -> U
fn into_color(self) -> U
Source§impl<T, U> IntoColorUnclamped<U> for Twhere
U: FromColorUnclamped<T>,
impl<T, U> IntoColorUnclamped<U> for Twhere
U: FromColorUnclamped<T>,
Source§fn into_color_unclamped(self) -> U
fn into_color_unclamped(self) -> U
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self
into a Left
variant of Either<Self, Self>
if into_left
is true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self
into a Left
variant of Either<Self, Self>
if into_left(&self)
returns true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> IntoStimulus<T> for T
impl<T> IntoStimulus<T> for T
Source§fn into_stimulus(self) -> T
fn into_stimulus(self) -> T
self
into T
, while performing the appropriate scaling,
rounding and clamping.Source§impl<T> ToCompactString for Twhere
T: Display,
impl<T> ToCompactString for Twhere
T: Display,
Source§fn try_to_compact_string(&self) -> Result<CompactString, ToCompactStringError>
fn try_to_compact_string(&self) -> Result<CompactString, ToCompactStringError>
ToCompactString::to_compact_string()
Read moreSource§fn to_compact_string(&self) -> CompactString
fn to_compact_string(&self) -> CompactString
CompactString
. Read moreSource§impl<T, C> TryComponentsInto<C> for Twhere
C: TryFromComponents<T>,
impl<T, C> TryComponentsInto<C> for Twhere
C: TryFromComponents<T>,
Source§type Error = <C as TryFromComponents<T>>::Error
type Error = <C as TryFromComponents<T>>::Error
try_into_colors
fails to cast.Source§fn try_components_into(self) -> Result<C, <T as TryComponentsInto<C>>::Error>
fn try_components_into(self) -> Result<C, <T as TryComponentsInto<C>>::Error>
Source§impl<T, U> TryIntoColor<U> for Twhere
U: TryFromColor<T>,
impl<T, U> TryIntoColor<U> for Twhere
U: TryFromColor<T>,
Source§fn try_into_color(self) -> Result<U, OutOfBounds<U>>
fn try_into_color(self) -> Result<U, OutOfBounds<U>>
OutOfBounds
error is returned which contains
the unclamped color. Read more