Skip to content

Commit 382683b

Browse files
committed
refactor: extract shared ViewManager with HashMap — replaces duplicated Vec + find_view across all engines
1 parent d878cca commit 382683b

7 files changed

Lines changed: 192 additions & 176 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
- Improved `on_action()` docs — now states it's required for litehtml/blitz engines
1111

1212
### Changed
13+
- Engine view storage refactored to shared `ViewManager<V>` with HashMap for O(1) lookups
1314
- Advanced WebView urls/titles tracking switched from Vec to HashMap for O(1) lookups
1415
- Reduced hot-path allocations — URL/title strings moved instead of cloned, pixel buffer avoids double copy
1516
- `on_action()` error messages now suggest the fix; doc comments name affected engines

src/engines.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ use iced::mouse::{self, Interaction};
66
use iced::Point;
77
use iced::Size;
88

9+
mod view_manager;
10+
pub use view_manager::ViewManager;
11+
912
/// A Blitz implementation of Engine (Stylo + Taffy + Vello)
1013
#[cfg(feature = "blitz")]
1114
pub mod blitz;

src/engines/blitz.rs

Lines changed: 30 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@ use std::sync::{Arc, Mutex};
44
use iced::keyboard;
55
use iced::mouse::{self, Interaction};
66
use iced::{Point, Size};
7-
use rand::Rng;
87

9-
use super::{Engine, PageType, PixelFormat, ViewId};
8+
use super::{Engine, PageType, PixelFormat, ViewId, ViewManager};
109
use crate::ImageInfo;
1110

1211
use anyrender::render_to_buffer;
@@ -47,7 +46,6 @@ impl ShellProvider for WebviewShell {
4746
}
4847

4948
struct BlitzView {
50-
id: ViewId,
5149
document: Option<HtmlDocument>,
5250
net_provider: Arc<dyn NetProvider>,
5351
nav_capture: Arc<Mutex<Option<String>>>,
@@ -74,7 +72,7 @@ struct BlitzView {
7472
/// Supports modern CSS (flexbox, grid, Firefox CSS engine via Stylo),
7573
/// but no JavaScript. Uses `anyrender_vello_cpu` for software rasterization.
7674
pub struct Blitz {
77-
views: Vec<BlitzView>,
75+
views: ViewManager<BlitzView>,
7876
scale_factor: f32,
7977
color_scheme: ColorScheme,
8078
}
@@ -97,23 +95,13 @@ fn detect_color_scheme() -> ColorScheme {
9795
impl Default for Blitz {
9896
fn default() -> Self {
9997
Self {
100-
views: Vec::new(),
98+
views: ViewManager::default(),
10199
scale_factor: 1.0,
102100
color_scheme: detect_color_scheme(),
103101
}
104102
}
105103
}
106104

107-
impl Blitz {
108-
fn find_view(&self, id: ViewId) -> Option<&BlitzView> {
109-
self.views.iter().find(|v| v.id == id)
110-
}
111-
112-
fn find_view_mut(&mut self, id: ViewId) -> Option<&mut BlitzView> {
113-
self.views.iter_mut().find(|v| v.id == id)
114-
}
115-
}
116-
117105
fn cursor_icon_to_interaction(icon: CursorIcon) -> Interaction {
118106
match icon {
119107
CursorIcon::Pointer => Interaction::Pointer,
@@ -263,7 +251,7 @@ impl Engine for Blitz {
263251
}
264252

265253
fn update(&mut self) {
266-
for view in &mut self.views {
254+
for view in self.views.values_mut() {
267255
if view.resource_ticks > 0 {
268256
view.resource_ticks -= 1;
269257
if view.resource_ticks % RESOLVE_INTERVAL == 0 {
@@ -275,15 +263,15 @@ impl Engine for Blitz {
275263
}
276264

277265
fn render(&mut self, _size: Size<u32>) {
278-
for view in &mut self.views {
266+
for view in self.views.values_mut() {
279267
if view.needs_render {
280268
render_view(view);
281269
}
282270
}
283271
}
284272

285273
fn request_render(&mut self, id: ViewId, _size: Size<u32>) {
286-
let Some(view) = self.find_view_mut(id) else {
274+
let Some(view) = self.views.get_mut(id) else {
287275
return;
288276
};
289277
if view.needs_render {
@@ -292,7 +280,6 @@ impl Engine for Blitz {
292280
}
293281

294282
fn new_view(&mut self, size: Size<u32>, content: Option<PageType>) -> ViewId {
295-
let id = rand::thread_rng().gen();
296283
let w = size.width.max(1);
297284
let h = size.height.max(1);
298285
let size = Size::new(w, h);
@@ -328,7 +315,6 @@ impl Engine for Blitz {
328315
let has_document = document.is_some();
329316

330317
let mut view = BlitzView {
331-
id,
332318
document,
333319
net_provider: net,
334320
nav_capture,
@@ -351,28 +337,27 @@ impl Engine for Blitz {
351337
};
352338

353339
render_view(&mut view);
354-
self.views.push(view);
355-
id
340+
self.views.insert(view)
356341
}
357342

358343
fn remove_view(&mut self, id: ViewId) {
359-
self.views.retain(|v| v.id != id);
344+
self.views.remove(id);
360345
}
361346

362347
fn has_view(&self, id: ViewId) -> bool {
363-
self.views.iter().any(|v| v.id == id)
348+
self.views.contains(id)
364349
}
365350

366351
fn view_ids(&self) -> Vec<ViewId> {
367-
self.views.iter().map(|v| v.id).collect()
352+
self.views.keys()
368353
}
369354

370355
fn focus(&mut self) {}
371356

372357
fn unfocus(&self) {}
373358

374359
fn resize(&mut self, size: Size<u32>) {
375-
for view in &mut self.views {
360+
for view in self.views.values_mut() {
376361
view.size = size;
377362
if let Some(ref mut doc) = view.document {
378363
let scale = view.scale;
@@ -392,7 +377,7 @@ impl Engine for Blitz {
392377
return;
393378
}
394379
self.scale_factor = scale;
395-
for view in &mut self.views {
380+
for view in self.views.values_mut() {
396381
view.scale = scale;
397382
if let Some(ref mut doc) = view.document {
398383
let phys_w = (view.size.width as f32 * scale) as u32;
@@ -408,7 +393,7 @@ impl Engine for Blitz {
408393
}
409394

410395
fn handle_keyboard_event(&mut self, id: ViewId, event: keyboard::Event) {
411-
let Some(view) = self.find_view_mut(id) else {
396+
let Some(view) = self.views.get_mut(id) else {
412397
return;
413398
};
414399
if let Some(ref mut doc) = view.document {
@@ -441,7 +426,7 @@ impl Engine for Blitz {
441426
mouse::Button::Forward => (MouseEventButton::Fifth, MouseEventButtons::Fifth),
442427
_ => return,
443428
};
444-
let Some(view) = self.find_view_mut(id) else {
429+
let Some(view) = self.views.get_mut(id) else {
445430
return;
446431
};
447432
if let Some(ref mut doc) = view.document {
@@ -465,7 +450,7 @@ impl Engine for Blitz {
465450
}
466451
}
467452
mouse::Event::CursorMoved { .. } => {
468-
let Some(view) = self.find_view_mut(id) else {
453+
let Some(view) = self.views.get_mut(id) else {
469454
return;
470455
};
471456
if let Some(ref mut doc) = view.document {
@@ -486,7 +471,7 @@ impl Engine for Blitz {
486471
mouse::Button::Forward => MouseEventButton::Fifth,
487472
_ => return,
488473
};
489-
let Some(view) = self.find_view_mut(id) else {
474+
let Some(view) = self.views.get_mut(id) else {
490475
return;
491476
};
492477
if let Some(ref mut doc) = view.document {
@@ -510,7 +495,7 @@ impl Engine for Blitz {
510495
}
511496
}
512497
mouse::Event::CursorLeft => {
513-
if let Some(view) = self.find_view_mut(id) {
498+
if let Some(view) = self.views.get_mut(id) {
514499
view.cursor = Interaction::Idle;
515500
}
516501
}
@@ -519,7 +504,7 @@ impl Engine for Blitz {
519504
}
520505

521506
fn scroll(&mut self, id: ViewId, delta: mouse::ScrollDelta) {
522-
let Some(view) = self.find_view_mut(id) else {
507+
let Some(view) = self.views.get_mut(id) else {
523508
return;
524509
};
525510
match delta {
@@ -536,7 +521,7 @@ impl Engine for Blitz {
536521

537522
fn goto(&mut self, id: ViewId, page_type: PageType) {
538523
let color_scheme = self.color_scheme;
539-
let Some(view) = self.find_view_mut(id) else {
524+
let Some(view) = self.views.get_mut(id) else {
540525
return;
541526
};
542527
match page_type {
@@ -569,7 +554,7 @@ impl Engine for Blitz {
569554
}
570555

571556
fn refresh(&mut self, id: ViewId) {
572-
let Some(view) = self.find_view_mut(id) else {
557+
let Some(view) = self.views.get_mut(id) else {
573558
return;
574559
};
575560
if let Some(ref mut doc) = view.document {
@@ -583,7 +568,7 @@ impl Engine for Blitz {
583568
fn go_back(&mut self, _id: ViewId) {}
584569

585570
fn get_url(&self, id: ViewId) -> String {
586-
let Some(view) = self.find_view(id) else {
571+
let Some(view) = self.views.get(id) else {
587572
return "about:blank".to_string();
588573
};
589574
if view.url.is_empty() {
@@ -594,32 +579,34 @@ impl Engine for Blitz {
594579
}
595580

596581
fn get_title(&self, id: ViewId) -> String {
597-
self.find_view(id)
582+
self.views
583+
.get(id)
598584
.map(|v| v.title.clone())
599585
.unwrap_or_default()
600586
}
601587

602588
fn get_cursor(&self, id: ViewId) -> Interaction {
603-
self.find_view(id)
589+
self.views
590+
.get(id)
604591
.map(|v| v.cursor)
605592
.unwrap_or(Interaction::Idle)
606593
}
607594

608595
fn get_view(&self, id: ViewId) -> &ImageInfo {
609596
static BLANK: std::sync::LazyLock<ImageInfo> = std::sync::LazyLock::new(ImageInfo::default);
610-
self.find_view(id).map(|v| &v.last_frame).unwrap_or(&BLANK)
597+
self.views.get(id).map(|v| &v.last_frame).unwrap_or(&BLANK)
611598
}
612599

613600
fn get_scroll_y(&self, id: ViewId) -> f32 {
614-
self.find_view(id).map(|v| v.scroll_y).unwrap_or(0.0)
601+
self.views.get(id).map(|v| v.scroll_y).unwrap_or(0.0)
615602
}
616603

617604
fn get_content_height(&self, id: ViewId) -> f32 {
618-
self.find_view(id).map(|v| v.content_height).unwrap_or(0.0)
605+
self.views.get(id).map(|v| v.content_height).unwrap_or(0.0)
619606
}
620607

621608
fn scroll_to_fragment(&mut self, id: ViewId, fragment: &str) -> bool {
622-
let Some(view) = self.find_view_mut(id) else {
609+
let Some(view) = self.views.get_mut(id) else {
623610
return false;
624611
};
625612
let doc = match view.document.as_ref() {
@@ -648,7 +635,7 @@ impl Engine for Blitz {
648635
}
649636

650637
fn take_anchor_click(&mut self, id: ViewId) -> Option<String> {
651-
self.find_view_mut(id)?.nav_capture.lock().unwrap().take()
638+
self.views.get_mut(id)?.nav_capture.lock().unwrap().take()
652639
}
653640
}
654641

0 commit comments

Comments
 (0)