解答

  1. src
  2. ├── main.rs
  3. ├── widgets
  4. ├── button.rs
  5. ├── label.rs
  6. └── window.rs
  7. └── widgets.rs
  1. // ---- src/widgets.rs ----
  2. mod button;
  3. mod label;
  4. mod window;
  5. pub trait Widget {
  6. /// Natural width of `self`.
  7. fn width(&self) -> usize;
  8. /// Draw the widget into a buffer.
  9. fn draw_into(&self, buffer: &mut dyn std::fmt::Write);
  10. /// Draw the widget on standard output.
  11. fn draw(&self) {
  12. let mut buffer = String::new();
  13. self.draw_into(&mut buffer);
  14. println!("{buffer}");
  15. }
  16. }
  17. pub use button::Button;
  18. pub use label::Label;
  19. pub use window::Window;
  1. // ---- src/widgets/label.rs ----
  2. use super::Widget;
  3. pub struct Label {
  4. label: String,
  5. }
  6. impl Label {
  7. pub fn new(label: &str) -> Label {
  8. Label { label: label.to_owned() }
  9. }
  10. }
  11. impl Widget for Label {
  12. fn width(&self) -> usize {
  13. // ANCHOR_END: Label-width
  14. self.label.lines().map(|line| line.chars().count()).max().unwrap_or(0)
  15. }
  16. // ANCHOR: Label-draw_into
  17. fn draw_into(&self, buffer: &mut dyn std::fmt::Write) {
  18. // ANCHOR_END: Label-draw_into
  19. writeln!(buffer, "{}", &self.label).unwrap();
  20. }
  21. }
  1. // ---- src/widgets/button.rs ----
  2. use super::{Label, Widget};
  3. pub struct Button {
  4. label: Label,
  5. }
  6. impl Button {
  7. pub fn new(label: &str) -> Button {
  8. Button { label: Label::new(label) }
  9. }
  10. }
  11. impl Widget for Button {
  12. fn width(&self) -> usize {
  13. // ANCHOR_END: Button-width
  14. self.label.width() + 8 // add a bit of padding
  15. }
  16. // ANCHOR: Button-draw_into
  17. fn draw_into(&self, buffer: &mut dyn std::fmt::Write) {
  18. // ANCHOR_END: Button-draw_into
  19. let width = self.width();
  20. let mut label = String::new();
  21. self.label.draw_into(&mut label);
  22. writeln!(buffer, "+{:-<width$}+", "").unwrap();
  23. for line in label.lines() {
  24. writeln!(buffer, "|{:^width$}|", &line).unwrap();
  25. }
  26. writeln!(buffer, "+{:-<width$}+", "").unwrap();
  27. }
  28. }
  1. // ---- src/widgets/window.rs ----
  2. use super::Widget;
  3. pub struct Window {
  4. title: String,
  5. widgets: Vec<Box<dyn Widget>>,
  6. }
  7. impl Window {
  8. pub fn new(title: &str) -> Window {
  9. Window { title: title.to_owned(), widgets: Vec::new() }
  10. }
  11. pub fn add_widget(&mut self, widget: Box<dyn Widget>) {
  12. self.widgets.push(widget);
  13. }
  14. fn inner_width(&self) -> usize {
  15. std::cmp::max(
  16. self.title.chars().count(),
  17. self.widgets.iter().map(|w| w.width()).max().unwrap_or(0),
  18. )
  19. }
  20. }
  21. impl Widget for Window {
  22. fn width(&self) -> usize {
  23. // ANCHOR_END: Window-width
  24. // Add 4 paddings for borders
  25. self.inner_width() + 4
  26. }
  27. // ANCHOR: Window-draw_into
  28. fn draw_into(&self, buffer: &mut dyn std::fmt::Write) {
  29. // ANCHOR_END: Window-draw_into
  30. let mut inner = String::new();
  31. for widget in &self.widgets {
  32. widget.draw_into(&mut inner);
  33. }
  34. let inner_width = self.inner_width();
  35. // TODO: after learning about error handling, you can change
  36. // draw_into to return Result<(), std::fmt::Error>. Then use
  37. // the ?-operator here instead of .unwrap().
  38. writeln!(buffer, "+-{:-<inner_width$}-+", "").unwrap();
  39. writeln!(buffer, "| {:^inner_width$} |", &self.title).unwrap();
  40. writeln!(buffer, "+={:=<inner_width$}=+", "").unwrap();
  41. for line in inner.lines() {
  42. writeln!(buffer, "| {:inner_width$} |", line).unwrap();
  43. }
  44. writeln!(buffer, "+-{:-<inner_width$}-+", "").unwrap();
  45. }
  46. }
  1. // ---- src/main.rs ----
  2. mod widgets;
  3. use widgets::Widget;
  4. fn main() {
  5. let mut window = widgets::Window::new("Rust GUI Demo 1.23");
  6. window
  7. .add_widget(Box::new(widgets::Label::new("This is a small text GUI demo.")));
  8. window.add_widget(Box::new(widgets::Button::new("Click me!")));
  9. window.draw();
  10. }