Skip to content

Commit c458a0a

Browse files
committed
ch2: Implement rust version
1 parent d6c523f commit c458a0a

File tree

4 files changed

+166
-20
lines changed

4 files changed

+166
-20
lines changed

python/graphics.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,12 @@ class Browser:
1313
def __init__(self):
1414
self.window = tkinter.Tk()
1515
self.scroll = 0
16+
self.min_scroll = 0
17+
self.max_scroll = 0
18+
self.window.title('Browser-engineering')
1619
self.window.bind("<Up>", self.scrollup)
1720
self.window.bind("<Down>", self.scrolldown)
21+
self.window.bind("<MouseWheel>", self.mousewheel)
1822
self.canvas = tkinter.Canvas(
1923
self.window,
2024
width=WIDTH,
@@ -32,6 +36,7 @@ def layout(self, text):
3236
display_list = []
3337
cursor_x, cursor_y = HSTEP, VSTEP
3438
for c in text:
39+
self.max_scroll = max(self.max_scroll, cursor_y)
3540
display_list.append((cursor_x, cursor_y, c))
3641
cursor_x += HSTEP
3742
if cursor_x >= WIDTH - HSTEP or c == '\n':
@@ -50,13 +55,25 @@ def render(self):
5055

5156
def scrolldown(self, e):
5257
self.scroll += SCROLL_STEP
58+
self.scroll = min(self.max_scroll, self.scroll)
5359
self.render()
5460

5561
def scrollup(self, e):
5662
self.scroll -= SCROLL_STEP
57-
self.scroll = max(self.scroll, 0)
63+
self.scroll = max(self.scroll, self.min_scroll)
5864
self.render()
5965

66+
def mousewheel(self, e):
67+
if e.delta > 0:
68+
self.scroll -= SCROLL_STEP
69+
self.scroll = max(self.scroll, self.min_scroll)
70+
self.render()
71+
elif e.delta < 0:
72+
self.scroll += SCROLL_STEP
73+
self.scroll = min(self.max_scroll, self.scroll)
74+
self.render()
75+
76+
6077

6178
if __name__ == '__main__':
6279
import sys

rust/Cargo.toml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,20 @@ name = "browser-engineering"
33
version = "0.1.0"
44
edition = "2018"
55
authors = [
6-
"Seo Sanghyeon <sanxiyn@gmail.com>"
6+
"Seo Sanghyeon <sanxiyn@gmail.com>",
7+
"Dong-hee Na <donghee.na92@gmail.com",
78
]
89

9-
[[example]]
10+
[lib]
1011
name = "http"
12+
path = "src/http.rs"
13+
14+
[[bin]]
15+
name = "browser"
16+
path = "src/main.rs"
1117

1218
[dependencies]
19+
druid = "0.7.0"
1320
rustls = "0.19"
1421
webpki = "0.21"
1522
webpki-roots = "0.21"

rust/examples/http.rs renamed to rust/src/http.rs

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,13 @@ fn split2<'a>(string: &'a str, delimiter: &str) -> Option<(&'a str, &'a str)> {
5555
Some((split.next()?, split.next()?))
5656
}
5757

58-
fn request(url: &str) -> (HashMap<String, String>, Vec<u8>) {
58+
pub fn request(url: &str) -> (HashMap<String, String>, Vec<u8>) {
5959
// 1. Parse scheme
6060
let (scheme, url) = split2(url, "://").expect(MALFORMED_URL);
6161
let port = match scheme {
6262
"http" => 80,
6363
"https" => 443,
64-
_ => panic!("Unknown scheme {}", scheme)
64+
_ => panic!("Unknown scheme {}", scheme),
6565
};
6666

6767
// 2. Parse host
@@ -110,7 +110,7 @@ fn request(url: &str) -> (HashMap<String, String>, Vec<u8>) {
110110
// 9. Check status
111111
match status {
112112
"200" => (),
113-
_ => panic!("{}: {}", status, explanation)
113+
_ => panic!("{}: {}", status, explanation),
114114
};
115115

116116
// 10. Parse headers
@@ -134,30 +134,20 @@ fn request(url: &str) -> (HashMap<String, String>, Vec<u8>) {
134134
(headers, body)
135135
}
136136

137-
fn show(body: &[u8]) {
137+
pub fn lex(body: &[u8]) -> String {
138138
// 13. Print content
139139
let mut in_angle = false;
140+
let mut ret = String::new();
140141
for c in body {
141142
match *c {
142143
b'<' => in_angle = true,
143144
b'>' => in_angle = false,
144145
_ => {
145146
if !in_angle {
146-
print!("{}", *c as char);
147+
ret.push(*c as char);
147148
}
148149
}
149150
}
150151
}
151-
}
152-
153-
fn load(url: &str) {
154-
// 14. Wire up
155-
let (_headers, body) = request(url);
156-
show(&body);
157-
}
158-
159-
fn main() {
160-
// 15. Run from command line
161-
let args: Vec<String> = std::env::args().collect();
162-
load(&args[1]);
152+
ret
163153
}

rust/src/main.rs

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
use druid::piet::{FontFamily, Text, TextLayoutBuilder};
2+
use druid::widget::prelude::*;
3+
use druid::{AppLauncher, Color, LocalizedString, WindowDesc};
4+
use http::{lex, request};
5+
use std::cmp;
6+
7+
static WIDTH: i32 = 800;
8+
static HEIGHT: i32 = 600;
9+
static HSTEP: i32 = 13;
10+
static VSTEP: i32 = 12;
11+
static SCROLL_STEP: i32 = 100;
12+
13+
struct Character {
14+
x: i32,
15+
y: i32,
16+
ch: char,
17+
}
18+
19+
struct BrowserWidget {
20+
display_list: Vec<Character>,
21+
scroll: i32,
22+
min_scroll: i32,
23+
max_scroll: i32,
24+
}
25+
26+
trait Browser {
27+
fn load(&mut self, url: &str);
28+
}
29+
30+
impl Browser for BrowserWidget {
31+
fn load(&mut self, url: &str) {
32+
let (_headers, body) = request(url);
33+
let text = lex(&body);
34+
let mut cursor_x = HSTEP;
35+
let mut cursor_y = VSTEP;
36+
for c in text.chars() {
37+
self.max_scroll = cmp::max(self.max_scroll, cursor_y);
38+
self.display_list.push(Character {
39+
x: cursor_x,
40+
y: cursor_y,
41+
ch: c,
42+
});
43+
cursor_x += VSTEP;
44+
if cursor_x >= WIDTH - HSTEP || c == '\n' {
45+
cursor_y += VSTEP;
46+
cursor_x = HSTEP;
47+
}
48+
}
49+
}
50+
}
51+
52+
impl Widget<i32> for BrowserWidget {
53+
fn event(&mut self, ctx: &mut EventCtx, _event: &Event, _data: &mut i32, _env: &Env) {
54+
match _event {
55+
Event::Wheel(e) => {
56+
if e.wheel_delta.y < 0.0 {
57+
self.scroll -= SCROLL_STEP;
58+
self.scroll = cmp::max(self.scroll, self.min_scroll);
59+
} else if e.wheel_delta.y > 0.0 {
60+
self.scroll += SCROLL_STEP;
61+
self.scroll = cmp::min(self.scroll, self.max_scroll);
62+
}
63+
*_data = self.scroll;
64+
ctx.request_update();
65+
}
66+
_ => {}
67+
}
68+
}
69+
70+
fn lifecycle(&mut self, _ctx: &mut LifeCycleCtx, _event: &LifeCycle, _data: &i32, _env: &Env) {}
71+
72+
fn update(&mut self, ctx: &mut UpdateCtx, old_data: &i32, data: &i32, _env: &Env) {
73+
if old_data != data {
74+
ctx.request_paint();
75+
}
76+
}
77+
78+
fn layout(
79+
&mut self,
80+
_layout_ctx: &mut LayoutCtx,
81+
bc: &BoxConstraints,
82+
_data: &i32,
83+
_env: &Env,
84+
) -> Size {
85+
bc.max()
86+
}
87+
88+
fn paint(&mut self, ctx: &mut PaintCtx, _data: &i32, _env: &Env) {
89+
let size = ctx.size();
90+
let rect = size.to_rect();
91+
ctx.fill(rect, &Color::WHITE);
92+
for ch in &self.display_list {
93+
if ch.y > self.scroll + HEIGHT {
94+
continue;
95+
}
96+
97+
if ch.y + VSTEP < self.scroll {
98+
continue;
99+
}
100+
101+
let text = ctx.text();
102+
let layout = text
103+
.new_text_layout(String::from(ch.ch))
104+
.font(FontFamily::default(), 12.0)
105+
.text_color(Color::BLACK)
106+
.build()
107+
.unwrap();
108+
ctx.draw_text(&layout, (ch.x as f64, ch.y as f64 - self.scroll as f64));
109+
}
110+
}
111+
}
112+
113+
pub fn main() {
114+
let browser = || -> BrowserWidget {
115+
let args: Vec<String> = std::env::args().collect();
116+
let mut ret = BrowserWidget {
117+
display_list: Vec::new(),
118+
scroll: 0,
119+
min_scroll: 0,
120+
max_scroll: 0,
121+
};
122+
ret.load(&args[1]);
123+
ret
124+
};
125+
let window = WindowDesc::new(browser)
126+
.title(LocalizedString::new("Browser-engineering"))
127+
.window_size((WIDTH as f64, HEIGHT as f64));
128+
AppLauncher::with_window(window)
129+
.use_simple_logger()
130+
.launch(0)
131+
.expect("launch failed");
132+
}

0 commit comments

Comments
 (0)