mirror of
https://github.com/servo/servo.git
synced 2025-08-05 21:50:18 +01:00
minibrowser: Add loading spinner (#31713)
* minibrowser: Rename "history_changed" flag to "need_update" There are other data points in the toolbar that might need to be updated. This commit prepares for that by generaliziing the "history_changed" flag to a more generic "need_update" flag. Signed-off-by: Frederik Reiter <hi@frereit.de> * minibrowser: Add spinner to indicate loading status of the webview Signed-off-by: Frederik Reiter <hi@frereit.de> --------- Signed-off-by: Frederik Reiter <hi@frereit.de>
This commit is contained in:
parent
5f65a09d3a
commit
585e0d69cd
3 changed files with 70 additions and 20 deletions
|
@ -51,7 +51,7 @@ enum PumpResult {
|
||||||
/// The caller should shut down Servo and its related context.
|
/// The caller should shut down Servo and its related context.
|
||||||
Shutdown,
|
Shutdown,
|
||||||
Continue {
|
Continue {
|
||||||
history_changed: bool,
|
update: bool,
|
||||||
present: Present,
|
present: Present,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -298,14 +298,11 @@ impl App {
|
||||||
minibrowser.context.destroy();
|
minibrowser.context.destroy();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
PumpResult::Continue {
|
PumpResult::Continue { update, present } => {
|
||||||
history_changed,
|
if update {
|
||||||
present,
|
|
||||||
} => {
|
|
||||||
if history_changed {
|
|
||||||
if let Some(mut minibrowser) = app.minibrowser() {
|
if let Some(mut minibrowser) = app.minibrowser() {
|
||||||
let webviews = &mut app.webviews.borrow_mut();
|
let webviews = &mut app.webviews.borrow_mut();
|
||||||
if minibrowser.update_location_in_toolbar(webviews) {
|
if minibrowser.update_webview_data(webviews) {
|
||||||
// Update the minibrowser immediately. While we could update by requesting a
|
// Update the minibrowser immediately. While we could update by requesting a
|
||||||
// redraw, doing so would delay the location update by two frames.
|
// redraw, doing so would delay the location update by two frames.
|
||||||
minibrowser.update(
|
minibrowser.update(
|
||||||
|
@ -432,12 +429,12 @@ impl App {
|
||||||
let mut embedder_messages = self.servo.as_mut().unwrap().get_events();
|
let mut embedder_messages = self.servo.as_mut().unwrap().get_events();
|
||||||
let mut need_resize = false;
|
let mut need_resize = false;
|
||||||
let mut need_present = false;
|
let mut need_present = false;
|
||||||
let mut history_changed = false;
|
let mut need_update = false;
|
||||||
loop {
|
loop {
|
||||||
// Consume and handle those embedder messages.
|
// Consume and handle those embedder messages.
|
||||||
let servo_event_response = webviews.handle_servo_events(embedder_messages);
|
let servo_event_response = webviews.handle_servo_events(embedder_messages);
|
||||||
need_present |= servo_event_response.need_present;
|
need_present |= servo_event_response.need_present;
|
||||||
history_changed |= servo_event_response.history_changed;
|
need_update |= servo_event_response.need_update;
|
||||||
|
|
||||||
// Route embedder events from the WebViewManager to the relevant Servo components,
|
// Route embedder events from the WebViewManager to the relevant Servo components,
|
||||||
// receives and collects embedder messages from various Servo components,
|
// receives and collects embedder messages from various Servo components,
|
||||||
|
@ -467,7 +464,7 @@ impl App {
|
||||||
};
|
};
|
||||||
|
|
||||||
PumpResult::Continue {
|
PumpResult::Continue {
|
||||||
history_changed,
|
update: need_update,
|
||||||
present,
|
present,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ use std::num::NonZeroU32;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
use egui::{CentralPanel, Frame, Key, Modifiers, PaintCallback, TopBottomPanel};
|
use egui::{CentralPanel, Color32, Frame, Key, Modifiers, PaintCallback, Spinner, TopBottomPanel};
|
||||||
use egui_glow::CallbackFn;
|
use egui_glow::CallbackFn;
|
||||||
use egui_winit::EventResponse;
|
use egui_winit::EventResponse;
|
||||||
use euclid::{Length, Point2D, Scale};
|
use euclid::{Length, Point2D, Scale};
|
||||||
|
@ -24,7 +24,7 @@ use crate::egui_glue::EguiGlow;
|
||||||
use crate::events_loop::EventsLoop;
|
use crate::events_loop::EventsLoop;
|
||||||
use crate::geometry::winit_position_to_euclid_point;
|
use crate::geometry::winit_position_to_euclid_point;
|
||||||
use crate::parser::location_bar_input_to_url;
|
use crate::parser::location_bar_input_to_url;
|
||||||
use crate::webview::WebViewManager;
|
use crate::webview::{LoadStatus, WebViewManager};
|
||||||
use crate::window_trait::WindowPortsMethods;
|
use crate::window_trait::WindowPortsMethods;
|
||||||
|
|
||||||
pub struct Minibrowser {
|
pub struct Minibrowser {
|
||||||
|
@ -42,6 +42,8 @@ pub struct Minibrowser {
|
||||||
|
|
||||||
/// Whether the location has been edited by the user without clicking Go.
|
/// Whether the location has been edited by the user without clicking Go.
|
||||||
location_dirty: Cell<bool>,
|
location_dirty: Cell<bool>,
|
||||||
|
|
||||||
|
load_status: LoadStatus,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum MinibrowserEvent {
|
pub enum MinibrowserEvent {
|
||||||
|
@ -83,6 +85,7 @@ impl Minibrowser {
|
||||||
last_mouse_position: None,
|
last_mouse_position: None,
|
||||||
location: RefCell::new(initial_url.to_string()),
|
location: RefCell::new(initial_url.to_string()),
|
||||||
location_dirty: false.into(),
|
location_dirty: false.into(),
|
||||||
|
load_status: LoadStatus::LoadComplete,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,6 +165,16 @@ impl Minibrowser {
|
||||||
location_dirty.set(false);
|
location_dirty.set(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
match self.load_status {
|
||||||
|
LoadStatus::LoadStart => {
|
||||||
|
ui.add(Spinner::new().color(Color32::GRAY));
|
||||||
|
},
|
||||||
|
LoadStatus::HeadParsed => {
|
||||||
|
ui.add(Spinner::new().color(Color32::WHITE));
|
||||||
|
},
|
||||||
|
LoadStatus::LoadComplete => { /* No Spinner */ },
|
||||||
|
}
|
||||||
|
|
||||||
let location_field = ui.add_sized(
|
let location_field = ui.add_sized(
|
||||||
ui.available_size(),
|
ui.available_size(),
|
||||||
egui::TextEdit::singleline(&mut *location.borrow_mut()),
|
egui::TextEdit::singleline(&mut *location.borrow_mut()),
|
||||||
|
@ -314,4 +327,28 @@ impl Minibrowser {
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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 {
|
||||||
|
let need_update = browser.load_status() != self.load_status;
|
||||||
|
self.load_status = browser.load_status();
|
||||||
|
return need_update;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 {
|
||||||
|
// 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
|
||||||
|
// does not short-circuit.
|
||||||
|
return self.update_location_in_toolbar(browser) | self.update_spinner_in_toolbar(browser);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,7 @@ pub struct WebViewManager<Window: WindowPortsMethods + ?Sized> {
|
||||||
clipboard: Option<Clipboard>,
|
clipboard: Option<Clipboard>,
|
||||||
gamepad: Option<Gilrs>,
|
gamepad: Option<Gilrs>,
|
||||||
shutdown_requested: bool,
|
shutdown_requested: bool,
|
||||||
|
load_status: LoadStatus,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -63,7 +64,14 @@ pub struct WebView {}
|
||||||
|
|
||||||
pub struct ServoEventResponse {
|
pub struct ServoEventResponse {
|
||||||
pub need_present: bool,
|
pub need_present: bool,
|
||||||
pub history_changed: bool,
|
pub need_update: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub enum LoadStatus {
|
||||||
|
HeadParsed,
|
||||||
|
LoadStart,
|
||||||
|
LoadComplete,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Window> WebViewManager<Window>
|
impl<Window> WebViewManager<Window>
|
||||||
|
@ -95,6 +103,7 @@ where
|
||||||
},
|
},
|
||||||
event_queue: Vec::new(),
|
event_queue: Vec::new(),
|
||||||
shutdown_requested: false,
|
shutdown_requested: false,
|
||||||
|
load_status: LoadStatus::LoadComplete,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,6 +115,10 @@ where
|
||||||
self.current_url_string.as_deref()
|
self.current_url_string.as_deref()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn load_status(&self) -> LoadStatus {
|
||||||
|
self.load_status
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_events(&mut self) -> Vec<EmbedderEvent> {
|
pub fn get_events(&mut self) -> Vec<EmbedderEvent> {
|
||||||
std::mem::take(&mut self.event_queue)
|
std::mem::take(&mut self.event_queue)
|
||||||
}
|
}
|
||||||
|
@ -409,8 +422,8 @@ where
|
||||||
&mut self,
|
&mut self,
|
||||||
events: Drain<'_, (Option<WebViewId>, EmbedderMsg)>,
|
events: Drain<'_, (Option<WebViewId>, EmbedderMsg)>,
|
||||||
) -> ServoEventResponse {
|
) -> ServoEventResponse {
|
||||||
let mut need_present = false;
|
let mut need_present = self.load_status != LoadStatus::LoadComplete;
|
||||||
let mut history_changed = false;
|
let mut need_update = false;
|
||||||
for (webview_id, msg) in events {
|
for (webview_id, msg) in events {
|
||||||
if let Some(webview_id) = webview_id {
|
if let Some(webview_id) = webview_id {
|
||||||
trace_embedder_msg!(msg, "{webview_id} {msg:?}");
|
trace_embedder_msg!(msg, "{webview_id} {msg:?}");
|
||||||
|
@ -594,21 +607,24 @@ where
|
||||||
// FIXME: show favicons in the UI somehow
|
// FIXME: show favicons in the UI somehow
|
||||||
},
|
},
|
||||||
EmbedderMsg::HeadParsed => {
|
EmbedderMsg::HeadParsed => {
|
||||||
// FIXME: surface the loading state in the UI somehow
|
self.load_status = LoadStatus::HeadParsed;
|
||||||
|
need_update = true;
|
||||||
},
|
},
|
||||||
EmbedderMsg::HistoryChanged(urls, current) => {
|
EmbedderMsg::HistoryChanged(urls, current) => {
|
||||||
self.current_url = Some(urls[current].clone());
|
self.current_url = Some(urls[current].clone());
|
||||||
self.current_url_string = Some(urls[current].clone().into_string());
|
self.current_url_string = Some(urls[current].clone().into_string());
|
||||||
history_changed = true;
|
need_update = true;
|
||||||
},
|
},
|
||||||
EmbedderMsg::SetFullscreenState(state) => {
|
EmbedderMsg::SetFullscreenState(state) => {
|
||||||
self.window.set_fullscreen(state);
|
self.window.set_fullscreen(state);
|
||||||
},
|
},
|
||||||
EmbedderMsg::LoadStart => {
|
EmbedderMsg::LoadStart => {
|
||||||
// FIXME: surface the loading state in the UI somehow
|
self.load_status = LoadStatus::LoadStart;
|
||||||
|
need_update = true;
|
||||||
},
|
},
|
||||||
EmbedderMsg::LoadComplete => {
|
EmbedderMsg::LoadComplete => {
|
||||||
// FIXME: surface the loading state in the UI somehow
|
self.load_status = LoadStatus::LoadComplete;
|
||||||
|
need_update = true;
|
||||||
},
|
},
|
||||||
EmbedderMsg::Shutdown => {
|
EmbedderMsg::Shutdown => {
|
||||||
self.shutdown_requested = true;
|
self.shutdown_requested = true;
|
||||||
|
@ -680,7 +696,7 @@ where
|
||||||
|
|
||||||
ServoEventResponse {
|
ServoEventResponse {
|
||||||
need_present,
|
need_present,
|
||||||
history_changed,
|
need_update,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue