Skip to content

Commit 5f6b5ec

Browse files
committed
Command Pattern: Text Editor with undo
1 parent 63857f9 commit 5f6b5ec

File tree

9 files changed

+160
-0
lines changed

9 files changed

+160
-0
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,5 @@ members = [
1717
"structural/flyweight",
1818
"structural/proxy",
1919
"behavioral/chain-of-responsibility",
20+
"behavioral/command",
2021
]

behavioral/command/Cargo.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
edition = "2021"
3+
name = "command"
4+
version = "0.1.0"
5+
6+
[[bin]]
7+
name = "command"
8+
path = "main.rs"
9+
10+
[dependencies]
11+
cursive = {version = "0.19", default-features = false, features = ["termion-backend"]}
12+
owning_ref = "0.4"

behavioral/command/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Command
2+
3+
Command is behavioral design pattern that converts requests or simple operations into objects.
4+
5+
## Text Editor: Commands and Undo
6+
7+
```bash
8+
cargo run
9+
```
10+
11+
![Text Editor screenshot](res/editor.png)

behavioral/command/command.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
mod copy;
2+
mod cut;
3+
mod paste;
4+
5+
pub use copy::CopyCommand;
6+
pub use cut::CutCommand;
7+
pub use paste::PasteCommand;
8+
9+
pub trait Command {
10+
fn execute(&mut self, app: &mut cursive::Cursive) -> bool;
11+
fn undo(&mut self, app: &mut cursive::Cursive);
12+
}

behavioral/command/command/copy.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
use cursive::{views::EditView, Cursive};
2+
3+
use super::Command;
4+
use crate::AppContext;
5+
6+
#[derive(Default)]
7+
pub struct CopyCommand;
8+
9+
impl Command for CopyCommand {
10+
fn execute(&mut self, app: &mut Cursive) -> bool {
11+
let editor = app.find_name::<EditView>("Editor").unwrap();
12+
let mut context = app.take_user_data::<AppContext>().unwrap();
13+
14+
context.clipboard = editor.get_content().to_string();
15+
16+
app.set_user_data(context);
17+
return false;
18+
}
19+
20+
fn undo(&mut self, _: &mut Cursive) {}
21+
}

behavioral/command/command/cut.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
use cursive::{views::EditView, Cursive};
2+
3+
use super::Command;
4+
use crate::AppContext;
5+
6+
#[derive(Default)]
7+
pub struct CutCommand {
8+
backup: String,
9+
}
10+
11+
impl Command for CutCommand {
12+
fn execute(&mut self, app: &mut Cursive) -> bool {
13+
let mut editor = app.find_name::<EditView>("Editor").unwrap();
14+
15+
app.with_user_data(|context: &mut AppContext| {
16+
self.backup = editor.get_content().to_string();
17+
context.clipboard = self.backup.clone();
18+
editor.set_content("".to_string());
19+
});
20+
21+
return true;
22+
}
23+
24+
fn undo(&mut self, app: &mut Cursive) {
25+
let mut editor = app.find_name::<EditView>("Editor").unwrap();
26+
editor.set_content(&self.backup);
27+
}
28+
}

behavioral/command/command/paste.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
use cursive::{views::EditView, Cursive};
2+
3+
use super::Command;
4+
use crate::AppContext;
5+
6+
#[derive(Default)]
7+
pub struct PasteCommand {
8+
backup: String,
9+
}
10+
11+
impl Command for PasteCommand {
12+
fn execute(&mut self, app: &mut Cursive) -> bool {
13+
let mut editor = app.find_name::<EditView>("Editor").unwrap();
14+
15+
app.with_user_data(|context: &mut AppContext| {
16+
self.backup = editor.get_content().to_string();
17+
editor.set_content(context.clipboard.clone());
18+
});
19+
20+
return true;
21+
}
22+
23+
fn undo(&mut self, app: &mut Cursive) {
24+
let mut editor = app.find_name::<EditView>("Editor").unwrap();
25+
editor.set_content(&self.backup);
26+
}
27+
}

behavioral/command/main.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
mod command;
2+
3+
use cursive::{
4+
traits::Nameable,
5+
views::{Dialog, EditView},
6+
Cursive,
7+
};
8+
9+
use command::{Command, CopyCommand, CutCommand, PasteCommand};
10+
11+
#[derive(Default)]
12+
struct AppContext {
13+
clipboard: String,
14+
history: Vec<Box<dyn Command>>,
15+
}
16+
17+
fn main() {
18+
let mut app = cursive::default();
19+
20+
app.set_user_data(AppContext::default());
21+
app.add_layer(
22+
Dialog::around(EditView::default().with_name("Editor"))
23+
.title("Type and use buttons")
24+
.button("Copy", |s| execute(s, CopyCommand::default()))
25+
.button("Cut", |s| execute(s, CutCommand::default()))
26+
.button("Paste", |s| execute(s, PasteCommand::default()))
27+
.button("Undo", |s| undo(s))
28+
.button("Quit", |s| s.quit()),
29+
);
30+
31+
app.run();
32+
}
33+
34+
fn execute(app: &mut Cursive, mut command: impl Command + 'static) {
35+
if command.execute(app) {
36+
app.with_user_data(|context: &mut AppContext| {
37+
context.history.push(Box::new(command));
38+
});
39+
}
40+
}
41+
42+
fn undo(app: &mut Cursive) {
43+
let mut context = app.take_user_data::<AppContext>().unwrap();
44+
if let Some(mut command) = context.history.pop() {
45+
command.undo(app)
46+
}
47+
app.set_user_data(context);
48+
}

behavioral/command/res/editor.png

14 KB
Loading

0 commit comments

Comments
 (0)