1use std::{
2 cell::RefCell,
3 rc::Rc,
4};
5
6use freya_core::prelude::*;
7use futures_lite::StreamExt;
8
9use crate::{
10 cache::ResourceCache,
11 element::Html,
12 handle::HtmlHandle,
13 net::FetchRequest,
14 state::BlitzState,
15};
16
17#[derive(PartialEq)]
34pub struct HtmlViewer {
35 handle: HtmlHandle,
36 layout: LayoutData,
37 fonts: Vec<Bytes>,
38}
39
40impl HtmlViewer {
41 pub fn new(handle: HtmlHandle) -> Self {
43 Self {
44 handle,
45 layout: LayoutData::default(),
46 fonts: Vec::new(),
47 }
48 .expanded()
49 }
50
51 pub fn font(mut self, font: impl Into<Bytes>) -> Self {
53 self.fonts.push(font.into());
54 self
55 }
56}
57
58impl LayoutExt for HtmlViewer {
59 fn layout_mut(&mut self) -> &mut LayoutData {
60 &mut self.layout
61 }
62}
63
64impl ContainerExt for HtmlViewer {}
65
66impl Component for HtmlViewer {
67 fn render(&self) -> impl IntoElement {
68 let platform = Platform::get().current_window();
69 let mut handle = self.handle;
70 let fonts = self.fonts.clone();
71
72 let state = use_hook(move || {
73 let platform = Platform::get().current_window();
74 let resource_cache = ResourceCache::get();
75 let (wake_tx, mut wake_rx) = futures_channel::mpsc::unbounded::<()>();
76 let (nav_tx, mut nav_rx) = futures_channel::mpsc::unbounded::<String>();
77 let (fetch_tx, mut fetch_rx) = futures_channel::mpsc::unbounded::<FetchRequest>();
78
79 let state = Rc::new(RefCell::new(BlitzState::new(
80 wake_tx,
81 nav_tx,
82 fetch_tx,
83 resource_cache.clone(),
84 fonts,
85 )));
86 handle.attach(state.clone());
87
88 spawn(async move {
89 while wake_rx.next().await.is_some() {
90 platform.send(UserEvent::RequestRedraw);
91 }
92 });
93
94 spawn(async move {
95 while let Some((url, handler)) = fetch_rx.next().await {
96 let resource_cache = resource_cache.clone();
97
98 spawn(async move {
99 match freya_components::http::fetch(url.clone()).await {
100 Ok(bytes) => {
101 let resolved_url = url.to_string();
102 resource_cache.set(url, bytes.clone());
103 handler.bytes(resolved_url, bytes);
104 }
105 Err(err) => tracing::warn!("Failed to fetch resource {url}: {err}"),
106 }
107 });
108 }
109 });
110
111 spawn(async move {
112 while let Some(url) = nav_rx.next().await {
113 handle.navigate(url);
114 }
115 });
116
117 state
118 });
119
120 let a11y_id = use_hook(AccessibilityId::new_unique);
121
122 Html::new(state.clone())
123 .layout(self.layout.clone())
124 .a11y_id(a11y_id)
125 .a11y_focusable(true)
126 .on_mouse_move({
127 let state = state.clone();
128 move |e: Event<MouseEventData>| {
129 state
130 .borrow_mut()
131 .mouse_move(e.element_location.x as f32, e.element_location.y as f32);
132 }
133 })
134 .on_mouse_down({
135 let state = state.clone();
136 move |e: Event<MouseEventData>| {
137 a11y_id.request_focus();
138 state.borrow_mut().mouse_button(
139 e.element_location.x as f32,
140 e.element_location.y as f32,
141 e.button,
142 true,
143 );
144 }
145 })
146 .on_mouse_up({
147 let state = state.clone();
148 move |e: Event<MouseEventData>| {
149 state.borrow_mut().mouse_button(
150 e.element_location.x as f32,
151 e.element_location.y as f32,
152 e.button,
153 false,
154 );
155 }
156 })
157 .on_touch_start({
158 let state = state.clone();
159 move |event: Event<TouchEventData>| {
160 a11y_id.request_focus();
161 state.borrow_mut().touch(&event, TouchPhase::Started);
162 }
163 })
164 .on_touch_move({
165 let state = state.clone();
166 move |event: Event<TouchEventData>| {
167 state.borrow_mut().touch(&event, TouchPhase::Moved);
168 }
169 })
170 .on_touch_end({
171 let state = state.clone();
172 move |event: Event<TouchEventData>| {
173 state.borrow_mut().touch(&event, TouchPhase::Ended);
174 }
175 })
176 .on_touch_cancel({
177 let state = state.clone();
178 move |event: Event<TouchEventData>| {
179 state.borrow_mut().touch(&event, TouchPhase::Cancelled);
180 }
181 })
182 .on_wheel({
183 let state = state.clone();
184 move |e: Event<WheelEventData>| {
185 let scale = *platform.scale_factor.read();
186 state.borrow_mut().wheel(
187 e.element_location.x as f32,
188 e.element_location.y as f32,
189 e.delta_x / scale,
190 e.delta_y / scale,
191 );
192 }
193 })
194 .on_key_down({
195 let state = state.clone();
196 move |e: Event<KeyboardEventData>| {
197 state.borrow_mut().key(&e.key, &e.code, e.modifiers, true);
198 }
199 })
200 .on_key_up(move |e: Event<KeyboardEventData>| {
201 state.borrow_mut().key(&e.key, &e.code, e.modifiers, false);
202 })
203 }
204}