mirror of
https://github.com/RGBCube/serenity
synced 2025-07-24 21:47:43 +00:00
Ladybird+LibWeb: Add MouseEvent screenX and screenY support
This commit is contained in:
parent
e584189b8f
commit
836a7b00dd
19 changed files with 125 additions and 103 deletions
|
@ -10,7 +10,7 @@
|
||||||
<script>
|
<script>
|
||||||
function handle(e) {
|
function handle(e) {
|
||||||
var out = document.getElementById('out');
|
var out = document.getElementById('out');
|
||||||
out.innerHTML = e.type + ' @ ' + e.offsetX + ',' + e.y + '\n' + out.innerHTML;
|
out.innerHTML = `${e.type} | client: ${e.clientX}x${e.y} | screen: ${e.screenX}x${e.screenY}\n${out.innerHTML}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
var rect = document.getElementById('rect');
|
var rect = document.getElementById('rect');
|
||||||
|
|
|
@ -16,6 +16,7 @@ namespace Ladybird {
|
||||||
|
|
||||||
struct MouseEvent {
|
struct MouseEvent {
|
||||||
Gfx::IntPoint position {};
|
Gfx::IntPoint position {};
|
||||||
|
Gfx::IntPoint screen_position {};
|
||||||
GUI::MouseButton button { GUI::MouseButton::Primary };
|
GUI::MouseButton button { GUI::MouseButton::Primary };
|
||||||
KeyModifier modifiers { KeyModifier::Mod_None };
|
KeyModifier modifiers { KeyModifier::Mod_None };
|
||||||
};
|
};
|
||||||
|
|
|
@ -39,9 +39,10 @@ static KeyModifier ns_modifiers_to_key_modifiers(NSEventModifierFlags modifier_f
|
||||||
MouseEvent ns_event_to_mouse_event(NSEvent* event, NSView* view, GUI::MouseButton button)
|
MouseEvent ns_event_to_mouse_event(NSEvent* event, NSView* view, GUI::MouseButton button)
|
||||||
{
|
{
|
||||||
auto position = [view convertPoint:event.locationInWindow fromView:nil];
|
auto position = [view convertPoint:event.locationInWindow fromView:nil];
|
||||||
|
auto screen_position = [NSEvent mouseLocation];
|
||||||
auto modifiers = ns_modifiers_to_key_modifiers(event.modifierFlags, button);
|
auto modifiers = ns_modifiers_to_key_modifiers(event.modifierFlags, button);
|
||||||
|
|
||||||
return { ns_point_to_gfx_point(position), button, modifiers };
|
return { ns_point_to_gfx_point(position), ns_point_to_gfx_point(screen_position), button, modifiers };
|
||||||
}
|
}
|
||||||
|
|
||||||
NSEvent* create_context_menu_mouse_event(NSView* view, Gfx::IntPoint position)
|
NSEvent* create_context_menu_mouse_event(NSView* view, Gfx::IntPoint position)
|
||||||
|
|
|
@ -956,58 +956,58 @@ static void copy_text_to_clipboard(StringView text)
|
||||||
|
|
||||||
- (void)mouseMoved:(NSEvent*)event
|
- (void)mouseMoved:(NSEvent*)event
|
||||||
{
|
{
|
||||||
auto [position, button, modifiers] = Ladybird::ns_event_to_mouse_event(event, self, GUI::MouseButton::None);
|
auto [position, screen_position, button, modifiers] = Ladybird::ns_event_to_mouse_event(event, self, GUI::MouseButton::None);
|
||||||
m_web_view_bridge->mouse_move_event(position, button, modifiers);
|
m_web_view_bridge->mouse_move_event(position, screen_position, button, modifiers);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)mouseDown:(NSEvent*)event
|
- (void)mouseDown:(NSEvent*)event
|
||||||
{
|
{
|
||||||
[[self window] makeFirstResponder:self];
|
[[self window] makeFirstResponder:self];
|
||||||
|
|
||||||
auto [position, button, modifiers] = Ladybird::ns_event_to_mouse_event(event, self, GUI::MouseButton::Primary);
|
auto [position, screen_position, button, modifiers] = Ladybird::ns_event_to_mouse_event(event, self, GUI::MouseButton::Primary);
|
||||||
|
|
||||||
if (event.clickCount % 2 == 0) {
|
if (event.clickCount % 2 == 0) {
|
||||||
m_web_view_bridge->mouse_double_click_event(position, button, modifiers);
|
m_web_view_bridge->mouse_double_click_event(position, screen_position, button, modifiers);
|
||||||
} else {
|
} else {
|
||||||
m_web_view_bridge->mouse_down_event(position, button, modifiers);
|
m_web_view_bridge->mouse_down_event(position, screen_position, button, modifiers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)mouseUp:(NSEvent*)event
|
- (void)mouseUp:(NSEvent*)event
|
||||||
{
|
{
|
||||||
auto [position, button, modifiers] = Ladybird::ns_event_to_mouse_event(event, self, GUI::MouseButton::Primary);
|
auto [position, screen_position, button, modifiers] = Ladybird::ns_event_to_mouse_event(event, self, GUI::MouseButton::Primary);
|
||||||
m_web_view_bridge->mouse_up_event(position, button, modifiers);
|
m_web_view_bridge->mouse_up_event(position, screen_position, button, modifiers);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)mouseDragged:(NSEvent*)event
|
- (void)mouseDragged:(NSEvent*)event
|
||||||
{
|
{
|
||||||
auto [position, button, modifiers] = Ladybird::ns_event_to_mouse_event(event, self, GUI::MouseButton::Primary);
|
auto [position, screen_position, button, modifiers] = Ladybird::ns_event_to_mouse_event(event, self, GUI::MouseButton::Primary);
|
||||||
m_web_view_bridge->mouse_move_event(position, button, modifiers);
|
m_web_view_bridge->mouse_move_event(position, screen_position, button, modifiers);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)rightMouseDown:(NSEvent*)event
|
- (void)rightMouseDown:(NSEvent*)event
|
||||||
{
|
{
|
||||||
[[self window] makeFirstResponder:self];
|
[[self window] makeFirstResponder:self];
|
||||||
|
|
||||||
auto [position, button, modifiers] = Ladybird::ns_event_to_mouse_event(event, self, GUI::MouseButton::Secondary);
|
auto [position, screen_position, button, modifiers] = Ladybird::ns_event_to_mouse_event(event, self, GUI::MouseButton::Secondary);
|
||||||
|
|
||||||
if (event.clickCount % 2 == 0) {
|
if (event.clickCount % 2 == 0) {
|
||||||
m_web_view_bridge->mouse_double_click_event(position, button, modifiers);
|
m_web_view_bridge->mouse_double_click_event(position, screen_position, button, modifiers);
|
||||||
} else {
|
} else {
|
||||||
m_web_view_bridge->mouse_down_event(position, button, modifiers);
|
m_web_view_bridge->mouse_down_event(position, screen_position, button, modifiers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)rightMouseUp:(NSEvent*)event
|
- (void)rightMouseUp:(NSEvent*)event
|
||||||
{
|
{
|
||||||
auto [position, button, modifiers] = Ladybird::ns_event_to_mouse_event(event, self, GUI::MouseButton::Secondary);
|
auto [position, screen_position, button, modifiers] = Ladybird::ns_event_to_mouse_event(event, self, GUI::MouseButton::Secondary);
|
||||||
m_web_view_bridge->mouse_up_event(position, button, modifiers);
|
m_web_view_bridge->mouse_up_event(position, screen_position, button, modifiers);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)rightMouseDragged:(NSEvent*)event
|
- (void)rightMouseDragged:(NSEvent*)event
|
||||||
{
|
{
|
||||||
auto [position, button, modifiers] = Ladybird::ns_event_to_mouse_event(event, self, GUI::MouseButton::Secondary);
|
auto [position, screen_position, button, modifiers] = Ladybird::ns_event_to_mouse_event(event, self, GUI::MouseButton::Secondary);
|
||||||
m_web_view_bridge->mouse_move_event(position, button, modifiers);
|
m_web_view_bridge->mouse_move_event(position, screen_position, button, modifiers);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)keyDown:(NSEvent*)event
|
- (void)keyDown:(NSEvent*)event
|
||||||
|
|
|
@ -100,24 +100,24 @@ void WebViewBridge::set_preferred_color_scheme(Web::CSS::PreferredColorScheme co
|
||||||
client().async_set_preferred_color_scheme(color_scheme);
|
client().async_set_preferred_color_scheme(color_scheme);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebViewBridge::mouse_down_event(Gfx::IntPoint position, GUI::MouseButton button, KeyModifier modifiers)
|
void WebViewBridge::mouse_down_event(Gfx::IntPoint position, Gfx::IntPoint screen_position, GUI::MouseButton button, KeyModifier modifiers)
|
||||||
{
|
{
|
||||||
client().async_mouse_down(to_content_position(position), to_underlying(button), to_underlying(button), modifiers);
|
client().async_mouse_down(to_content_position(position), screen_position, to_underlying(button), to_underlying(button), modifiers);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebViewBridge::mouse_up_event(Gfx::IntPoint position, GUI::MouseButton button, KeyModifier modifiers)
|
void WebViewBridge::mouse_up_event(Gfx::IntPoint position, Gfx::IntPoint screen_position, GUI::MouseButton button, KeyModifier modifiers)
|
||||||
{
|
{
|
||||||
client().async_mouse_up(to_content_position(position), to_underlying(button), to_underlying(button), modifiers);
|
client().async_mouse_up(to_content_position(position), screen_position, to_underlying(button), to_underlying(button), modifiers);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebViewBridge::mouse_move_event(Gfx::IntPoint position, GUI::MouseButton button, KeyModifier modifiers)
|
void WebViewBridge::mouse_move_event(Gfx::IntPoint position, Gfx::IntPoint screen_position, GUI::MouseButton button, KeyModifier modifiers)
|
||||||
{
|
{
|
||||||
client().async_mouse_move(to_content_position(position), 0, to_underlying(button), modifiers);
|
client().async_mouse_move(to_content_position(position), screen_position, 0, to_underlying(button), modifiers);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebViewBridge::mouse_double_click_event(Gfx::IntPoint position, GUI::MouseButton button, KeyModifier modifiers)
|
void WebViewBridge::mouse_double_click_event(Gfx::IntPoint position, Gfx::IntPoint screen_position, GUI::MouseButton button, KeyModifier modifiers)
|
||||||
{
|
{
|
||||||
client().async_doubleclick(to_content_position(position), button, to_underlying(button), modifiers);
|
client().async_doubleclick(to_content_position(position), screen_position, button, to_underlying(button), modifiers);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebViewBridge::key_down_event(KeyCode key_code, KeyModifier modifiers, u32 code_point)
|
void WebViewBridge::key_down_event(KeyCode key_code, KeyModifier modifiers, u32 code_point)
|
||||||
|
|
|
@ -39,10 +39,10 @@ public:
|
||||||
void update_palette();
|
void update_palette();
|
||||||
void set_preferred_color_scheme(Web::CSS::PreferredColorScheme);
|
void set_preferred_color_scheme(Web::CSS::PreferredColorScheme);
|
||||||
|
|
||||||
void mouse_down_event(Gfx::IntPoint, GUI::MouseButton, KeyModifier);
|
void mouse_down_event(Gfx::IntPoint, Gfx::IntPoint, GUI::MouseButton, KeyModifier);
|
||||||
void mouse_up_event(Gfx::IntPoint, GUI::MouseButton, KeyModifier);
|
void mouse_up_event(Gfx::IntPoint, Gfx::IntPoint, GUI::MouseButton, KeyModifier);
|
||||||
void mouse_move_event(Gfx::IntPoint, GUI::MouseButton, KeyModifier);
|
void mouse_move_event(Gfx::IntPoint, Gfx::IntPoint, GUI::MouseButton, KeyModifier);
|
||||||
void mouse_double_click_event(Gfx::IntPoint, GUI::MouseButton, KeyModifier);
|
void mouse_double_click_event(Gfx::IntPoint, Gfx::IntPoint, GUI::MouseButton, KeyModifier);
|
||||||
|
|
||||||
void key_down_event(KeyCode, KeyModifier, u32);
|
void key_down_event(KeyCode, KeyModifier, u32);
|
||||||
void key_up_event(KeyCode, KeyModifier, u32);
|
void key_up_event(KeyCode, KeyModifier, u32);
|
||||||
|
|
|
@ -325,13 +325,14 @@ void WebContentView::wheelEvent(QWheelEvent* event)
|
||||||
{
|
{
|
||||||
if (!event->modifiers().testFlag(Qt::ControlModifier)) {
|
if (!event->modifiers().testFlag(Qt::ControlModifier)) {
|
||||||
Gfx::IntPoint position(event->position().x() / m_inverse_pixel_scaling_ratio, event->position().y() / m_inverse_pixel_scaling_ratio);
|
Gfx::IntPoint position(event->position().x() / m_inverse_pixel_scaling_ratio, event->position().y() / m_inverse_pixel_scaling_ratio);
|
||||||
|
Gfx::IntPoint screen_position(event->globalPosition().x() / m_inverse_pixel_scaling_ratio, event->globalPosition().y() / m_inverse_pixel_scaling_ratio);
|
||||||
auto button = get_button_from_qt_event(*event);
|
auto button = get_button_from_qt_event(*event);
|
||||||
auto buttons = get_buttons_from_qt_event(*event);
|
auto buttons = get_buttons_from_qt_event(*event);
|
||||||
auto modifiers = get_modifiers_from_qt_mouse_event(*event);
|
auto modifiers = get_modifiers_from_qt_mouse_event(*event);
|
||||||
|
|
||||||
auto num_pixels = -event->pixelDelta();
|
auto num_pixels = -event->pixelDelta();
|
||||||
if (!num_pixels.isNull()) {
|
if (!num_pixels.isNull()) {
|
||||||
client().async_mouse_wheel(to_content_position(position), button, buttons, modifiers, num_pixels.x(), num_pixels.y());
|
client().async_mouse_wheel(to_content_position(position), screen_position, button, buttons, modifiers, num_pixels.x(), num_pixels.y());
|
||||||
} else {
|
} else {
|
||||||
auto num_degrees = -event->angleDelta();
|
auto num_degrees = -event->angleDelta();
|
||||||
float delta_x = -num_degrees.x() / 120;
|
float delta_x = -num_degrees.x() / 120;
|
||||||
|
@ -339,7 +340,7 @@ void WebContentView::wheelEvent(QWheelEvent* event)
|
||||||
auto step_x = delta_x * QApplication::wheelScrollLines() * devicePixelRatio();
|
auto step_x = delta_x * QApplication::wheelScrollLines() * devicePixelRatio();
|
||||||
auto step_y = delta_y * QApplication::wheelScrollLines() * devicePixelRatio();
|
auto step_y = delta_y * QApplication::wheelScrollLines() * devicePixelRatio();
|
||||||
int scroll_step_size = verticalScrollBar()->singleStep();
|
int scroll_step_size = verticalScrollBar()->singleStep();
|
||||||
client().async_mouse_wheel(to_content_position(position), button, buttons, modifiers, step_x * scroll_step_size, step_y * scroll_step_size);
|
client().async_mouse_wheel(to_content_position(position), screen_position, button, buttons, modifiers, step_x * scroll_step_size, step_y * scroll_step_size);
|
||||||
}
|
}
|
||||||
event->accept();
|
event->accept();
|
||||||
return;
|
return;
|
||||||
|
@ -350,14 +351,16 @@ void WebContentView::wheelEvent(QWheelEvent* event)
|
||||||
void WebContentView::mouseMoveEvent(QMouseEvent* event)
|
void WebContentView::mouseMoveEvent(QMouseEvent* event)
|
||||||
{
|
{
|
||||||
Gfx::IntPoint position(event->position().x() / m_inverse_pixel_scaling_ratio, event->position().y() / m_inverse_pixel_scaling_ratio);
|
Gfx::IntPoint position(event->position().x() / m_inverse_pixel_scaling_ratio, event->position().y() / m_inverse_pixel_scaling_ratio);
|
||||||
|
Gfx::IntPoint screen_position(event->globalPosition().x() / m_inverse_pixel_scaling_ratio, event->globalPosition().y() / m_inverse_pixel_scaling_ratio);
|
||||||
auto buttons = get_buttons_from_qt_event(*event);
|
auto buttons = get_buttons_from_qt_event(*event);
|
||||||
auto modifiers = get_modifiers_from_qt_mouse_event(*event);
|
auto modifiers = get_modifiers_from_qt_mouse_event(*event);
|
||||||
client().async_mouse_move(to_content_position(position), 0, buttons, modifiers);
|
client().async_mouse_move(to_content_position(position), screen_position, 0, buttons, modifiers);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebContentView::mousePressEvent(QMouseEvent* event)
|
void WebContentView::mousePressEvent(QMouseEvent* event)
|
||||||
{
|
{
|
||||||
Gfx::IntPoint position(event->position().x() / m_inverse_pixel_scaling_ratio, event->position().y() / m_inverse_pixel_scaling_ratio);
|
Gfx::IntPoint position(event->position().x() / m_inverse_pixel_scaling_ratio, event->position().y() / m_inverse_pixel_scaling_ratio);
|
||||||
|
Gfx::IntPoint screen_position(event->globalPosition().x() / m_inverse_pixel_scaling_ratio, event->globalPosition().y() / m_inverse_pixel_scaling_ratio);
|
||||||
auto button = get_button_from_qt_event(*event);
|
auto button = get_button_from_qt_event(*event);
|
||||||
if (button == 0) {
|
if (button == 0) {
|
||||||
// We could not convert Qt buttons to something that Lagom can
|
// We could not convert Qt buttons to something that Lagom can
|
||||||
|
@ -367,12 +370,13 @@ void WebContentView::mousePressEvent(QMouseEvent* event)
|
||||||
}
|
}
|
||||||
auto modifiers = get_modifiers_from_qt_mouse_event(*event);
|
auto modifiers = get_modifiers_from_qt_mouse_event(*event);
|
||||||
auto buttons = get_buttons_from_qt_event(*event);
|
auto buttons = get_buttons_from_qt_event(*event);
|
||||||
client().async_mouse_down(to_content_position(position), button, buttons, modifiers);
|
client().async_mouse_down(to_content_position(position), screen_position, button, buttons, modifiers);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebContentView::mouseReleaseEvent(QMouseEvent* event)
|
void WebContentView::mouseReleaseEvent(QMouseEvent* event)
|
||||||
{
|
{
|
||||||
Gfx::IntPoint position(event->position().x() / m_inverse_pixel_scaling_ratio, event->position().y() / m_inverse_pixel_scaling_ratio);
|
Gfx::IntPoint position(event->position().x() / m_inverse_pixel_scaling_ratio, event->position().y() / m_inverse_pixel_scaling_ratio);
|
||||||
|
Gfx::IntPoint screen_position(event->globalPosition().x() / m_inverse_pixel_scaling_ratio, event->globalPosition().y() / m_inverse_pixel_scaling_ratio);
|
||||||
auto button = get_button_from_qt_event(*event);
|
auto button = get_button_from_qt_event(*event);
|
||||||
|
|
||||||
if (event->button() & Qt::MouseButton::BackButton) {
|
if (event->button() & Qt::MouseButton::BackButton) {
|
||||||
|
@ -391,12 +395,13 @@ void WebContentView::mouseReleaseEvent(QMouseEvent* event)
|
||||||
}
|
}
|
||||||
auto modifiers = get_modifiers_from_qt_mouse_event(*event);
|
auto modifiers = get_modifiers_from_qt_mouse_event(*event);
|
||||||
auto buttons = get_buttons_from_qt_event(*event);
|
auto buttons = get_buttons_from_qt_event(*event);
|
||||||
client().async_mouse_up(to_content_position(position), button, buttons, modifiers);
|
client().async_mouse_up(to_content_position(position), screen_position, button, buttons, modifiers);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebContentView::mouseDoubleClickEvent(QMouseEvent* event)
|
void WebContentView::mouseDoubleClickEvent(QMouseEvent* event)
|
||||||
{
|
{
|
||||||
Gfx::IntPoint position(event->position().x() / m_inverse_pixel_scaling_ratio, event->position().y() / m_inverse_pixel_scaling_ratio);
|
Gfx::IntPoint position(event->position().x() / m_inverse_pixel_scaling_ratio, event->position().y() / m_inverse_pixel_scaling_ratio);
|
||||||
|
Gfx::IntPoint screen_position(event->globalPosition().x() / m_inverse_pixel_scaling_ratio, event->globalPosition().y() / m_inverse_pixel_scaling_ratio);
|
||||||
auto button = get_button_from_qt_event(*event);
|
auto button = get_button_from_qt_event(*event);
|
||||||
if (button == 0) {
|
if (button == 0) {
|
||||||
// We could not convert Qt buttons to something that Lagom can
|
// We could not convert Qt buttons to something that Lagom can
|
||||||
|
@ -406,7 +411,7 @@ void WebContentView::mouseDoubleClickEvent(QMouseEvent* event)
|
||||||
}
|
}
|
||||||
auto modifiers = get_modifiers_from_qt_mouse_event(*event);
|
auto modifiers = get_modifiers_from_qt_mouse_event(*event);
|
||||||
auto buttons = get_buttons_from_qt_event(*event);
|
auto buttons = get_buttons_from_qt_event(*event);
|
||||||
client().async_doubleclick(to_content_position(position), button, buttons, modifiers);
|
client().async_doubleclick(to_content_position(position), screen_position, button, buttons, modifiers);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebContentView::dragEnterEvent(QDragEnterEvent* event)
|
void WebContentView::dragEnterEvent(QDragEnterEvent* event)
|
||||||
|
|
|
@ -144,7 +144,7 @@ Painting::PaintableBox const* EventHandler::paint_root() const
|
||||||
return m_browsing_context->active_document()->paintable_box();
|
return m_browsing_context->active_document()->paintable_box();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EventHandler::handle_mousewheel(CSSPixelPoint position, unsigned button, unsigned buttons, unsigned int modifiers, int wheel_delta_x, int wheel_delta_y)
|
bool EventHandler::handle_mousewheel(CSSPixelPoint position, CSSPixelPoint screen_position, unsigned button, unsigned buttons, unsigned int modifiers, int wheel_delta_x, int wheel_delta_y)
|
||||||
{
|
{
|
||||||
if (m_browsing_context->active_document())
|
if (m_browsing_context->active_document())
|
||||||
m_browsing_context->active_document()->update_layout();
|
m_browsing_context->active_document()->update_layout();
|
||||||
|
@ -189,7 +189,7 @@ bool EventHandler::handle_mousewheel(CSSPixelPoint position, unsigned button, un
|
||||||
auto offset = compute_mouse_event_offset(position, *layout_node);
|
auto offset = compute_mouse_event_offset(position, *layout_node);
|
||||||
auto client_offset = compute_mouse_event_client_offset(position);
|
auto client_offset = compute_mouse_event_client_offset(position);
|
||||||
auto page_offset = compute_mouse_event_page_offset(client_offset);
|
auto page_offset = compute_mouse_event_page_offset(client_offset);
|
||||||
if (node->dispatch_event(UIEvents::WheelEvent::create_from_platform_event(node->realm(), UIEvents::EventNames::wheel, page_offset, client_offset, offset, wheel_delta_x, wheel_delta_y, button, buttons).release_value_but_fixme_should_propagate_errors())) {
|
if (node->dispatch_event(UIEvents::WheelEvent::create_from_platform_event(node->realm(), UIEvents::EventNames::wheel, screen_position, page_offset, client_offset, offset, wheel_delta_x, wheel_delta_y, button, buttons).release_value_but_fixme_should_propagate_errors())) {
|
||||||
if (auto* page = m_browsing_context->page()) {
|
if (auto* page = m_browsing_context->page()) {
|
||||||
if (m_browsing_context == &page->top_level_browsing_context())
|
if (m_browsing_context == &page->top_level_browsing_context())
|
||||||
page->client().page_did_request_scroll(wheel_delta_x, wheel_delta_y);
|
page->client().page_did_request_scroll(wheel_delta_x, wheel_delta_y);
|
||||||
|
@ -203,7 +203,7 @@ bool EventHandler::handle_mousewheel(CSSPixelPoint position, unsigned button, un
|
||||||
return handled_event;
|
return handled_event;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EventHandler::handle_mouseup(CSSPixelPoint position, unsigned button, unsigned buttons, unsigned modifiers)
|
bool EventHandler::handle_mouseup(CSSPixelPoint position, CSSPixelPoint screen_position, unsigned button, unsigned buttons, unsigned modifiers)
|
||||||
{
|
{
|
||||||
if (m_browsing_context->active_document())
|
if (m_browsing_context->active_document())
|
||||||
m_browsing_context->active_document()->update_layout();
|
m_browsing_context->active_document()->update_layout();
|
||||||
|
@ -234,7 +234,7 @@ bool EventHandler::handle_mouseup(CSSPixelPoint position, unsigned button, unsig
|
||||||
if (node) {
|
if (node) {
|
||||||
if (is<HTML::HTMLIFrameElement>(*node)) {
|
if (is<HTML::HTMLIFrameElement>(*node)) {
|
||||||
if (auto* nested_browsing_context = static_cast<HTML::HTMLIFrameElement&>(*node).nested_browsing_context())
|
if (auto* nested_browsing_context = static_cast<HTML::HTMLIFrameElement&>(*node).nested_browsing_context())
|
||||||
return nested_browsing_context->event_handler().handle_mouseup(position.translated(compute_mouse_event_offset({}, paintable->layout_node())), button, buttons, modifiers);
|
return nested_browsing_context->event_handler().handle_mouseup(position.translated(compute_mouse_event_offset({}, paintable->layout_node())), screen_position, button, buttons, modifiers);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,15 +250,15 @@ bool EventHandler::handle_mouseup(CSSPixelPoint position, unsigned button, unsig
|
||||||
auto offset = compute_mouse_event_offset(position, *layout_node);
|
auto offset = compute_mouse_event_offset(position, *layout_node);
|
||||||
auto client_offset = compute_mouse_event_client_offset(position);
|
auto client_offset = compute_mouse_event_client_offset(position);
|
||||||
auto page_offset = compute_mouse_event_page_offset(client_offset);
|
auto page_offset = compute_mouse_event_page_offset(client_offset);
|
||||||
node->dispatch_event(UIEvents::MouseEvent::create_from_platform_event(node->realm(), UIEvents::EventNames::mouseup, page_offset, client_offset, offset, {}, button, buttons).release_value_but_fixme_should_propagate_errors());
|
node->dispatch_event(UIEvents::MouseEvent::create_from_platform_event(node->realm(), UIEvents::EventNames::mouseup, screen_position, page_offset, client_offset, offset, {}, button, buttons).release_value_but_fixme_should_propagate_errors());
|
||||||
handled_event = true;
|
handled_event = true;
|
||||||
|
|
||||||
bool run_activation_behavior = false;
|
bool run_activation_behavior = false;
|
||||||
if (node.ptr() == m_mousedown_target) {
|
if (node.ptr() == m_mousedown_target) {
|
||||||
if (button == GUI::MouseButton::Primary)
|
if (button == GUI::MouseButton::Primary)
|
||||||
run_activation_behavior = node->dispatch_event(UIEvents::MouseEvent::create_from_platform_event(node->realm(), UIEvents::EventNames::click, page_offset, client_offset, offset, {}, 1, button).release_value_but_fixme_should_propagate_errors());
|
run_activation_behavior = node->dispatch_event(UIEvents::MouseEvent::create_from_platform_event(node->realm(), UIEvents::EventNames::click, screen_position, page_offset, client_offset, offset, {}, 1, button).release_value_but_fixme_should_propagate_errors());
|
||||||
else if (button == GUI::MouseButton::Secondary && !(modifiers & Mod_Shift)) // Allow the user to bypass custom context menus by holding shift, like Firefox.
|
else if (button == GUI::MouseButton::Secondary && !(modifiers & Mod_Shift)) // Allow the user to bypass custom context menus by holding shift, like Firefox.
|
||||||
run_activation_behavior = node->dispatch_event(UIEvents::MouseEvent::create_from_platform_event(node->realm(), UIEvents::EventNames::contextmenu, page_offset, client_offset, offset, {}, 1, button).release_value_but_fixme_should_propagate_errors());
|
run_activation_behavior = node->dispatch_event(UIEvents::MouseEvent::create_from_platform_event(node->realm(), UIEvents::EventNames::contextmenu, screen_position, page_offset, client_offset, offset, {}, 1, button).release_value_but_fixme_should_propagate_errors());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (run_activation_behavior) {
|
if (run_activation_behavior) {
|
||||||
|
@ -333,7 +333,7 @@ after_node_use:
|
||||||
return handled_event;
|
return handled_event;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EventHandler::handle_mousedown(CSSPixelPoint position, unsigned button, unsigned buttons, unsigned modifiers)
|
bool EventHandler::handle_mousedown(CSSPixelPoint position, CSSPixelPoint screen_position, unsigned button, unsigned buttons, unsigned modifiers)
|
||||||
{
|
{
|
||||||
if (m_browsing_context->active_document())
|
if (m_browsing_context->active_document())
|
||||||
m_browsing_context->active_document()->update_layout();
|
m_browsing_context->active_document()->update_layout();
|
||||||
|
@ -368,7 +368,7 @@ bool EventHandler::handle_mousedown(CSSPixelPoint position, unsigned button, uns
|
||||||
|
|
||||||
if (is<HTML::HTMLIFrameElement>(*node)) {
|
if (is<HTML::HTMLIFrameElement>(*node)) {
|
||||||
if (auto* nested_browsing_context = static_cast<HTML::HTMLIFrameElement&>(*node).nested_browsing_context())
|
if (auto* nested_browsing_context = static_cast<HTML::HTMLIFrameElement&>(*node).nested_browsing_context())
|
||||||
return nested_browsing_context->event_handler().handle_mousedown(position.translated(compute_mouse_event_offset({}, paintable->layout_node())), button, buttons, modifiers);
|
return nested_browsing_context->event_handler().handle_mousedown(position.translated(compute_mouse_event_offset({}, paintable->layout_node())), screen_position, button, buttons, modifiers);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -386,7 +386,7 @@ bool EventHandler::handle_mousedown(CSSPixelPoint position, unsigned button, uns
|
||||||
auto offset = compute_mouse_event_offset(position, *layout_node);
|
auto offset = compute_mouse_event_offset(position, *layout_node);
|
||||||
auto client_offset = compute_mouse_event_client_offset(position);
|
auto client_offset = compute_mouse_event_client_offset(position);
|
||||||
auto page_offset = compute_mouse_event_page_offset(client_offset);
|
auto page_offset = compute_mouse_event_page_offset(client_offset);
|
||||||
node->dispatch_event(UIEvents::MouseEvent::create_from_platform_event(node->realm(), UIEvents::EventNames::mousedown, page_offset, client_offset, offset, {}, button, buttons).release_value_but_fixme_should_propagate_errors());
|
node->dispatch_event(UIEvents::MouseEvent::create_from_platform_event(node->realm(), UIEvents::EventNames::mousedown, screen_position, page_offset, client_offset, offset, {}, button, buttons).release_value_but_fixme_should_propagate_errors());
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Dispatching an event may have disturbed the world.
|
// NOTE: Dispatching an event may have disturbed the world.
|
||||||
|
@ -424,7 +424,7 @@ bool EventHandler::handle_mousedown(CSSPixelPoint position, unsigned button, uns
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EventHandler::handle_mousemove(CSSPixelPoint position, unsigned buttons, unsigned modifiers)
|
bool EventHandler::handle_mousemove(CSSPixelPoint position, CSSPixelPoint screen_position, unsigned buttons, unsigned modifiers)
|
||||||
{
|
{
|
||||||
if (m_browsing_context->active_document())
|
if (m_browsing_context->active_document())
|
||||||
m_browsing_context->active_document()->update_layout();
|
m_browsing_context->active_document()->update_layout();
|
||||||
|
@ -462,7 +462,7 @@ bool EventHandler::handle_mousemove(CSSPixelPoint position, unsigned buttons, un
|
||||||
|
|
||||||
if (node && is<HTML::HTMLIFrameElement>(*node)) {
|
if (node && is<HTML::HTMLIFrameElement>(*node)) {
|
||||||
if (auto* nested_browsing_context = static_cast<HTML::HTMLIFrameElement&>(*node).nested_browsing_context())
|
if (auto* nested_browsing_context = static_cast<HTML::HTMLIFrameElement&>(*node).nested_browsing_context())
|
||||||
return nested_browsing_context->event_handler().handle_mousemove(position.translated(compute_mouse_event_offset({}, paintable->layout_node())), buttons, modifiers);
|
return nested_browsing_context->event_handler().handle_mousemove(position.translated(compute_mouse_event_offset({}, paintable->layout_node())), screen_position, buttons, modifiers);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -499,9 +499,9 @@ bool EventHandler::handle_mousemove(CSSPixelPoint position, unsigned buttons, un
|
||||||
auto offset = compute_mouse_event_offset(position, *layout_node);
|
auto offset = compute_mouse_event_offset(position, *layout_node);
|
||||||
auto client_offset = compute_mouse_event_client_offset(position);
|
auto client_offset = compute_mouse_event_client_offset(position);
|
||||||
auto page_offset = compute_mouse_event_page_offset(client_offset);
|
auto page_offset = compute_mouse_event_page_offset(client_offset);
|
||||||
auto movement = compute_mouse_event_movement(client_offset);
|
auto movement = compute_mouse_event_movement(screen_position);
|
||||||
node->dispatch_event(UIEvents::MouseEvent::create_from_platform_event(node->realm(), UIEvents::EventNames::mousemove, page_offset, client_offset, offset, movement, 1, buttons).release_value_but_fixme_should_propagate_errors());
|
node->dispatch_event(UIEvents::MouseEvent::create_from_platform_event(node->realm(), UIEvents::EventNames::mousemove, screen_position, page_offset, client_offset, offset, movement, 1, buttons).release_value_but_fixme_should_propagate_errors());
|
||||||
m_mousemove_previous_client_offset = client_offset;
|
m_mousemove_previous_screen_position = screen_position;
|
||||||
// NOTE: Dispatching an event may have disturbed the world.
|
// NOTE: Dispatching an event may have disturbed the world.
|
||||||
if (!paint_root() || paint_root() != node->document().paintable_box())
|
if (!paint_root() || paint_root() != node->document().paintable_box())
|
||||||
return true;
|
return true;
|
||||||
|
@ -543,7 +543,7 @@ bool EventHandler::handle_mousemove(CSSPixelPoint position, unsigned buttons, un
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EventHandler::handle_doubleclick(CSSPixelPoint position, unsigned button, unsigned buttons, unsigned modifiers)
|
bool EventHandler::handle_doubleclick(CSSPixelPoint position, CSSPixelPoint screen_position, unsigned button, unsigned buttons, unsigned modifiers)
|
||||||
{
|
{
|
||||||
if (m_browsing_context->active_document())
|
if (m_browsing_context->active_document())
|
||||||
m_browsing_context->active_document()->update_layout();
|
m_browsing_context->active_document()->update_layout();
|
||||||
|
@ -573,7 +573,7 @@ bool EventHandler::handle_doubleclick(CSSPixelPoint position, unsigned button, u
|
||||||
|
|
||||||
if (is<HTML::HTMLIFrameElement>(*node)) {
|
if (is<HTML::HTMLIFrameElement>(*node)) {
|
||||||
if (auto* nested_browsing_context = static_cast<HTML::HTMLIFrameElement&>(*node).nested_browsing_context())
|
if (auto* nested_browsing_context = static_cast<HTML::HTMLIFrameElement&>(*node).nested_browsing_context())
|
||||||
return nested_browsing_context->event_handler().handle_doubleclick(position.translated(compute_mouse_event_offset({}, paintable->layout_node())), button, buttons, modifiers);
|
return nested_browsing_context->event_handler().handle_doubleclick(position.translated(compute_mouse_event_offset({}, paintable->layout_node())), screen_position, button, buttons, modifiers);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -586,7 +586,7 @@ bool EventHandler::handle_doubleclick(CSSPixelPoint position, unsigned button, u
|
||||||
auto offset = compute_mouse_event_offset(position, *layout_node);
|
auto offset = compute_mouse_event_offset(position, *layout_node);
|
||||||
auto client_offset = compute_mouse_event_client_offset(position);
|
auto client_offset = compute_mouse_event_client_offset(position);
|
||||||
auto page_offset = compute_mouse_event_page_offset(client_offset);
|
auto page_offset = compute_mouse_event_page_offset(client_offset);
|
||||||
node->dispatch_event(UIEvents::MouseEvent::create_from_platform_event(node->realm(), UIEvents::EventNames::dblclick, page_offset, client_offset, offset, {}, button, buttons).release_value_but_fixme_should_propagate_errors());
|
node->dispatch_event(UIEvents::MouseEvent::create_from_platform_event(node->realm(), UIEvents::EventNames::dblclick, screen_position, page_offset, client_offset, offset, {}, button, buttons).release_value_but_fixme_should_propagate_errors());
|
||||||
|
|
||||||
// NOTE: Dispatching an event may have disturbed the world.
|
// NOTE: Dispatching an event may have disturbed the world.
|
||||||
if (!paint_root() || paint_root() != node->document().paintable_box())
|
if (!paint_root() || paint_root() != node->document().paintable_box())
|
||||||
|
@ -838,21 +838,20 @@ CSSPixelPoint EventHandler::compute_mouse_event_page_offset(CSSPixelPoint event_
|
||||||
return event_client_offset.translated(scroll_offset);
|
return event_client_offset.translated(scroll_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
CSSPixelPoint EventHandler::compute_mouse_event_movement(CSSPixelPoint event_client_offset) const
|
CSSPixelPoint EventHandler::compute_mouse_event_movement(CSSPixelPoint screen_position) const
|
||||||
{
|
{
|
||||||
// https://w3c.github.io/pointerlock/#dom-mouseevent-movementx
|
// https://w3c.github.io/pointerlock/#dom-mouseevent-movementx
|
||||||
// The attributes movementX movementY must provide the change in position of the pointer,
|
// The attributes movementX movementY must provide the change in position of the pointer,
|
||||||
// as if the values of screenX, screenY, were stored between two subsequent mousemove events eNow and ePrevious and the difference taken movementX = eNow.screenX-ePrevious.screenX.
|
// as if the values of screenX, screenY, were stored between two subsequent mousemove events eNow and ePrevious and the difference taken movementX = eNow.screenX-ePrevious.screenX.
|
||||||
// FIXME: Using client_offset as screenX and screenY is currently equal to clientX and clientY
|
|
||||||
|
|
||||||
if (!m_mousemove_previous_client_offset.has_value())
|
if (!m_mousemove_previous_screen_position.has_value())
|
||||||
// When unlocked, the system cursor can exit and re-enter the user agent window.
|
// When unlocked, the system cursor can exit and re-enter the user agent window.
|
||||||
// If it does so and the user agent was not the target of operating system mouse move events
|
// If it does so and the user agent was not the target of operating system mouse move events
|
||||||
// then the most recent pointer position will be unknown to the user agent and movementX/movementY can not be computed and must be set to zero.
|
// then the most recent pointer position will be unknown to the user agent and movementX/movementY can not be computed and must be set to zero.
|
||||||
// FIXME: For this to actually work, m_mousemove_previous_client_offset needs to be cleared when the mouse leaves the window
|
// FIXME: For this to actually work, m_mousemove_previous_client_offset needs to be cleared when the mouse leaves the window
|
||||||
return { 0, 0 };
|
return { 0, 0 };
|
||||||
|
|
||||||
return { event_client_offset.x() - m_mousemove_previous_client_offset.value().x(), event_client_offset.y() - m_mousemove_previous_client_offset.value().y() };
|
return { screen_position.x() - m_mousemove_previous_screen_position.value().x(), screen_position.y() - m_mousemove_previous_screen_position.value().y() };
|
||||||
}
|
}
|
||||||
|
|
||||||
Optional<EventHandler::Target> EventHandler::target_for_mouse_position(CSSPixelPoint position)
|
Optional<EventHandler::Target> EventHandler::target_for_mouse_position(CSSPixelPoint position)
|
||||||
|
|
|
@ -24,11 +24,11 @@ public:
|
||||||
explicit EventHandler(Badge<HTML::BrowsingContext>, HTML::BrowsingContext&);
|
explicit EventHandler(Badge<HTML::BrowsingContext>, HTML::BrowsingContext&);
|
||||||
~EventHandler();
|
~EventHandler();
|
||||||
|
|
||||||
bool handle_mouseup(CSSPixelPoint, unsigned button, unsigned buttons, unsigned modifiers);
|
bool handle_mouseup(CSSPixelPoint, CSSPixelPoint screen_position, unsigned button, unsigned buttons, unsigned modifiers);
|
||||||
bool handle_mousedown(CSSPixelPoint, unsigned button, unsigned buttons, unsigned modifiers);
|
bool handle_mousedown(CSSPixelPoint, CSSPixelPoint screen_position, unsigned button, unsigned buttons, unsigned modifiers);
|
||||||
bool handle_mousemove(CSSPixelPoint, unsigned buttons, unsigned modifiers);
|
bool handle_mousemove(CSSPixelPoint, CSSPixelPoint screen_position, unsigned buttons, unsigned modifiers);
|
||||||
bool handle_mousewheel(CSSPixelPoint, unsigned button, unsigned buttons, unsigned modifiers, int wheel_delta_x, int wheel_delta_y);
|
bool handle_mousewheel(CSSPixelPoint, CSSPixelPoint screen_position, unsigned button, unsigned buttons, unsigned modifiers, int wheel_delta_x, int wheel_delta_y);
|
||||||
bool handle_doubleclick(CSSPixelPoint, unsigned button, unsigned buttons, unsigned modifiers);
|
bool handle_doubleclick(CSSPixelPoint, CSSPixelPoint screen_position, unsigned button, unsigned buttons, unsigned modifiers);
|
||||||
|
|
||||||
bool handle_keydown(KeyCode, unsigned modifiers, u32 code_point);
|
bool handle_keydown(KeyCode, unsigned modifiers, u32 code_point);
|
||||||
bool handle_keyup(KeyCode, unsigned modifiers, u32 code_point);
|
bool handle_keyup(KeyCode, unsigned modifiers, u32 code_point);
|
||||||
|
@ -65,7 +65,7 @@ private:
|
||||||
|
|
||||||
WeakPtr<DOM::EventTarget> m_mousedown_target;
|
WeakPtr<DOM::EventTarget> m_mousedown_target;
|
||||||
|
|
||||||
Optional<CSSPixelPoint> m_mousemove_previous_client_offset;
|
Optional<CSSPixelPoint> m_mousemove_previous_screen_position;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,29 +132,29 @@ DevicePixelRect Page::rounded_device_rect(CSSPixelRect rect) const
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Page::handle_mousewheel(DevicePixelPoint position, unsigned button, unsigned buttons, unsigned modifiers, int wheel_delta_x, int wheel_delta_y)
|
bool Page::handle_mousewheel(DevicePixelPoint position, DevicePixelPoint screen_position, unsigned button, unsigned buttons, unsigned modifiers, int wheel_delta_x, int wheel_delta_y)
|
||||||
{
|
{
|
||||||
return top_level_browsing_context().event_handler().handle_mousewheel(device_to_css_point(position), button, buttons, modifiers, wheel_delta_x, wheel_delta_y);
|
return top_level_browsing_context().event_handler().handle_mousewheel(device_to_css_point(position), device_to_css_point(screen_position), button, buttons, modifiers, wheel_delta_x, wheel_delta_y);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Page::handle_mouseup(DevicePixelPoint position, unsigned button, unsigned buttons, unsigned modifiers)
|
bool Page::handle_mouseup(DevicePixelPoint position, DevicePixelPoint screen_position, unsigned button, unsigned buttons, unsigned modifiers)
|
||||||
{
|
{
|
||||||
return top_level_browsing_context().event_handler().handle_mouseup(device_to_css_point(position), button, buttons, modifiers);
|
return top_level_browsing_context().event_handler().handle_mouseup(device_to_css_point(position), device_to_css_point(screen_position), button, buttons, modifiers);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Page::handle_mousedown(DevicePixelPoint position, unsigned button, unsigned buttons, unsigned modifiers)
|
bool Page::handle_mousedown(DevicePixelPoint position, DevicePixelPoint screen_position, unsigned button, unsigned buttons, unsigned modifiers)
|
||||||
{
|
{
|
||||||
return top_level_browsing_context().event_handler().handle_mousedown(device_to_css_point(position), button, buttons, modifiers);
|
return top_level_browsing_context().event_handler().handle_mousedown(device_to_css_point(position), device_to_css_point(screen_position), button, buttons, modifiers);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Page::handle_mousemove(DevicePixelPoint position, unsigned buttons, unsigned modifiers)
|
bool Page::handle_mousemove(DevicePixelPoint position, DevicePixelPoint screen_position, unsigned buttons, unsigned modifiers)
|
||||||
{
|
{
|
||||||
return top_level_browsing_context().event_handler().handle_mousemove(device_to_css_point(position), buttons, modifiers);
|
return top_level_browsing_context().event_handler().handle_mousemove(device_to_css_point(position), device_to_css_point(screen_position), buttons, modifiers);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Page::handle_doubleclick(DevicePixelPoint position, unsigned button, unsigned buttons, unsigned modifiers)
|
bool Page::handle_doubleclick(DevicePixelPoint position, DevicePixelPoint screen_position, unsigned button, unsigned buttons, unsigned modifiers)
|
||||||
{
|
{
|
||||||
return top_level_browsing_context().event_handler().handle_doubleclick(device_to_css_point(position), button, buttons, modifiers);
|
return top_level_browsing_context().event_handler().handle_doubleclick(device_to_css_point(position), device_to_css_point(screen_position), button, buttons, modifiers);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Page::handle_keydown(KeyCode key, unsigned modifiers, u32 code_point)
|
bool Page::handle_keydown(KeyCode key, unsigned modifiers, u32 code_point)
|
||||||
|
|
|
@ -69,11 +69,11 @@ public:
|
||||||
DevicePixelRect enclosing_device_rect(CSSPixelRect) const;
|
DevicePixelRect enclosing_device_rect(CSSPixelRect) const;
|
||||||
DevicePixelRect rounded_device_rect(CSSPixelRect) const;
|
DevicePixelRect rounded_device_rect(CSSPixelRect) const;
|
||||||
|
|
||||||
bool handle_mouseup(DevicePixelPoint, unsigned button, unsigned buttons, unsigned modifiers);
|
bool handle_mouseup(DevicePixelPoint, DevicePixelPoint screen_position, unsigned button, unsigned buttons, unsigned modifiers);
|
||||||
bool handle_mousedown(DevicePixelPoint, unsigned button, unsigned buttons, unsigned modifiers);
|
bool handle_mousedown(DevicePixelPoint, DevicePixelPoint screen_position, unsigned button, unsigned buttons, unsigned modifiers);
|
||||||
bool handle_mousemove(DevicePixelPoint, unsigned buttons, unsigned modifiers);
|
bool handle_mousemove(DevicePixelPoint, DevicePixelPoint screen_position, unsigned buttons, unsigned modifiers);
|
||||||
bool handle_mousewheel(DevicePixelPoint, unsigned button, unsigned buttons, unsigned modifiers, int wheel_delta_x, int wheel_delta_y);
|
bool handle_mousewheel(DevicePixelPoint, DevicePixelPoint screen_position, unsigned button, unsigned buttons, unsigned modifiers, int wheel_delta_x, int wheel_delta_y);
|
||||||
bool handle_doubleclick(DevicePixelPoint, unsigned button, unsigned buttons, unsigned modifiers);
|
bool handle_doubleclick(DevicePixelPoint, DevicePixelPoint screen_position, unsigned button, unsigned buttons, unsigned modifiers);
|
||||||
|
|
||||||
bool handle_keydown(KeyCode, unsigned modifiers, u32 code_point);
|
bool handle_keydown(KeyCode, unsigned modifiers, u32 code_point);
|
||||||
bool handle_keyup(KeyCode, unsigned modifiers, u32 code_point);
|
bool handle_keyup(KeyCode, unsigned modifiers, u32 code_point);
|
||||||
|
|
|
@ -63,9 +63,11 @@ JS::NonnullGCPtr<MouseEvent> MouseEvent::create(JS::Realm& realm, FlyString cons
|
||||||
return realm.heap().allocate<MouseEvent>(realm, realm, event_name, event_init, page_x, page_y, offset_x, offset_y);
|
return realm.heap().allocate<MouseEvent>(realm, realm, event_name, event_init, page_x, page_y, offset_x, offset_y);
|
||||||
}
|
}
|
||||||
|
|
||||||
WebIDL::ExceptionOr<JS::NonnullGCPtr<MouseEvent>> MouseEvent::create_from_platform_event(JS::Realm& realm, FlyString const& event_name, CSSPixelPoint page, CSSPixelPoint client, CSSPixelPoint offset, Optional<CSSPixelPoint> movement, unsigned button, unsigned buttons)
|
WebIDL::ExceptionOr<JS::NonnullGCPtr<MouseEvent>> MouseEvent::create_from_platform_event(JS::Realm& realm, FlyString const& event_name, CSSPixelPoint screen, CSSPixelPoint page, CSSPixelPoint client, CSSPixelPoint offset, Optional<CSSPixelPoint> movement, unsigned button, unsigned buttons)
|
||||||
{
|
{
|
||||||
MouseEventInit event_init {};
|
MouseEventInit event_init {};
|
||||||
|
event_init.screen_x = screen.x().to_double();
|
||||||
|
event_init.screen_y = screen.y().to_double();
|
||||||
event_init.client_x = client.x().to_double();
|
event_init.client_x = client.x().to_double();
|
||||||
event_init.client_y = client.y().to_double();
|
event_init.client_y = client.y().to_double();
|
||||||
if (movement.has_value()) {
|
if (movement.has_value()) {
|
||||||
|
|
|
@ -29,7 +29,7 @@ class MouseEvent : public UIEvent {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
[[nodiscard]] static JS::NonnullGCPtr<MouseEvent> create(JS::Realm&, FlyString const& event_name, MouseEventInit const& = {}, double page_x = 0, double page_y = 0, double offset_x = 0, double offset_y = 0);
|
[[nodiscard]] static JS::NonnullGCPtr<MouseEvent> create(JS::Realm&, FlyString const& event_name, MouseEventInit const& = {}, double page_x = 0, double page_y = 0, double offset_x = 0, double offset_y = 0);
|
||||||
static WebIDL::ExceptionOr<JS::NonnullGCPtr<MouseEvent>> create_from_platform_event(JS::Realm&, FlyString const& event_name, CSSPixelPoint page, CSSPixelPoint client, CSSPixelPoint offset, Optional<CSSPixelPoint> movement, unsigned button, unsigned buttons);
|
static WebIDL::ExceptionOr<JS::NonnullGCPtr<MouseEvent>> create_from_platform_event(JS::Realm&, FlyString const& event_name, CSSPixelPoint screen, CSSPixelPoint page, CSSPixelPoint client, CSSPixelPoint offset, Optional<CSSPixelPoint> movement, unsigned button, unsigned buttons);
|
||||||
|
|
||||||
virtual ~MouseEvent() override;
|
virtual ~MouseEvent() override;
|
||||||
|
|
||||||
|
|
|
@ -34,9 +34,11 @@ JS::NonnullGCPtr<WheelEvent> WheelEvent::create(JS::Realm& realm, FlyString cons
|
||||||
return realm.heap().allocate<WheelEvent>(realm, realm, event_name, event_init, page_x, page_y, offset_x, offset_y);
|
return realm.heap().allocate<WheelEvent>(realm, realm, event_name, event_init, page_x, page_y, offset_x, offset_y);
|
||||||
}
|
}
|
||||||
|
|
||||||
WebIDL::ExceptionOr<JS::NonnullGCPtr<WheelEvent>> WheelEvent::create_from_platform_event(JS::Realm& realm, FlyString const& event_name, CSSPixelPoint page, CSSPixelPoint client, CSSPixelPoint offset, double delta_x, double delta_y, unsigned button, unsigned buttons)
|
WebIDL::ExceptionOr<JS::NonnullGCPtr<WheelEvent>> WheelEvent::create_from_platform_event(JS::Realm& realm, FlyString const& event_name, CSSPixelPoint screen, CSSPixelPoint page, CSSPixelPoint client, CSSPixelPoint offset, double delta_x, double delta_y, unsigned button, unsigned buttons)
|
||||||
{
|
{
|
||||||
WheelEventInit event_init {};
|
WheelEventInit event_init {};
|
||||||
|
event_init.screen_x = screen.x().to_double();
|
||||||
|
event_init.screen_y = screen.y().to_double();
|
||||||
event_init.client_x = client.x().to_double();
|
event_init.client_x = client.x().to_double();
|
||||||
event_init.client_y = client.y().to_double();
|
event_init.client_y = client.y().to_double();
|
||||||
event_init.button = button;
|
event_init.button = button;
|
||||||
|
|
|
@ -30,7 +30,7 @@ class WheelEvent final : public MouseEvent {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
[[nodiscard]] static JS::NonnullGCPtr<WheelEvent> create(JS::Realm&, FlyString const& event_name, WheelEventInit const& event_init = {}, double page_x = 0, double page_y = 0, double offset_x = 0, double offset_y = 0);
|
[[nodiscard]] static JS::NonnullGCPtr<WheelEvent> create(JS::Realm&, FlyString const& event_name, WheelEventInit const& event_init = {}, double page_x = 0, double page_y = 0, double offset_x = 0, double offset_y = 0);
|
||||||
static WebIDL::ExceptionOr<JS::NonnullGCPtr<WheelEvent>> create_from_platform_event(JS::Realm&, FlyString const& event_name, CSSPixelPoint page, CSSPixelPoint client, CSSPixelPoint offset, double delta_x, double delta_y, unsigned button, unsigned buttons);
|
static WebIDL::ExceptionOr<JS::NonnullGCPtr<WheelEvent>> create_from_platform_event(JS::Realm&, FlyString const& event_name, CSSPixelPoint screen, CSSPixelPoint page, CSSPixelPoint client, CSSPixelPoint offset, double delta_x, double delta_y, unsigned button, unsigned buttons);
|
||||||
|
|
||||||
virtual ~WheelEvent() override;
|
virtual ~WheelEvent() override;
|
||||||
|
|
||||||
|
|
|
@ -328,24 +328,25 @@ void OutOfProcessWebView::process_next_input_event()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[this](GUI::MouseEvent const& event) {
|
[this](GUI::MouseEvent const& event) {
|
||||||
|
auto screen_position = event.position() + (window()->position() + relative_position());
|
||||||
switch (event.type()) {
|
switch (event.type()) {
|
||||||
case GUI::Event::Type::MouseDown:
|
case GUI::Event::Type::MouseDown:
|
||||||
client().async_mouse_down(to_content_position(event.position()), event.button(), event.buttons(), event.modifiers());
|
client().async_mouse_down(to_content_position(event.position()), screen_position, event.button(), event.buttons(), event.modifiers());
|
||||||
break;
|
break;
|
||||||
case GUI::Event::Type::MouseUp:
|
case GUI::Event::Type::MouseUp:
|
||||||
client().async_mouse_up(to_content_position(event.position()), event.button(), event.buttons(), event.modifiers());
|
client().async_mouse_up(to_content_position(event.position()), screen_position, event.button(), event.buttons(), event.modifiers());
|
||||||
break;
|
break;
|
||||||
case GUI::Event::Type::MouseMove:
|
case GUI::Event::Type::MouseMove:
|
||||||
client().async_mouse_move(to_content_position(event.position()), event.button(), event.buttons(), event.modifiers());
|
client().async_mouse_move(to_content_position(event.position()), screen_position, event.button(), event.buttons(), event.modifiers());
|
||||||
break;
|
break;
|
||||||
case GUI::Event::Type::MouseWheel: {
|
case GUI::Event::Type::MouseWheel: {
|
||||||
// FIXME: This wheel delta step size multiplier is used to remain the old scroll behaviour, in future use system step size.
|
// FIXME: This wheel delta step size multiplier is used to remain the old scroll behaviour, in future use system step size.
|
||||||
constexpr int scroll_step_size = 24;
|
constexpr int scroll_step_size = 24;
|
||||||
client().async_mouse_wheel(to_content_position(event.position()), event.button(), event.buttons(), event.modifiers(), event.wheel_delta_x() * scroll_step_size, event.wheel_delta_y() * scroll_step_size);
|
client().async_mouse_wheel(to_content_position(event.position()), screen_position, event.button(), event.buttons(), event.modifiers(), event.wheel_delta_x() * scroll_step_size, event.wheel_delta_y() * scroll_step_size);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GUI::Event::Type::MouseDoubleClick:
|
case GUI::Event::Type::MouseDoubleClick:
|
||||||
client().async_doubleclick(to_content_position(event.position()), event.button(), event.buttons(), event.modifiers());
|
client().async_doubleclick(to_content_position(event.position()), screen_position, event.button(), event.buttons(), event.modifiers());
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
dbgln("Unrecognized mouse event type in OOPWV input event queue: {}", event.type());
|
dbgln("Unrecognized mouse event type in OOPWV input event queue: {}", event.type());
|
||||||
|
|
|
@ -182,11 +182,13 @@ void ConnectionFromClient::process_next_input_event()
|
||||||
case QueuedMouseEvent::Type::MouseDown:
|
case QueuedMouseEvent::Type::MouseDown:
|
||||||
report_finished_handling_input_event(page().handle_mousedown(
|
report_finished_handling_input_event(page().handle_mousedown(
|
||||||
event.position.to_type<Web::DevicePixels>(),
|
event.position.to_type<Web::DevicePixels>(),
|
||||||
|
event.screen_position.to_type<Web::DevicePixels>(),
|
||||||
event.button, event.buttons, event.modifiers));
|
event.button, event.buttons, event.modifiers));
|
||||||
break;
|
break;
|
||||||
case QueuedMouseEvent::Type::MouseUp:
|
case QueuedMouseEvent::Type::MouseUp:
|
||||||
report_finished_handling_input_event(page().handle_mouseup(
|
report_finished_handling_input_event(page().handle_mouseup(
|
||||||
event.position.to_type<Web::DevicePixels>(),
|
event.position.to_type<Web::DevicePixels>(),
|
||||||
|
event.screen_position.to_type<Web::DevicePixels>(),
|
||||||
event.button, event.buttons, event.modifiers));
|
event.button, event.buttons, event.modifiers));
|
||||||
break;
|
break;
|
||||||
case QueuedMouseEvent::Type::MouseMove:
|
case QueuedMouseEvent::Type::MouseMove:
|
||||||
|
@ -197,11 +199,13 @@ void ConnectionFromClient::process_next_input_event()
|
||||||
}
|
}
|
||||||
report_finished_handling_input_event(page().handle_mousemove(
|
report_finished_handling_input_event(page().handle_mousemove(
|
||||||
event.position.to_type<Web::DevicePixels>(),
|
event.position.to_type<Web::DevicePixels>(),
|
||||||
|
event.screen_position.to_type<Web::DevicePixels>(),
|
||||||
event.buttons, event.modifiers));
|
event.buttons, event.modifiers));
|
||||||
break;
|
break;
|
||||||
case QueuedMouseEvent::Type::DoubleClick:
|
case QueuedMouseEvent::Type::DoubleClick:
|
||||||
report_finished_handling_input_event(page().handle_doubleclick(
|
report_finished_handling_input_event(page().handle_doubleclick(
|
||||||
event.position.to_type<Web::DevicePixels>(),
|
event.position.to_type<Web::DevicePixels>(),
|
||||||
|
event.screen_position.to_type<Web::DevicePixels>(),
|
||||||
event.button, event.buttons, event.modifiers));
|
event.button, event.buttons, event.modifiers));
|
||||||
break;
|
break;
|
||||||
case QueuedMouseEvent::Type::MouseWheel:
|
case QueuedMouseEvent::Type::MouseWheel:
|
||||||
|
@ -210,6 +214,7 @@ void ConnectionFromClient::process_next_input_event()
|
||||||
}
|
}
|
||||||
report_finished_handling_input_event(page().handle_mousewheel(
|
report_finished_handling_input_event(page().handle_mousewheel(
|
||||||
event.position.to_type<Web::DevicePixels>(),
|
event.position.to_type<Web::DevicePixels>(),
|
||||||
|
event.screen_position.to_type<Web::DevicePixels>(),
|
||||||
event.button, event.buttons, event.modifiers, event.wheel_delta_x, event.wheel_delta_y));
|
event.button, event.buttons, event.modifiers, event.wheel_delta_x, event.wheel_delta_y));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -229,23 +234,25 @@ void ConnectionFromClient::process_next_input_event()
|
||||||
m_input_event_queue_timer->start();
|
m_input_event_queue_timer->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectionFromClient::mouse_down(Gfx::IntPoint position, unsigned int button, unsigned int buttons, unsigned int modifiers)
|
void ConnectionFromClient::mouse_down(Gfx::IntPoint position, Gfx::IntPoint screen_position, unsigned int button, unsigned int buttons, unsigned int modifiers)
|
||||||
{
|
{
|
||||||
enqueue_input_event(
|
enqueue_input_event(
|
||||||
QueuedMouseEvent {
|
QueuedMouseEvent {
|
||||||
.type = QueuedMouseEvent::Type::MouseDown,
|
.type = QueuedMouseEvent::Type::MouseDown,
|
||||||
.position = position,
|
.position = position,
|
||||||
|
.screen_position = screen_position,
|
||||||
.button = button,
|
.button = button,
|
||||||
.buttons = buttons,
|
.buttons = buttons,
|
||||||
.modifiers = modifiers,
|
.modifiers = modifiers,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectionFromClient::mouse_move(Gfx::IntPoint position, [[maybe_unused]] unsigned int button, unsigned int buttons, unsigned int modifiers)
|
void ConnectionFromClient::mouse_move(Gfx::IntPoint position, Gfx::IntPoint screen_position, [[maybe_unused]] unsigned int button, unsigned int buttons, unsigned int modifiers)
|
||||||
{
|
{
|
||||||
auto event = QueuedMouseEvent {
|
auto event = QueuedMouseEvent {
|
||||||
.type = QueuedMouseEvent::Type::MouseMove,
|
.type = QueuedMouseEvent::Type::MouseMove,
|
||||||
.position = position,
|
.position = position,
|
||||||
|
.screen_position = screen_position,
|
||||||
.button = button,
|
.button = button,
|
||||||
.buttons = buttons,
|
.buttons = buttons,
|
||||||
.modifiers = modifiers,
|
.modifiers = modifiers,
|
||||||
|
@ -263,23 +270,25 @@ void ConnectionFromClient::mouse_move(Gfx::IntPoint position, [[maybe_unused]] u
|
||||||
enqueue_input_event(move(event));
|
enqueue_input_event(move(event));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectionFromClient::mouse_up(Gfx::IntPoint position, unsigned int button, unsigned int buttons, unsigned int modifiers)
|
void ConnectionFromClient::mouse_up(Gfx::IntPoint position, Gfx::IntPoint screen_position, unsigned int button, unsigned int buttons, unsigned int modifiers)
|
||||||
{
|
{
|
||||||
enqueue_input_event(
|
enqueue_input_event(
|
||||||
QueuedMouseEvent {
|
QueuedMouseEvent {
|
||||||
.type = QueuedMouseEvent::Type::MouseUp,
|
.type = QueuedMouseEvent::Type::MouseUp,
|
||||||
.position = position,
|
.position = position,
|
||||||
|
.screen_position = screen_position,
|
||||||
.button = button,
|
.button = button,
|
||||||
.buttons = buttons,
|
.buttons = buttons,
|
||||||
.modifiers = modifiers,
|
.modifiers = modifiers,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectionFromClient::mouse_wheel(Gfx::IntPoint position, unsigned int button, unsigned int buttons, unsigned int modifiers, i32 wheel_delta_x, i32 wheel_delta_y)
|
void ConnectionFromClient::mouse_wheel(Gfx::IntPoint position, Gfx::IntPoint screen_position, unsigned int button, unsigned int buttons, unsigned int modifiers, i32 wheel_delta_x, i32 wheel_delta_y)
|
||||||
{
|
{
|
||||||
auto event = QueuedMouseEvent {
|
auto event = QueuedMouseEvent {
|
||||||
.type = QueuedMouseEvent::Type::MouseWheel,
|
.type = QueuedMouseEvent::Type::MouseWheel,
|
||||||
.position = position,
|
.position = position,
|
||||||
|
.screen_position = screen_position,
|
||||||
.button = button,
|
.button = button,
|
||||||
.buttons = buttons,
|
.buttons = buttons,
|
||||||
.modifiers = modifiers,
|
.modifiers = modifiers,
|
||||||
|
@ -302,12 +311,13 @@ void ConnectionFromClient::mouse_wheel(Gfx::IntPoint position, unsigned int butt
|
||||||
enqueue_input_event(move(event));
|
enqueue_input_event(move(event));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectionFromClient::doubleclick(Gfx::IntPoint position, unsigned int button, unsigned int buttons, unsigned int modifiers)
|
void ConnectionFromClient::doubleclick(Gfx::IntPoint position, Gfx::IntPoint screen_position, unsigned int button, unsigned int buttons, unsigned int modifiers)
|
||||||
{
|
{
|
||||||
enqueue_input_event(
|
enqueue_input_event(
|
||||||
QueuedMouseEvent {
|
QueuedMouseEvent {
|
||||||
.type = QueuedMouseEvent::Type::DoubleClick,
|
.type = QueuedMouseEvent::Type::DoubleClick,
|
||||||
.position = position,
|
.position = position,
|
||||||
|
.screen_position = screen_position,
|
||||||
.button = button,
|
.button = button,
|
||||||
.buttons = buttons,
|
.buttons = buttons,
|
||||||
.modifiers = modifiers,
|
.modifiers = modifiers,
|
||||||
|
|
|
@ -58,11 +58,11 @@ private:
|
||||||
virtual void load_html(DeprecatedString const&, URL const&) override;
|
virtual void load_html(DeprecatedString const&, URL const&) override;
|
||||||
virtual void paint(Gfx::IntRect const&, i32) override;
|
virtual void paint(Gfx::IntRect const&, i32) override;
|
||||||
virtual void set_viewport_rect(Gfx::IntRect const&) override;
|
virtual void set_viewport_rect(Gfx::IntRect const&) override;
|
||||||
virtual void mouse_down(Gfx::IntPoint, unsigned, unsigned, unsigned) override;
|
virtual void mouse_down(Gfx::IntPoint, Gfx::IntPoint, unsigned, unsigned, unsigned) override;
|
||||||
virtual void mouse_move(Gfx::IntPoint, unsigned, unsigned, unsigned) override;
|
virtual void mouse_move(Gfx::IntPoint, Gfx::IntPoint, unsigned, unsigned, unsigned) override;
|
||||||
virtual void mouse_up(Gfx::IntPoint, unsigned, unsigned, unsigned) override;
|
virtual void mouse_up(Gfx::IntPoint, Gfx::IntPoint, unsigned, unsigned, unsigned) override;
|
||||||
virtual void mouse_wheel(Gfx::IntPoint, unsigned, unsigned, unsigned, i32, i32) override;
|
virtual void mouse_wheel(Gfx::IntPoint, Gfx::IntPoint, unsigned, unsigned, unsigned, i32, i32) override;
|
||||||
virtual void doubleclick(Gfx::IntPoint, unsigned, unsigned, unsigned) override;
|
virtual void doubleclick(Gfx::IntPoint, Gfx::IntPoint, unsigned, unsigned, unsigned) override;
|
||||||
virtual void key_down(i32, unsigned, u32) override;
|
virtual void key_down(i32, unsigned, u32) override;
|
||||||
virtual void key_up(i32, unsigned, u32) override;
|
virtual void key_up(i32, unsigned, u32) override;
|
||||||
virtual void add_backing_store(i32, Gfx::ShareableBitmap const&) override;
|
virtual void add_backing_store(i32, Gfx::ShareableBitmap const&) override;
|
||||||
|
@ -144,6 +144,7 @@ private:
|
||||||
};
|
};
|
||||||
Type type {};
|
Type type {};
|
||||||
Gfx::IntPoint position {};
|
Gfx::IntPoint position {};
|
||||||
|
Gfx::IntPoint screen_position {};
|
||||||
unsigned button {};
|
unsigned button {};
|
||||||
unsigned buttons {};
|
unsigned buttons {};
|
||||||
unsigned modifiers {};
|
unsigned modifiers {};
|
||||||
|
|
|
@ -27,11 +27,11 @@ endpoint WebContentServer
|
||||||
paint(Gfx::IntRect content_rect, i32 backing_store_id) =|
|
paint(Gfx::IntRect content_rect, i32 backing_store_id) =|
|
||||||
set_viewport_rect(Gfx::IntRect rect) =|
|
set_viewport_rect(Gfx::IntRect rect) =|
|
||||||
|
|
||||||
mouse_down(Gfx::IntPoint position, unsigned button, unsigned buttons, unsigned modifiers) =|
|
mouse_down(Gfx::IntPoint position, Gfx::IntPoint screen_position, unsigned button, unsigned buttons, unsigned modifiers) =|
|
||||||
mouse_move(Gfx::IntPoint position, unsigned button, unsigned buttons, unsigned modifiers) =|
|
mouse_move(Gfx::IntPoint position, Gfx::IntPoint screen_position, unsigned button, unsigned buttons, unsigned modifiers) =|
|
||||||
mouse_up(Gfx::IntPoint position, unsigned button, unsigned buttons, unsigned modifiers) =|
|
mouse_up(Gfx::IntPoint position, Gfx::IntPoint screen_position, unsigned button, unsigned buttons, unsigned modifiers) =|
|
||||||
mouse_wheel(Gfx::IntPoint position, unsigned button, unsigned buttons, unsigned modifiers, i32 wheel_delta_x, i32 wheel_delta_y) =|
|
mouse_wheel(Gfx::IntPoint position, Gfx::IntPoint screen_position, unsigned button, unsigned buttons, unsigned modifiers, i32 wheel_delta_x, i32 wheel_delta_y) =|
|
||||||
doubleclick(Gfx::IntPoint position, unsigned button, unsigned buttons, unsigned modifiers) =|
|
doubleclick(Gfx::IntPoint position, Gfx::IntPoint screen_position, unsigned button, unsigned buttons, unsigned modifiers) =|
|
||||||
|
|
||||||
key_down(i32 key, unsigned modifiers, u32 code_point) =|
|
key_down(i32 key, unsigned modifiers, u32 code_point) =|
|
||||||
key_up(i32 key, unsigned modifiers, u32 code_point) =|
|
key_up(i32 key, unsigned modifiers, u32 code_point) =|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue