mirror of
https://github.com/servo/servo.git
synced 2025-10-04 02:29:12 +01:00
libservo: Port desktop servoshell to use the new WebView
API (#35183)
This removes all uses of `EmbedderEvent` in the desktop servoshell to use the new `WebView` API -- filling it out when necessary. Signed-off-by: Martin Robinson <mrobinson@igalia.com> Co-authored-by: Delan Azabani <dazabani@igalia.com>
This commit is contained in:
parent
a1cf0cbf86
commit
9dbc942bee
15 changed files with 826 additions and 798 deletions
|
@ -20,22 +20,18 @@ use gleam::gl;
|
|||
use glow::NativeFramebuffer;
|
||||
use log::{trace, warn};
|
||||
use servo::base::id::WebViewId;
|
||||
use servo::compositing::windowing::EmbedderEvent;
|
||||
use servo::servo_geometry::DeviceIndependentPixel;
|
||||
use servo::servo_url::ServoUrl;
|
||||
use servo::webrender_api::units::DevicePixel;
|
||||
use servo::webrender_traits::SurfmanRenderingContext;
|
||||
use servo::{TopLevelBrowsingContextId, TraversalDirection};
|
||||
use servo::Servo;
|
||||
use winit::event::{ElementState, MouseButton, WindowEvent};
|
||||
use winit::event_loop::ActiveEventLoop;
|
||||
use winit::window::Window;
|
||||
|
||||
use super::egui_glue::EguiGlow;
|
||||
use super::geometry::winit_position_to_euclid_point;
|
||||
use super::webview::{LoadStatus, WebViewManager};
|
||||
use super::window_trait::WindowPortsMethods;
|
||||
use crate::parser::location_bar_input_to_url;
|
||||
use crate::prefs::ServoShellPreferences;
|
||||
use super::webview::{LoadStatus, WebView, WebViewManager};
|
||||
|
||||
pub struct Minibrowser {
|
||||
pub context: EguiGlow,
|
||||
|
@ -60,11 +56,12 @@ pub struct Minibrowser {
|
|||
|
||||
pub enum MinibrowserEvent {
|
||||
/// Go button clicked.
|
||||
Go,
|
||||
Go(String),
|
||||
Back,
|
||||
Forward,
|
||||
Reload,
|
||||
NewWebView,
|
||||
CloseWebView(WebViewId),
|
||||
}
|
||||
|
||||
fn truncate_with_ellipsis(input: &str, max_length: usize) -> String {
|
||||
|
@ -116,6 +113,10 @@ impl Minibrowser {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn take_events(&self) -> Vec<MinibrowserEvent> {
|
||||
self.event_queue.take()
|
||||
}
|
||||
|
||||
/// Preprocess the given [winit::event::WindowEvent], returning unconsumed for mouse events in
|
||||
/// the Servo browser rect. This is needed because the CentralPanel we create for our webview
|
||||
/// would otherwise make egui report events in that area as consumed.
|
||||
|
@ -169,15 +170,15 @@ impl Minibrowser {
|
|||
.min_size(Vec2 { x: 20.0, y: 20.0 })
|
||||
}
|
||||
|
||||
/// Draws a browser tab, checking for clicks and returns an appropriate [EmbedderEvent]
|
||||
/// Draws a browser tab, checking for clicks and queues appropriate `MinibrowserEvent`s.
|
||||
/// Using a custom widget here would've been nice, but it doesn't seem as though egui
|
||||
/// supports that, so we arrange multiple Widgets in a way that they look connected.
|
||||
fn browser_tab(
|
||||
ui: &mut egui::Ui,
|
||||
label: &str,
|
||||
selected: bool,
|
||||
webview_id: TopLevelBrowsingContextId,
|
||||
) -> Option<EmbedderEvent> {
|
||||
webview: &WebView,
|
||||
event_queue: &mut Vec<MinibrowserEvent>,
|
||||
) {
|
||||
let old_item_spacing = ui.spacing().item_spacing;
|
||||
let old_visuals = ui.visuals().clone();
|
||||
let active_bg_color = old_visuals.widgets.active.weak_bg_fill;
|
||||
|
@ -213,6 +214,7 @@ impl Minibrowser {
|
|||
visuals.widgets.hovered.rounding = rounding;
|
||||
visuals.widgets.inactive.rounding = rounding;
|
||||
|
||||
let selected = webview.focused;
|
||||
let tab = ui.add(SelectableLabel::new(
|
||||
selected,
|
||||
truncate_with_ellipsis(label, 20),
|
||||
|
@ -242,11 +244,9 @@ impl Minibrowser {
|
|||
let close_button = ui.add(egui::Button::new("X").fill(fill_color));
|
||||
*ui.visuals_mut() = old_visuals;
|
||||
if close_button.clicked() || close_button.middle_clicked() || tab.middle_clicked() {
|
||||
Some(EmbedderEvent::CloseWebView(webview_id))
|
||||
event_queue.push(MinibrowserEvent::CloseWebView(webview.servo_webview.id()))
|
||||
} else if !selected && tab.clicked() {
|
||||
Some(EmbedderEvent::FocusWebView(webview_id))
|
||||
} else {
|
||||
None
|
||||
webview.servo_webview.focus();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -256,8 +256,8 @@ impl Minibrowser {
|
|||
pub fn update(
|
||||
&mut self,
|
||||
window: &Window,
|
||||
webviews: &mut WebViewManager<dyn WindowPortsMethods>,
|
||||
servo_framebuffer_id: Option<gl::GLuint>,
|
||||
webviews: &mut WebViewManager,
|
||||
servo: Option<&mut Servo>,
|
||||
reason: &'static str,
|
||||
) {
|
||||
let now = Instant::now();
|
||||
|
@ -277,6 +277,9 @@ impl Minibrowser {
|
|||
..
|
||||
} = self;
|
||||
let widget_fbo = *widget_surface_fbo;
|
||||
let servo_framebuffer_id = servo
|
||||
.as_ref()
|
||||
.and_then(|servo| servo.offscreen_framebuffer_id());
|
||||
let _duration = context.run(window, |ctx| {
|
||||
// TODO: While in fullscreen add some way to mitigate the increased phishing risk
|
||||
// when not displaying the URL bar: https://github.com/servo/servo/issues/32443
|
||||
|
@ -342,8 +345,9 @@ impl Minibrowser {
|
|||
if location_field.lost_focus() &&
|
||||
ui.input(|i| i.clone().key_pressed(Key::Enter))
|
||||
{
|
||||
event_queue.borrow_mut().push(MinibrowserEvent::Go);
|
||||
location_dirty.set(false);
|
||||
event_queue
|
||||
.borrow_mut()
|
||||
.push(MinibrowserEvent::Go(location.borrow().clone()));
|
||||
}
|
||||
},
|
||||
);
|
||||
|
@ -352,26 +356,19 @@ impl Minibrowser {
|
|||
});
|
||||
};
|
||||
|
||||
let mut embedder_events = vec![];
|
||||
|
||||
// A simple Tab header strip
|
||||
TopBottomPanel::top("tabs").show(ctx, |ui| {
|
||||
ui.allocate_ui_with_layout(
|
||||
ui.available_size(),
|
||||
egui::Layout::left_to_right(egui::Align::Center),
|
||||
|ui| {
|
||||
for (webview_id, webview) in webviews.webviews().into_iter() {
|
||||
for (_, webview) in webviews.webviews().into_iter() {
|
||||
let label = match (&webview.title, &webview.url) {
|
||||
(Some(title), _) if !title.is_empty() => title,
|
||||
(_, Some(url)) => &url.to_string(),
|
||||
_ => "New Tab",
|
||||
};
|
||||
if let Some(event) =
|
||||
Self::browser_tab(ui, label, webview.focused, webview_id)
|
||||
{
|
||||
location_dirty.set(false);
|
||||
embedder_events.push(event);
|
||||
}
|
||||
Self::browser_tab(ui, label, webview, &mut event_queue.borrow_mut());
|
||||
}
|
||||
if ui.add(Minibrowser::toolbar_button("+")).clicked() {
|
||||
event_queue.borrow_mut().push(MinibrowserEvent::NewWebView);
|
||||
|
@ -408,8 +405,7 @@ impl Minibrowser {
|
|||
) * scale;
|
||||
if rect != webview.rect {
|
||||
webview.rect = rect;
|
||||
embedder_events
|
||||
.push(EmbedderEvent::MoveResizeWebView(focused_webview_id, rect));
|
||||
webview.servo_webview.move_resize(rect)
|
||||
}
|
||||
let min = ui.cursor().min;
|
||||
let size = ui.available_size();
|
||||
|
@ -471,10 +467,6 @@ impl Minibrowser {
|
|||
});
|
||||
});
|
||||
|
||||
if !embedder_events.is_empty() {
|
||||
webviews.handle_window_events(embedder_events);
|
||||
}
|
||||
|
||||
*last_update = now;
|
||||
});
|
||||
}
|
||||
|
@ -491,58 +483,9 @@ impl Minibrowser {
|
|||
self.context.paint(window);
|
||||
}
|
||||
|
||||
/// Takes any outstanding events from the [Minibrowser], converting them to [EmbedderEvent] and
|
||||
/// routing those to the App event queue.
|
||||
pub fn queue_embedder_events_for_minibrowser_events(
|
||||
&self,
|
||||
browser: &WebViewManager<dyn WindowPortsMethods>,
|
||||
app_event_queue: &mut Vec<EmbedderEvent>,
|
||||
preferences: &ServoShellPreferences,
|
||||
) {
|
||||
for event in self.event_queue.borrow_mut().drain(..) {
|
||||
let browser_id = browser.focused_webview_id().unwrap();
|
||||
match event {
|
||||
MinibrowserEvent::Go => {
|
||||
let location = self.location.borrow();
|
||||
let Some(url) =
|
||||
location_bar_input_to_url(&location.clone(), &preferences.searchpage)
|
||||
else {
|
||||
warn!("failed to parse location");
|
||||
break;
|
||||
};
|
||||
app_event_queue.push(EmbedderEvent::LoadUrl(browser_id, url));
|
||||
},
|
||||
MinibrowserEvent::Back => {
|
||||
app_event_queue.push(EmbedderEvent::Navigation(
|
||||
browser_id,
|
||||
TraversalDirection::Back(1),
|
||||
));
|
||||
},
|
||||
MinibrowserEvent::Forward => {
|
||||
app_event_queue.push(EmbedderEvent::Navigation(
|
||||
browser_id,
|
||||
TraversalDirection::Forward(1),
|
||||
));
|
||||
},
|
||||
MinibrowserEvent::Reload => {
|
||||
let browser_id = browser.focused_webview_id().unwrap();
|
||||
app_event_queue.push(EmbedderEvent::Reload(browser_id));
|
||||
},
|
||||
MinibrowserEvent::NewWebView => {
|
||||
self.location_dirty.set(false);
|
||||
let url = ServoUrl::parse("servo:newtab").unwrap();
|
||||
app_event_queue.push(EmbedderEvent::NewWebView(url, WebViewId::new()));
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Updates the location field from the given [WebViewManager], unless the user has started
|
||||
/// editing it without clicking Go, returning true iff it has changed (needing an egui update).
|
||||
pub fn update_location_in_toolbar(
|
||||
&mut self,
|
||||
browser: &mut WebViewManager<dyn WindowPortsMethods>,
|
||||
) -> bool {
|
||||
pub fn update_location_in_toolbar(&mut self, browser: &mut WebViewManager) -> bool {
|
||||
// User edited without clicking Go?
|
||||
if self.location_dirty.get() {
|
||||
return false;
|
||||
|
@ -557,21 +500,19 @@ impl Minibrowser {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn update_location_dirty(&self, dirty: bool) {
|
||||
self.location_dirty.set(dirty);
|
||||
}
|
||||
|
||||
/// Updates the spinner from the given [WebViewManager], returning true iff it has changed
|
||||
/// (needing an egui update).
|
||||
pub fn update_spinner_in_toolbar(
|
||||
&mut self,
|
||||
browser: &mut WebViewManager<dyn WindowPortsMethods>,
|
||||
) -> bool {
|
||||
pub fn update_spinner_in_toolbar(&mut self, browser: &mut WebViewManager) -> bool {
|
||||
let need_update = browser.load_status() != self.load_status;
|
||||
self.load_status = browser.load_status();
|
||||
need_update
|
||||
}
|
||||
|
||||
pub fn update_status_text(
|
||||
&mut self,
|
||||
browser: &mut WebViewManager<dyn WindowPortsMethods>,
|
||||
) -> bool {
|
||||
pub fn update_status_text(&mut self, browser: &mut WebViewManager) -> bool {
|
||||
let need_update = browser.status_text() != self.status_text;
|
||||
self.status_text = browser.status_text();
|
||||
need_update
|
||||
|
@ -579,10 +520,7 @@ impl Minibrowser {
|
|||
|
||||
/// Updates all fields taken from the given [WebViewManager], such as the location field.
|
||||
/// Returns true iff the egui needs an update.
|
||||
pub fn update_webview_data(
|
||||
&mut self,
|
||||
browser: &mut WebViewManager<dyn WindowPortsMethods>,
|
||||
) -> bool {
|
||||
pub fn update_webview_data(&mut self, browser: &mut WebViewManager) -> bool {
|
||||
// Note: We must use the "bitwise OR" (|) operator here instead of "logical OR" (||)
|
||||
// because logical OR would short-circuit if any of the functions return true.
|
||||
// We want to ensure that all functions are called. The "bitwise OR" operator
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue