mirror of
https://github.com/servo/servo
synced 2026-04-25 17:15:48 +02:00
Compare commits
5 Commits
6e4a9e85a2
...
accesskit-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c29ffd6ffa | ||
|
|
4779055946 | ||
|
|
4de0c793f1 | ||
|
|
3ad788867d | ||
|
|
5f07ea33c6 |
19
Cargo.lock
generated
19
Cargo.lock
generated
@@ -2289,8 +2289,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ecolor"
|
||||
version = "0.33.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adf31f99fad93fe83c1055b92b5c1b135f1ecfa464789817c372000e768d4bd1"
|
||||
source = "git+https://codeberg.org/shuppy/accesskit-test#053a2f7737ba6e63737fb878d67b801f6df184ba"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"emath",
|
||||
@@ -2299,8 +2298,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "egui"
|
||||
version = "0.33.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab9b5d3376c79439f53a78bf7da1e3c0b862ffa3e29f46ab0f3e107430f2e576"
|
||||
source = "git+https://codeberg.org/shuppy/accesskit-test#053a2f7737ba6e63737fb878d67b801f6df184ba"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"ahash",
|
||||
@@ -2330,8 +2328,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "egui-winit"
|
||||
version = "0.33.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb4ea8cb063c00d8f23ce11279c01eb63a195a72be0e21d429148246dab7983e"
|
||||
source = "git+https://codeberg.org/shuppy/accesskit-test#053a2f7737ba6e63737fb878d67b801f6df184ba"
|
||||
dependencies = [
|
||||
"accesskit_winit",
|
||||
"arboard",
|
||||
@@ -2396,8 +2393,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "emath"
|
||||
version = "0.33.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c615516cdceec867065f20d7db13d8eb8aedd65c9e32cc0c7c379380fa42e6e8"
|
||||
source = "git+https://codeberg.org/shuppy/accesskit-test#053a2f7737ba6e63737fb878d67b801f6df184ba"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
]
|
||||
@@ -2525,8 +2521,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "epaint"
|
||||
version = "0.33.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9926b9500ccb917adb070207ec722dd8ea78b8321f94a85ebec776f501f2930c"
|
||||
source = "git+https://codeberg.org/shuppy/accesskit-test#053a2f7737ba6e63737fb878d67b801f6df184ba"
|
||||
dependencies = [
|
||||
"ab_glyph",
|
||||
"ahash",
|
||||
@@ -2543,8 +2538,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "epaint_default_fonts"
|
||||
version = "0.33.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "66054d943c66715c6003a27a3dc152d87cadf714ef2597ccd79f550413009b97"
|
||||
source = "git+https://codeberg.org/shuppy/accesskit-test#053a2f7737ba6e63737fb878d67b801f6df184ba"
|
||||
|
||||
[[package]]
|
||||
name = "equator"
|
||||
@@ -8123,6 +8117,7 @@ dependencies = [
|
||||
"ohos-vsync",
|
||||
"ohos-window-manager-sys",
|
||||
"raw-window-handle",
|
||||
"rustc-hash 2.1.1",
|
||||
"rustls",
|
||||
"serde_json",
|
||||
"servo_allocator",
|
||||
|
||||
@@ -240,6 +240,8 @@ lto = "thin"
|
||||
codegen-units = 1
|
||||
|
||||
[patch.crates-io]
|
||||
egui = { git = "https://codeberg.org/shuppy/accesskit-test" }
|
||||
egui-winit = { git = "https://codeberg.org/shuppy/accesskit-test" }
|
||||
# If you need to temporarily test Servo with a local fork of some upstream
|
||||
# crate, add that here. Use the form:
|
||||
#
|
||||
|
||||
@@ -317,7 +317,6 @@ impl Painter {
|
||||
lcp_calculator: LargestContentfulPaintCalculator::new(),
|
||||
};
|
||||
painter.assert_gl_framebuffer_complete();
|
||||
painter.clear_background();
|
||||
painter
|
||||
}
|
||||
|
||||
|
||||
@@ -69,6 +69,7 @@ libservo = { path = "../../components/servo", features = ["background_hang_monit
|
||||
log = { workspace = true }
|
||||
mime_guess = { workspace = true }
|
||||
raw-window-handle = { workspace = true }
|
||||
rustc-hash = { workspace = true }
|
||||
rustls = { workspace = true, features = ["aws-lc-rs"] }
|
||||
tokio = { workspace = true }
|
||||
tracing = { workspace = true, optional = true }
|
||||
|
||||
@@ -8,13 +8,17 @@ use std::sync::Arc;
|
||||
use std::time::Instant;
|
||||
|
||||
use dpi::PhysicalSize;
|
||||
use egui::accesskit::{Node, NodeId, Role, TreeUpdate};
|
||||
use egui::text::{CCursor, CCursorRange};
|
||||
use egui::text_edit::TextEditState;
|
||||
use egui::{Button, Key, Label, LayerId, Modifiers, PaintCallback, TopBottomPanel, Vec2, pos2};
|
||||
use egui::{
|
||||
Button, CentralPanel, Frame, Key, Label, Modifiers, PaintCallback, TopBottomPanel, Vec2, pos2,
|
||||
};
|
||||
use egui_glow::{CallbackFn, EguiGlow};
|
||||
use egui_winit::EventResponse;
|
||||
use egui_winit::{EventResponse, accesskit_winit};
|
||||
use euclid::{Box2D, Length, Point2D, Rect, Scale, Size2D};
|
||||
use log::{trace, warn};
|
||||
use rustc_hash::FxHasher;
|
||||
use servo::base::id::WebViewId;
|
||||
use servo::servo_geometry::DeviceIndependentPixel;
|
||||
use servo::servo_url::ServoUrl;
|
||||
@@ -59,6 +63,9 @@ pub struct Minibrowser {
|
||||
|
||||
/// Whether the user has enabled experimental preferences.
|
||||
experimental_prefs_enabled: bool,
|
||||
|
||||
root_accesskit_node_id: Option<egui::accesskit::NodeId>,
|
||||
accesskit_adapter: accesskit_winit::Adapter,
|
||||
}
|
||||
|
||||
pub enum MinibrowserEvent {
|
||||
@@ -87,6 +94,19 @@ impl Drop for Minibrowser {
|
||||
}
|
||||
}
|
||||
|
||||
trait NodeIdExt {
|
||||
fn with(self, child: impl std::hash::Hash) -> Self;
|
||||
}
|
||||
impl NodeIdExt for NodeId {
|
||||
fn with(self, child: impl std::hash::Hash) -> Self {
|
||||
use std::hash::Hasher as _;
|
||||
let mut hasher = FxHasher::with_seed(0);
|
||||
hasher.write_u64(self.0);
|
||||
child.hash(&mut hasher);
|
||||
Self(hasher.finish())
|
||||
}
|
||||
}
|
||||
|
||||
impl Minibrowser {
|
||||
pub fn new(
|
||||
window: &ServoWindow,
|
||||
@@ -108,7 +128,7 @@ impl Minibrowser {
|
||||
let winit_window = window.winit_window().unwrap();
|
||||
context
|
||||
.egui_winit
|
||||
.init_accesskit(event_loop, winit_window, event_loop_proxy);
|
||||
.init_accesskit(event_loop, winit_window, event_loop_proxy.clone());
|
||||
winit_window.set_visible(true);
|
||||
|
||||
context.egui_ctx.options_mut(|options| {
|
||||
@@ -134,6 +154,12 @@ impl Minibrowser {
|
||||
status_text: None,
|
||||
favicon_textures: Default::default(),
|
||||
experimental_prefs_enabled: preferences.experimental_prefs_enabled,
|
||||
root_accesskit_node_id: None,
|
||||
accesskit_adapter: accesskit_winit::Adapter::with_event_loop_proxy(
|
||||
event_loop,
|
||||
winit_window,
|
||||
event_loop_proxy,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,6 +167,9 @@ impl Minibrowser {
|
||||
std::mem::take(&mut self.event_queue)
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn on_window_event(
|
||||
&mut self,
|
||||
window: &Window,
|
||||
@@ -149,6 +178,16 @@ impl Minibrowser {
|
||||
) -> EventResponse {
|
||||
let mut result = self.context.on_window_event(window, event);
|
||||
|
||||
// For some reason, egui is eating all PinchGesture events, even when they happen
|
||||
// on top of a WebView. Detect this situation and avoid sending those events to
|
||||
// egui.
|
||||
if matches!(event, WindowEvent::PinchGesture { .. }) &&
|
||||
self.last_mouse_position
|
||||
.is_some_and(|point| !self.is_in_egui_toolbar_rect(point))
|
||||
{
|
||||
return Default::default();
|
||||
}
|
||||
|
||||
if app_state.has_active_dialog() {
|
||||
result.consumed = true;
|
||||
return result;
|
||||
@@ -281,6 +320,8 @@ impl Minibrowser {
|
||||
}
|
||||
|
||||
/// Update the minibrowser, but don’t paint.
|
||||
/// If `servo_framebuffer_id` is given, set up a paint callback to blit its contents to our
|
||||
/// CentralPanel when [`Minibrowser::paint`] is called.
|
||||
pub fn update(
|
||||
&mut self,
|
||||
window: &dyn WindowPortsMethods,
|
||||
@@ -434,51 +475,115 @@ impl Minibrowser {
|
||||
let scale =
|
||||
Scale::<_, DeviceIndependentPixel, DevicePixel>::new(ctx.pixels_per_point());
|
||||
|
||||
state.for_each_active_dialog(|dialog| dialog.update(ctx));
|
||||
egui::CentralPanel::default().show(ctx, |_| {
|
||||
state.for_each_active_dialog(|dialog| dialog.update(ctx));
|
||||
});
|
||||
|
||||
// If the top parts of the GUI changed size, then update the size of the WebView and also
|
||||
// the size of its RenderingContext.
|
||||
let rect = ctx.available_rect();
|
||||
let size = Size2D::new(rect.width(), rect.height()) * scale;
|
||||
let rect = Box2D::from_origin_and_size(Point2D::origin(), size);
|
||||
if let Some(webview) = state.focused_webview() &&
|
||||
rect != webview.rect()
|
||||
{
|
||||
webview.move_resize(rect);
|
||||
// `rect` is sized to just the WebView viewport, which is required by
|
||||
// `OffscreenRenderingContext` See:
|
||||
// <https://github.com/servo/servo/issues/38369#issuecomment-3138378527>
|
||||
webview.resize(PhysicalSize::new(size.width as u32, size.height as u32))
|
||||
}
|
||||
let Some(webview) = state.focused_webview() else {
|
||||
return;
|
||||
};
|
||||
CentralPanel::default().frame(Frame::NONE).show(ctx, |ui| {
|
||||
// If the top parts of the GUI changed size, then update the size of the WebView and also
|
||||
// the size of its RenderingContext.
|
||||
let available_size = ui.available_size();
|
||||
let size = Size2D::new(available_size.x, available_size.y) * scale;
|
||||
let rect = Box2D::from_origin_and_size(Point2D::origin(), size);
|
||||
if rect != webview.rect() {
|
||||
webview.move_resize(rect);
|
||||
// `rect` is sized to just the WebView viewport, which is required by
|
||||
// `OffscreenRenderingContext` See:
|
||||
// <https://github.com/servo/servo/issues/38369#issuecomment-3138378527>
|
||||
webview.resize(PhysicalSize::new(size.width as u32, size.height as u32))
|
||||
}
|
||||
|
||||
if let Some(status_text) = &self.status_text {
|
||||
egui::Tooltip::always_open(
|
||||
ctx.clone(),
|
||||
LayerId::background(),
|
||||
"tooltip layer".into(),
|
||||
pos2(0.0, ctx.available_rect().max.y),
|
||||
)
|
||||
.show(|ui| ui.add(Label::new(status_text.clone()).extend()));
|
||||
}
|
||||
let min = ui.cursor().min;
|
||||
let size = ui.available_size();
|
||||
let rect = egui::Rect::from_min_size(min, size);
|
||||
ui.allocate_space(size);
|
||||
|
||||
state.repaint_servo_if_necessary();
|
||||
if let Some(status_text) = &self.status_text {
|
||||
egui::Tooltip::always_open(
|
||||
ctx.clone(),
|
||||
ui.layer_id(),
|
||||
"tooltip layer".into(),
|
||||
pos2(0.0, ctx.available_rect().max.y),
|
||||
)
|
||||
.show(|ui| ui.add(Label::new(status_text.clone()).extend()))
|
||||
.map(|response| response.inner);
|
||||
}
|
||||
|
||||
if let Some(render_to_parent) = rendering_context.render_to_parent_callback() {
|
||||
ctx.layer_painter(LayerId::background()).add(PaintCallback {
|
||||
rect: ctx.available_rect(),
|
||||
callback: Arc::new(CallbackFn::new(move |info, painter| {
|
||||
let clip = info.viewport_in_pixels();
|
||||
let rect_in_parent = Rect::new(
|
||||
Point2D::new(clip.left_px, clip.from_bottom_px),
|
||||
Size2D::new(clip.width_px, clip.height_px),
|
||||
);
|
||||
render_to_parent(painter.gl(), rect_in_parent)
|
||||
})),
|
||||
state.repaint_servo_if_necessary();
|
||||
|
||||
if let Some(render_to_parent) = rendering_context.render_to_parent_callback() {
|
||||
ui.painter().add(PaintCallback {
|
||||
rect,
|
||||
callback: Arc::new(CallbackFn::new(move |info, painter| {
|
||||
let clip = info.viewport_in_pixels();
|
||||
let rect_in_parent = Rect::new(
|
||||
Point2D::new(clip.left_px, clip.from_bottom_px),
|
||||
Size2D::new(clip.width_px, clip.height_px),
|
||||
);
|
||||
render_to_parent(painter.gl(), rect_in_parent)
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
let first_update = self.root_accesskit_node_id.is_none();
|
||||
|
||||
ctx.accesskit_subtree_builder(ui.id(), |node, accesskit_state| {
|
||||
node.set_role(Role::Group);
|
||||
|
||||
// egui sends a TreeUpdate on every tick with all of the nodes it knows about.
|
||||
// but TreeUpdate can be used incrementally, so we can take advantage of that
|
||||
// to send updates to Servo’s accessibility subtree on our own schedule.
|
||||
let root_accesskit_node_id = self.root_accesskit_node_id.get_or_insert_with(|| {
|
||||
// the first time only, we tell accesskit about the root of our tree using
|
||||
// a dummy node, which we can later update however we like.
|
||||
let child = Node::default();
|
||||
let child_id = ui.id().with(1);
|
||||
accesskit_state.nodes.insert(child_id, child);
|
||||
child_id.value().into()
|
||||
});
|
||||
// to ensure that the boundary between egui’s tree and our tree doesn’t get
|
||||
// clobbered, we need to tell egui to include the root of our tree at the node
|
||||
// where they meet. then we can do what we want with that root.
|
||||
node.push_child(*root_accesskit_node_id);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
*last_update = now;
|
||||
});
|
||||
|
||||
if let Some(root_id) = self.root_accesskit_node_id {
|
||||
if let Some(accesskit_mut) = self.context.egui_winit.accesskit_mut() {
|
||||
accesskit_mut.update_if_active(|| {
|
||||
let mut root = Node::default();
|
||||
root.set_role(Role::WebView);
|
||||
let a_id = root_id.with(1);
|
||||
let mut a = Node::default();
|
||||
a.set_role(Role::Button);
|
||||
let b_id = root_id.with(2);
|
||||
let mut b = Node::default();
|
||||
b.set_role(Role::Button);
|
||||
let c_id = root_id.with(3);
|
||||
let mut c = Node::default();
|
||||
c.set_role(Role::Button);
|
||||
root.set_children(vec![a_id, b_id, c_id]);
|
||||
TreeUpdate {
|
||||
nodes: vec![
|
||||
(root_id, root),
|
||||
(a_id, a),
|
||||
(b_id, b),
|
||||
(c_id, c),
|
||||
],
|
||||
tree: None,
|
||||
// TODO: this needs to align with the focus in egui’s updates,
|
||||
// unless the focus has genuinely changed
|
||||
focus: b_id,
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Paint the minibrowser, as of the last update.
|
||||
|
||||
Reference in New Issue
Block a user