mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-30 04:39:06 +00:00
LibWeb+WebContent+UI: Support image cursors
The `cursor` property accepts a list of possible cursors, which behave as a fallback: We use whichever cursor is the first available one. This is a little complicated because initially, any remote images have not loaded, so we need to use the fallback standard cursor, and then switch to another when it loads. So, ComputedValues stores a Vector of cursors, and then in EventHandler we scan down that list until we find a cursor that's ready for use. The spec defines cursors as being `<url>`, but allows for `<image>` instead. That includes functions like `linear-gradient()`. This commit implements image cursors in the Qt UI, but not AppKit.
This commit is contained in:
parent
fd2414ba35
commit
bfd7ac1204
Notes:
github-actions[bot]
2025-02-28 12:51:27 +00:00
Author: https://github.com/AtkinsSJ
Commit: bfd7ac1204
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3644
20 changed files with 297 additions and 170 deletions
|
@ -11,7 +11,6 @@
|
||||||
#include <LibGC/CellAllocator.h>
|
#include <LibGC/CellAllocator.h>
|
||||||
#include <LibWeb/CSS/Clip.h>
|
#include <LibWeb/CSS/Clip.h>
|
||||||
#include <LibWeb/CSS/ComputedProperties.h>
|
#include <LibWeb/CSS/ComputedProperties.h>
|
||||||
#include <LibWeb/CSS/StyleValues/AngleStyleValue.h>
|
|
||||||
#include <LibWeb/CSS/StyleValues/CSSKeywordValue.h>
|
#include <LibWeb/CSS/StyleValues/CSSKeywordValue.h>
|
||||||
#include <LibWeb/CSS/StyleValues/ColorSchemeStyleValue.h>
|
#include <LibWeb/CSS/StyleValues/ColorSchemeStyleValue.h>
|
||||||
#include <LibWeb/CSS/StyleValues/ContentStyleValue.h>
|
#include <LibWeb/CSS/StyleValues/ContentStyleValue.h>
|
||||||
|
@ -32,7 +31,6 @@
|
||||||
#include <LibWeb/CSS/StyleValues/PercentageStyleValue.h>
|
#include <LibWeb/CSS/StyleValues/PercentageStyleValue.h>
|
||||||
#include <LibWeb/CSS/StyleValues/PositionStyleValue.h>
|
#include <LibWeb/CSS/StyleValues/PositionStyleValue.h>
|
||||||
#include <LibWeb/CSS/StyleValues/RectStyleValue.h>
|
#include <LibWeb/CSS/StyleValues/RectStyleValue.h>
|
||||||
#include <LibWeb/CSS/StyleValues/ScrollbarGutterStyleValue.h>
|
|
||||||
#include <LibWeb/CSS/StyleValues/ShadowStyleValue.h>
|
#include <LibWeb/CSS/StyleValues/ShadowStyleValue.h>
|
||||||
#include <LibWeb/CSS/StyleValues/StringStyleValue.h>
|
#include <LibWeb/CSS/StyleValues/StringStyleValue.h>
|
||||||
#include <LibWeb/CSS/StyleValues/StyleValueList.h>
|
#include <LibWeb/CSS/StyleValues/StyleValueList.h>
|
||||||
|
@ -954,13 +952,30 @@ ContentVisibility ComputedProperties::content_visibility() const
|
||||||
return keyword_to_content_visibility(value.to_keyword()).release_value();
|
return keyword_to_content_visibility(value.to_keyword()).release_value();
|
||||||
}
|
}
|
||||||
|
|
||||||
Cursor ComputedProperties::cursor() const
|
Vector<CursorData> ComputedProperties::cursor() const
|
||||||
{
|
{
|
||||||
|
// Return the first available cursor.
|
||||||
auto const& value = property(PropertyID::Cursor);
|
auto const& value = property(PropertyID::Cursor);
|
||||||
// FIXME: We don't currently support custom cursors.
|
Vector<CursorData> cursors;
|
||||||
if (value.is_url())
|
if (value.is_value_list()) {
|
||||||
return Cursor::Auto;
|
for (auto const& item : value.as_value_list().values()) {
|
||||||
return keyword_to_cursor(value.to_keyword()).release_value();
|
if (item->is_cursor()) {
|
||||||
|
cursors.append({ item->as_cursor() });
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto keyword = keyword_to_cursor(item->to_keyword()); keyword.has_value())
|
||||||
|
cursors.append(keyword.release_value());
|
||||||
|
}
|
||||||
|
} else if (value.is_keyword()) {
|
||||||
|
if (auto keyword = keyword_to_cursor(value.to_keyword()); keyword.has_value())
|
||||||
|
cursors.append(keyword.release_value());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cursors.is_empty())
|
||||||
|
cursors.append(Cursor::Auto);
|
||||||
|
|
||||||
|
return cursors;
|
||||||
}
|
}
|
||||||
|
|
||||||
Visibility ComputedProperties::visibility() const
|
Visibility ComputedProperties::visibility() const
|
||||||
|
|
|
@ -94,7 +94,7 @@ public:
|
||||||
};
|
};
|
||||||
ContentDataAndQuoteNestingLevel content(DOM::Element&, u32 initial_quote_nesting_level) const;
|
ContentDataAndQuoteNestingLevel content(DOM::Element&, u32 initial_quote_nesting_level) const;
|
||||||
ContentVisibility content_visibility() const;
|
ContentVisibility content_visibility() const;
|
||||||
Cursor cursor() const;
|
Vector<CursorData> cursor() const;
|
||||||
Variant<LengthOrCalculated, NumberOrCalculated> tab_size() const;
|
Variant<LengthOrCalculated, NumberOrCalculated> tab_size() const;
|
||||||
WhiteSpace white_space() const;
|
WhiteSpace white_space() const;
|
||||||
WordBreak word_break() const;
|
WordBreak word_break() const;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2020-2023, Andreas Kling <andreas@ladybird.org>
|
* Copyright (c) 2020-2023, Andreas Kling <andreas@ladybird.org>
|
||||||
|
* Copyright (c) 2023-2025, Sam Atkins <sam@ladybird.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -28,7 +29,7 @@
|
||||||
#include <LibWeb/CSS/Size.h>
|
#include <LibWeb/CSS/Size.h>
|
||||||
#include <LibWeb/CSS/StyleValues/AbstractImageStyleValue.h>
|
#include <LibWeb/CSS/StyleValues/AbstractImageStyleValue.h>
|
||||||
#include <LibWeb/CSS/StyleValues/BasicShapeStyleValue.h>
|
#include <LibWeb/CSS/StyleValues/BasicShapeStyleValue.h>
|
||||||
#include <LibWeb/CSS/StyleValues/PositionStyleValue.h>
|
#include <LibWeb/CSS/StyleValues/CursorStyleValue.h>
|
||||||
#include <LibWeb/CSS/StyleValues/ShadowStyleValue.h>
|
#include <LibWeb/CSS/StyleValues/ShadowStyleValue.h>
|
||||||
#include <LibWeb/CSS/Transformation.h>
|
#include <LibWeb/CSS/Transformation.h>
|
||||||
|
|
||||||
|
@ -79,6 +80,8 @@ struct Containment {
|
||||||
bool is_empty() const { return !(size_containment || inline_size_containment || layout_containment || style_containment || paint_containment); }
|
bool is_empty() const { return !(size_containment || inline_size_containment || layout_containment || style_containment || paint_containment); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using CursorData = Variant<NonnullRefPtr<CursorStyleValue>, Cursor>;
|
||||||
|
|
||||||
using ListStyleType = Variant<CounterStyleNameKeyword, String>;
|
using ListStyleType = Variant<CounterStyleNameKeyword, String>;
|
||||||
|
|
||||||
class InitialValues {
|
class InitialValues {
|
||||||
|
@ -94,7 +97,7 @@ public:
|
||||||
static CSS::Clip clip() { return CSS::Clip::make_auto(); }
|
static CSS::Clip clip() { return CSS::Clip::make_auto(); }
|
||||||
static CSS::PreferredColorScheme color_scheme() { return CSS::PreferredColorScheme::Auto; }
|
static CSS::PreferredColorScheme color_scheme() { return CSS::PreferredColorScheme::Auto; }
|
||||||
static CSS::ContentVisibility content_visibility() { return CSS::ContentVisibility::Visible; }
|
static CSS::ContentVisibility content_visibility() { return CSS::ContentVisibility::Visible; }
|
||||||
static CSS::Cursor cursor() { return CSS::Cursor::Auto; }
|
static CursorData cursor() { return { CSS::Cursor::Auto }; }
|
||||||
static CSS::WhiteSpace white_space() { return CSS::WhiteSpace::Normal; }
|
static CSS::WhiteSpace white_space() { return CSS::WhiteSpace::Normal; }
|
||||||
static CSS::WordBreak word_break() { return CSS::WordBreak::Normal; }
|
static CSS::WordBreak word_break() { return CSS::WordBreak::Normal; }
|
||||||
static CSS::LengthOrCalculated word_spacing() { return CSS::Length::make_px(0); }
|
static CSS::LengthOrCalculated word_spacing() { return CSS::Length::make_px(0); }
|
||||||
|
@ -375,7 +378,7 @@ public:
|
||||||
CSS::Clip clip() const { return m_noninherited.clip; }
|
CSS::Clip clip() const { return m_noninherited.clip; }
|
||||||
CSS::PreferredColorScheme color_scheme() const { return m_inherited.color_scheme; }
|
CSS::PreferredColorScheme color_scheme() const { return m_inherited.color_scheme; }
|
||||||
CSS::ContentVisibility content_visibility() const { return m_inherited.content_visibility; }
|
CSS::ContentVisibility content_visibility() const { return m_inherited.content_visibility; }
|
||||||
CSS::Cursor cursor() const { return m_inherited.cursor; }
|
Vector<CursorData> const& cursor() const { return m_inherited.cursor; }
|
||||||
CSS::ContentData content() const { return m_noninherited.content; }
|
CSS::ContentData content() const { return m_noninherited.content; }
|
||||||
CSS::PointerEvents pointer_events() const { return m_inherited.pointer_events; }
|
CSS::PointerEvents pointer_events() const { return m_inherited.pointer_events; }
|
||||||
CSS::Display display() const { return m_noninherited.display; }
|
CSS::Display display() const { return m_noninherited.display; }
|
||||||
|
@ -575,7 +578,7 @@ protected:
|
||||||
Optional<Color> accent_color {};
|
Optional<Color> accent_color {};
|
||||||
Color webkit_text_fill_color { InitialValues::color() };
|
Color webkit_text_fill_color { InitialValues::color() };
|
||||||
CSS::ContentVisibility content_visibility { InitialValues::content_visibility() };
|
CSS::ContentVisibility content_visibility { InitialValues::content_visibility() };
|
||||||
CSS::Cursor cursor { InitialValues::cursor() };
|
Vector<CursorData> cursor { InitialValues::cursor() };
|
||||||
CSS::ImageRendering image_rendering { InitialValues::image_rendering() };
|
CSS::ImageRendering image_rendering { InitialValues::image_rendering() };
|
||||||
CSS::PointerEvents pointer_events { InitialValues::pointer_events() };
|
CSS::PointerEvents pointer_events { InitialValues::pointer_events() };
|
||||||
Variant<LengthOrCalculated, NumberOrCalculated> tab_size { InitialValues::tab_size() };
|
Variant<LengthOrCalculated, NumberOrCalculated> tab_size { InitialValues::tab_size() };
|
||||||
|
@ -763,7 +766,7 @@ public:
|
||||||
void set_clip(CSS::Clip const& clip) { m_noninherited.clip = clip; }
|
void set_clip(CSS::Clip const& clip) { m_noninherited.clip = clip; }
|
||||||
void set_content(ContentData const& content) { m_noninherited.content = content; }
|
void set_content(ContentData const& content) { m_noninherited.content = content; }
|
||||||
void set_content_visibility(CSS::ContentVisibility content_visibility) { m_inherited.content_visibility = content_visibility; }
|
void set_content_visibility(CSS::ContentVisibility content_visibility) { m_inherited.content_visibility = content_visibility; }
|
||||||
void set_cursor(CSS::Cursor cursor) { m_inherited.cursor = cursor; }
|
void set_cursor(Vector<CursorData> cursor) { m_inherited.cursor = move(cursor); }
|
||||||
void set_image_rendering(CSS::ImageRendering value) { m_inherited.image_rendering = value; }
|
void set_image_rendering(CSS::ImageRendering value) { m_inherited.image_rendering = value; }
|
||||||
void set_pointer_events(CSS::PointerEvents value) { m_inherited.pointer_events = value; }
|
void set_pointer_events(CSS::PointerEvents value) { m_inherited.pointer_events = value; }
|
||||||
void set_background_color(Color color) { m_noninherited.background_color = color; }
|
void set_background_color(Color color) { m_noninherited.background_color = color; }
|
||||||
|
|
|
@ -368,6 +368,7 @@ private:
|
||||||
RefPtr<CSSStyleValue> parse_counter_increment_value(TokenStream<ComponentValue>&);
|
RefPtr<CSSStyleValue> parse_counter_increment_value(TokenStream<ComponentValue>&);
|
||||||
RefPtr<CSSStyleValue> parse_counter_reset_value(TokenStream<ComponentValue>&);
|
RefPtr<CSSStyleValue> parse_counter_reset_value(TokenStream<ComponentValue>&);
|
||||||
RefPtr<CSSStyleValue> parse_counter_set_value(TokenStream<ComponentValue>&);
|
RefPtr<CSSStyleValue> parse_counter_set_value(TokenStream<ComponentValue>&);
|
||||||
|
RefPtr<CSSStyleValue> parse_cursor_value(TokenStream<ComponentValue>&);
|
||||||
RefPtr<CSSStyleValue> parse_display_value(TokenStream<ComponentValue>&);
|
RefPtr<CSSStyleValue> parse_display_value(TokenStream<ComponentValue>&);
|
||||||
RefPtr<CSSStyleValue> parse_flex_shorthand_value(TokenStream<ComponentValue>&);
|
RefPtr<CSSStyleValue> parse_flex_shorthand_value(TokenStream<ComponentValue>&);
|
||||||
RefPtr<CSSStyleValue> parse_flex_flow_value(TokenStream<ComponentValue>&);
|
RefPtr<CSSStyleValue> parse_flex_flow_value(TokenStream<ComponentValue>&);
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include <LibWeb/CSS/StyleValues/ColorSchemeStyleValue.h>
|
#include <LibWeb/CSS/StyleValues/ColorSchemeStyleValue.h>
|
||||||
#include <LibWeb/CSS/StyleValues/ContentStyleValue.h>
|
#include <LibWeb/CSS/StyleValues/ContentStyleValue.h>
|
||||||
#include <LibWeb/CSS/StyleValues/CounterDefinitionsStyleValue.h>
|
#include <LibWeb/CSS/StyleValues/CounterDefinitionsStyleValue.h>
|
||||||
|
#include <LibWeb/CSS/StyleValues/CursorStyleValue.h>
|
||||||
#include <LibWeb/CSS/StyleValues/CustomIdentStyleValue.h>
|
#include <LibWeb/CSS/StyleValues/CustomIdentStyleValue.h>
|
||||||
#include <LibWeb/CSS/StyleValues/DisplayStyleValue.h>
|
#include <LibWeb/CSS/StyleValues/DisplayStyleValue.h>
|
||||||
#include <LibWeb/CSS/StyleValues/EasingStyleValue.h>
|
#include <LibWeb/CSS/StyleValues/EasingStyleValue.h>
|
||||||
|
@ -537,6 +538,10 @@ Parser::ParseErrorOr<NonnullRefPtr<CSSStyleValue>> Parser::parse_css_value(Prope
|
||||||
if (auto parsed_value = parse_counter_set_value(tokens); parsed_value && !tokens.has_next_token())
|
if (auto parsed_value = parse_counter_set_value(tokens); parsed_value && !tokens.has_next_token())
|
||||||
return parsed_value.release_nonnull();
|
return parsed_value.release_nonnull();
|
||||||
return ParseError::SyntaxError;
|
return ParseError::SyntaxError;
|
||||||
|
case PropertyID::Cursor:
|
||||||
|
if (auto parsed_value = parse_cursor_value(tokens); parsed_value && !tokens.has_next_token())
|
||||||
|
return parsed_value.release_nonnull();
|
||||||
|
return ParseError::SyntaxError;
|
||||||
case PropertyID::Display:
|
case PropertyID::Display:
|
||||||
if (auto parsed_value = parse_display_value(tokens); parsed_value && !tokens.has_next_token())
|
if (auto parsed_value = parse_display_value(tokens); parsed_value && !tokens.has_next_token())
|
||||||
return parsed_value.release_nonnull();
|
return parsed_value.release_nonnull();
|
||||||
|
@ -958,6 +963,72 @@ RefPtr<CSSStyleValue> Parser::parse_counter_set_value(TokenStream<ComponentValue
|
||||||
return parse_counter_definitions_value(tokens, AllowReversed::No, 0);
|
return parse_counter_definitions_value(tokens, AllowReversed::No, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-ui-3/#cursor
|
||||||
|
RefPtr<CSSStyleValue> Parser::parse_cursor_value(TokenStream<ComponentValue>& tokens)
|
||||||
|
{
|
||||||
|
// [ [<url> [<x> <y>]?,]* <built-in-cursor> ]
|
||||||
|
// So, any number of custom cursor definitions, and then a mandatory cursor name keyword, all comma-separated.
|
||||||
|
|
||||||
|
auto transaction = tokens.begin_transaction();
|
||||||
|
|
||||||
|
StyleValueVector cursors;
|
||||||
|
|
||||||
|
auto parts = parse_a_comma_separated_list_of_component_values(tokens);
|
||||||
|
for (auto i = 0u; i < parts.size(); ++i) {
|
||||||
|
auto& part = parts[i];
|
||||||
|
TokenStream part_tokens { part };
|
||||||
|
|
||||||
|
if (i == parts.size() - 1) {
|
||||||
|
// Cursor keyword
|
||||||
|
part_tokens.discard_whitespace();
|
||||||
|
auto keyword_value = parse_keyword_value(part_tokens);
|
||||||
|
if (!keyword_value || !keyword_to_cursor(keyword_value->to_keyword()).has_value())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
part_tokens.discard_whitespace();
|
||||||
|
if (part_tokens.has_next_token())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
cursors.append(keyword_value.release_nonnull());
|
||||||
|
} else {
|
||||||
|
// Custom cursor definition
|
||||||
|
// <url> [<x> <y>]?
|
||||||
|
// "Conforming user agents may, instead of <url>, support <image> which is a superset."
|
||||||
|
|
||||||
|
part_tokens.discard_whitespace();
|
||||||
|
auto image_value = parse_image_value(part_tokens);
|
||||||
|
if (!image_value)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
part_tokens.discard_whitespace();
|
||||||
|
|
||||||
|
if (part_tokens.has_next_token()) {
|
||||||
|
// x and y, which are both <number>
|
||||||
|
auto x = parse_number(part_tokens);
|
||||||
|
part_tokens.discard_whitespace();
|
||||||
|
auto y = parse_number(part_tokens);
|
||||||
|
part_tokens.discard_whitespace();
|
||||||
|
if (!x.has_value() || !y.has_value() || part_tokens.has_next_token())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
cursors.append(CursorStyleValue::create(image_value.release_nonnull(), x.release_value(), y.release_value()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
cursors.append(CursorStyleValue::create(image_value.release_nonnull(), {}, {}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cursors.is_empty())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
transaction.commit();
|
||||||
|
if (cursors.size() == 1)
|
||||||
|
return *cursors.first();
|
||||||
|
|
||||||
|
return StyleValueList::create(move(cursors), StyleValueList::Separator::Comma);
|
||||||
|
}
|
||||||
|
|
||||||
// https://www.w3.org/TR/css-sizing-4/#aspect-ratio
|
// https://www.w3.org/TR/css-sizing-4/#aspect-ratio
|
||||||
RefPtr<CSSStyleValue> Parser::parse_aspect_ratio_value(TokenStream<ComponentValue>& tokens)
|
RefPtr<CSSStyleValue> Parser::parse_aspect_ratio_value(TokenStream<ComponentValue>& tokens)
|
||||||
{
|
{
|
||||||
|
|
|
@ -83,9 +83,12 @@ static bool parent_element_for_event_dispatch(Painting::Paintable& paintable, GC
|
||||||
return node && layout_node;
|
return node && layout_node;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Gfx::StandardCursor cursor_css_to_gfx(CSS::Cursor cursor)
|
static Gfx::Cursor resolve_cursor(Layout::NodeWithStyle const& layout_node, Vector<CSS::CursorData> const& cursor_data, Gfx::StandardCursor auto_cursor)
|
||||||
{
|
{
|
||||||
switch (cursor) {
|
for (auto const& cursor : cursor_data) {
|
||||||
|
auto result = cursor.visit(
|
||||||
|
[auto_cursor](CSS::Cursor css_cursor) -> Optional<Gfx::Cursor> {
|
||||||
|
switch (css_cursor) {
|
||||||
case CSS::Cursor::Crosshair:
|
case CSS::Cursor::Crosshair:
|
||||||
case CSS::Cursor::Cell:
|
case CSS::Cursor::Cell:
|
||||||
return Gfx::StandardCursor::Crosshair;
|
return Gfx::StandardCursor::Crosshair;
|
||||||
|
@ -132,16 +135,29 @@ static Gfx::StandardCursor cursor_css_to_gfx(CSS::Cursor cursor)
|
||||||
case CSS::Cursor::ZoomIn:
|
case CSS::Cursor::ZoomIn:
|
||||||
case CSS::Cursor::ZoomOut:
|
case CSS::Cursor::ZoomOut:
|
||||||
return Gfx::StandardCursor::Zoom;
|
return Gfx::StandardCursor::Zoom;
|
||||||
|
case CSS::Cursor::Auto:
|
||||||
|
return auto_cursor;
|
||||||
case CSS::Cursor::ContextMenu:
|
case CSS::Cursor::ContextMenu:
|
||||||
case CSS::Cursor::Alias:
|
case CSS::Cursor::Alias:
|
||||||
case CSS::Cursor::Copy:
|
case CSS::Cursor::Copy:
|
||||||
case CSS::Cursor::NoDrop:
|
case CSS::Cursor::NoDrop:
|
||||||
// FIXME: No corresponding GFX Standard Cursor, fallthrough to None
|
// FIXME: No corresponding GFX Standard Cursor, fallthrough to None
|
||||||
case CSS::Cursor::Auto:
|
|
||||||
case CSS::Cursor::Default:
|
case CSS::Cursor::Default:
|
||||||
default:
|
default:
|
||||||
return Gfx::StandardCursor::None;
|
return Gfx::StandardCursor::None;
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
[&layout_node](NonnullRefPtr<CSS::CursorStyleValue> const& cursor_style_value) -> Optional<Gfx::Cursor> {
|
||||||
|
if (auto image_cursor = cursor_style_value->make_image_cursor(layout_node); image_cursor.has_value())
|
||||||
|
return image_cursor.release_value();
|
||||||
|
return {};
|
||||||
|
});
|
||||||
|
if (result.has_value())
|
||||||
|
return result.release_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
// We should never get here
|
||||||
|
return Gfx::StandardCursor::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://drafts.csswg.org/cssom-view/#dom-mouseevent-offsetx
|
// https://drafts.csswg.org/cssom-view/#dom-mouseevent-offsetx
|
||||||
|
@ -692,7 +708,7 @@ EventResult EventHandler::handle_mousemove(CSSPixelPoint viewport_position, CSSP
|
||||||
|
|
||||||
bool hovered_node_changed = false;
|
bool hovered_node_changed = false;
|
||||||
bool is_hovering_link = false;
|
bool is_hovering_link = false;
|
||||||
Gfx::StandardCursor hovered_node_cursor = Gfx::StandardCursor::None;
|
Gfx::Cursor hovered_node_cursor = Gfx::StandardCursor::None;
|
||||||
|
|
||||||
GC::Ptr<Painting::Paintable> paintable;
|
GC::Ptr<Painting::Paintable> paintable;
|
||||||
Optional<int> start_index;
|
Optional<int> start_index;
|
||||||
|
@ -721,7 +737,7 @@ EventResult EventHandler::handle_mousemove(CSSPixelPoint viewport_position, CSSP
|
||||||
return EventResult::Dropped;
|
return EventResult::Dropped;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto const cursor = paintable->computed_values().cursor();
|
auto cursor_data = paintable->computed_values().cursor();
|
||||||
auto pointer_events = paintable->computed_values().pointer_events();
|
auto pointer_events = paintable->computed_values().pointer_events();
|
||||||
// FIXME: Handle other values for pointer-events.
|
// FIXME: Handle other values for pointer-events.
|
||||||
VERIFY(pointer_events != CSS::PointerEvents::None);
|
VERIFY(pointer_events != CSS::PointerEvents::None);
|
||||||
|
@ -739,15 +755,9 @@ EventResult EventHandler::handle_mousemove(CSSPixelPoint viewport_position, CSSP
|
||||||
is_hovering_link = true;
|
is_hovering_link = true;
|
||||||
|
|
||||||
if (paintable->layout_node().is_text_node()) {
|
if (paintable->layout_node().is_text_node()) {
|
||||||
if (cursor == CSS::Cursor::Auto)
|
hovered_node_cursor = resolve_cursor(*paintable->layout_node().parent(), cursor_data, Gfx::StandardCursor::IBeam);
|
||||||
hovered_node_cursor = Gfx::StandardCursor::IBeam;
|
|
||||||
else
|
|
||||||
hovered_node_cursor = cursor_css_to_gfx(cursor);
|
|
||||||
} else if (node->is_element()) {
|
} else if (node->is_element()) {
|
||||||
if (cursor == CSS::Cursor::Auto)
|
hovered_node_cursor = resolve_cursor(static_cast<Layout::NodeWithStyle&>(*layout_node), cursor_data, Gfx::StandardCursor::Arrow);
|
||||||
hovered_node_cursor = Gfx::StandardCursor::Arrow;
|
|
||||||
else
|
|
||||||
hovered_node_cursor = cursor_css_to_gfx(cursor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto page_offset = compute_mouse_event_page_offset(viewport_position);
|
auto page_offset = compute_mouse_event_page_offset(viewport_position);
|
||||||
|
@ -793,7 +803,10 @@ EventResult EventHandler::handle_mousemove(CSSPixelPoint viewport_position, CSSP
|
||||||
|
|
||||||
auto& page = m_navigable->page();
|
auto& page = m_navigable->page();
|
||||||
|
|
||||||
if (page.current_cursor() != hovered_node_cursor) {
|
// FIXME: This check is only approximate. ImageCursors from the same CursorStyleValue share bitmaps, but may repaint them.
|
||||||
|
// So comparing them does not tell you if they are the same image. Also, the image may change even if the hovered
|
||||||
|
// node does not.
|
||||||
|
if (page.current_cursor() != hovered_node_cursor || hovered_node_changed) {
|
||||||
page.set_current_cursor(hovered_node_cursor);
|
page.set_current_cursor(hovered_node_cursor);
|
||||||
page.client().page_did_request_cursor_change(hovered_node_cursor);
|
page.client().page_did_request_cursor_change(hovered_node_cursor);
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,8 +127,8 @@ public:
|
||||||
bool is_in_tooltip_area() const { return m_is_in_tooltip_area; }
|
bool is_in_tooltip_area() const { return m_is_in_tooltip_area; }
|
||||||
void set_is_in_tooltip_area(bool b) { m_is_in_tooltip_area = b; }
|
void set_is_in_tooltip_area(bool b) { m_is_in_tooltip_area = b; }
|
||||||
|
|
||||||
Gfx::StandardCursor current_cursor() const { return m_current_cursor; }
|
Gfx::Cursor current_cursor() const { return m_current_cursor; }
|
||||||
void set_current_cursor(Gfx::StandardCursor cursor) { m_current_cursor = cursor; }
|
void set_current_cursor(Gfx::Cursor cursor) { m_current_cursor = move(cursor); }
|
||||||
|
|
||||||
DevicePixelPoint window_position() const { return m_window_position; }
|
DevicePixelPoint window_position() const { return m_window_position; }
|
||||||
void set_window_position(DevicePixelPoint position) { m_window_position = position; }
|
void set_window_position(DevicePixelPoint position) { m_window_position = position; }
|
||||||
|
@ -258,7 +258,7 @@ private:
|
||||||
bool m_is_hovering_link { false };
|
bool m_is_hovering_link { false };
|
||||||
bool m_is_in_tooltip_area { false };
|
bool m_is_in_tooltip_area { false };
|
||||||
|
|
||||||
Gfx::StandardCursor m_current_cursor { Gfx::StandardCursor::Arrow };
|
Gfx::Cursor m_current_cursor { Gfx::StandardCursor::Arrow };
|
||||||
|
|
||||||
DevicePixelPoint m_window_position {};
|
DevicePixelPoint m_window_position {};
|
||||||
DevicePixelSize m_window_size {};
|
DevicePixelSize m_window_size {};
|
||||||
|
@ -338,7 +338,7 @@ public:
|
||||||
virtual void page_did_create_new_document(Web::DOM::Document&) { }
|
virtual void page_did_create_new_document(Web::DOM::Document&) { }
|
||||||
virtual void page_did_change_active_document_in_top_level_browsing_context(Web::DOM::Document&) { }
|
virtual void page_did_change_active_document_in_top_level_browsing_context(Web::DOM::Document&) { }
|
||||||
virtual void page_did_finish_loading(URL::URL const&) { }
|
virtual void page_did_finish_loading(URL::URL const&) { }
|
||||||
virtual void page_did_request_cursor_change(Gfx::StandardCursor) { }
|
virtual void page_did_request_cursor_change(Gfx::Cursor const&) { }
|
||||||
virtual void page_did_request_context_menu(CSSPixelPoint) { }
|
virtual void page_did_request_context_menu(CSSPixelPoint) { }
|
||||||
virtual void page_did_request_link_context_menu(CSSPixelPoint, URL::URL const&, [[maybe_unused]] ByteString const& target, [[maybe_unused]] unsigned modifiers) { }
|
virtual void page_did_request_link_context_menu(CSSPixelPoint, URL::URL const&, [[maybe_unused]] ByteString const& target, [[maybe_unused]] unsigned modifiers) { }
|
||||||
virtual void page_did_request_image_context_menu(CSSPixelPoint, URL::URL const&, [[maybe_unused]] ByteString const& target, [[maybe_unused]] unsigned modifiers, Optional<Gfx::Bitmap const*>) { }
|
virtual void page_did_request_image_context_menu(CSSPixelPoint, URL::URL const&, [[maybe_unused]] ByteString const& target, [[maybe_unused]] unsigned modifiers, Optional<Gfx::Bitmap const*>) { }
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022-2023, Andreas Kling <andreas@ladybird.org>
|
* Copyright (c) 2022-2023, Andreas Kling <andreas@ladybird.org>
|
||||||
|
* Copyright (c) 2025, Sam Atkins <sam@ladybird.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <LibWeb/DOM/Document.h>
|
#include <LibWeb/DOM/Document.h>
|
||||||
#include <LibWeb/Layout/BlockContainer.h>
|
|
||||||
#include <LibWeb/Painting/Paintable.h>
|
#include <LibWeb/Painting/Paintable.h>
|
||||||
#include <LibWeb/Painting/PaintableBox.h>
|
#include <LibWeb/Painting/PaintableBox.h>
|
||||||
#include <LibWeb/Painting/StackingContext.h>
|
#include <LibWeb/Painting/StackingContext.h>
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <AK/NonnullOwnPtr.h>
|
|
||||||
#include <LibGC/Root.h>
|
#include <LibGC/Root.h>
|
||||||
|
#include <LibGfx/Cursor.h>
|
||||||
#include <LibWeb/CSS/ComputedValues.h>
|
#include <LibWeb/CSS/ComputedValues.h>
|
||||||
#include <LibWeb/Forward.h>
|
#include <LibWeb/Forward.h>
|
||||||
#include <LibWeb/InvalidateDisplayList.h>
|
#include <LibWeb/InvalidateDisplayList.h>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022-2023, Andreas Kling <andreas@ladybird.org>
|
* Copyright (c) 2022-2023, Andreas Kling <andreas@ladybird.org>
|
||||||
* Copyright (c) 2022-2023, Sam Atkins <atkinssj@serenityos.org>
|
* Copyright (c) 2022-2025, Sam Atkins <sam@ladybird.org>
|
||||||
* Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
|
* Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
|
||||||
* Copyright (c) 2025, Jelle Raaijmakers <jelle@ladybird.org>
|
* Copyright (c) 2025, Jelle Raaijmakers <jelle@ladybird.org>
|
||||||
*
|
*
|
||||||
|
|
|
@ -194,7 +194,7 @@ public:
|
||||||
Function<void(URL::URL const&)> on_load_finish;
|
Function<void(URL::URL const&)> on_load_finish;
|
||||||
Function<void(ByteString const& path, i32)> on_request_file;
|
Function<void(ByteString const& path, i32)> on_request_file;
|
||||||
Function<void(Gfx::Bitmap const&)> on_favicon_change;
|
Function<void(Gfx::Bitmap const&)> on_favicon_change;
|
||||||
Function<void(Gfx::StandardCursor)> on_cursor_change;
|
Function<void(Gfx::Cursor const&)> on_cursor_change;
|
||||||
Function<void(Gfx::IntPoint, ByteString const&)> on_request_tooltip_override;
|
Function<void(Gfx::IntPoint, ByteString const&)> on_request_tooltip_override;
|
||||||
Function<void()> on_stop_tooltip_override;
|
Function<void()> on_stop_tooltip_override;
|
||||||
Function<void(ByteString const&)> on_enter_tooltip_area;
|
Function<void(ByteString const&)> on_enter_tooltip_area;
|
||||||
|
|
|
@ -123,16 +123,11 @@ void WebContentClient::did_request_refresh(u64 page_id)
|
||||||
view->reload();
|
view->reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebContentClient::did_request_cursor_change(u64 page_id, i32 cursor_type)
|
void WebContentClient::did_request_cursor_change(u64 page_id, Gfx::Cursor const& cursor)
|
||||||
{
|
{
|
||||||
if (cursor_type < 0 || cursor_type >= (i32)Gfx::StandardCursor::__Count) {
|
|
||||||
dbgln("DidRequestCursorChange: Bad cursor type");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto view = view_for_page_id(page_id); view.has_value()) {
|
if (auto view = view_for_page_id(page_id); view.has_value()) {
|
||||||
if (view->on_cursor_change)
|
if (view->on_cursor_change)
|
||||||
view->on_cursor_change(static_cast<Gfx::StandardCursor>(cursor_type));
|
view->on_cursor_change(cursor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,7 @@ private:
|
||||||
virtual void did_paint(u64 page_id, Gfx::IntRect const&, i32) override;
|
virtual void did_paint(u64 page_id, Gfx::IntRect const&, i32) override;
|
||||||
virtual void did_finish_loading(u64 page_id, URL::URL const&) override;
|
virtual void did_finish_loading(u64 page_id, URL::URL const&) override;
|
||||||
virtual void did_request_refresh(u64 page_id) override;
|
virtual void did_request_refresh(u64 page_id) override;
|
||||||
virtual void did_request_cursor_change(u64 page_id, i32) override;
|
virtual void did_request_cursor_change(u64 page_id, Gfx::Cursor const&) override;
|
||||||
virtual void did_change_title(u64 page_id, ByteString const&) override;
|
virtual void did_change_title(u64 page_id, ByteString const&) override;
|
||||||
virtual void did_change_url(u64 page_id, URL::URL const&) override;
|
virtual void did_change_url(u64 page_id, URL::URL const&) override;
|
||||||
virtual void did_request_tooltip_override(u64 page_id, Gfx::IntPoint, ByteString const&) override;
|
virtual void did_request_tooltip_override(u64 page_id, Gfx::IntPoint, ByteString const&) override;
|
||||||
|
|
|
@ -251,9 +251,9 @@ void PageClient::set_viewport_size(Web::DevicePixelSize const& size)
|
||||||
m_pending_set_browser_zoom_request = false;
|
m_pending_set_browser_zoom_request = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PageClient::page_did_request_cursor_change(Gfx::StandardCursor cursor)
|
void PageClient::page_did_request_cursor_change(Gfx::Cursor const& cursor)
|
||||||
{
|
{
|
||||||
client().async_did_request_cursor_change(m_id, (u32)cursor);
|
client().async_did_request_cursor_change(m_id, cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PageClient::page_did_layout()
|
void PageClient::page_did_layout()
|
||||||
|
|
|
@ -114,7 +114,7 @@ private:
|
||||||
virtual Web::CSS::PreferredColorScheme preferred_color_scheme() const override { return m_preferred_color_scheme; }
|
virtual Web::CSS::PreferredColorScheme preferred_color_scheme() const override { return m_preferred_color_scheme; }
|
||||||
virtual Web::CSS::PreferredContrast preferred_contrast() const override { return m_preferred_contrast; }
|
virtual Web::CSS::PreferredContrast preferred_contrast() const override { return m_preferred_contrast; }
|
||||||
virtual Web::CSS::PreferredMotion preferred_motion() const override { return m_preferred_motion; }
|
virtual Web::CSS::PreferredMotion preferred_motion() const override { return m_preferred_motion; }
|
||||||
virtual void page_did_request_cursor_change(Gfx::StandardCursor) override;
|
virtual void page_did_request_cursor_change(Gfx::Cursor const&) override;
|
||||||
virtual void page_did_layout() override;
|
virtual void page_did_layout() override;
|
||||||
virtual void page_did_change_title(ByteString const&) override;
|
virtual void page_did_change_title(ByteString const&) override;
|
||||||
virtual void page_did_change_url(URL::URL const&) override;
|
virtual void page_did_change_url(URL::URL const&) override;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include <LibCore/AnonymousBuffer.h>
|
#include <LibCore/AnonymousBuffer.h>
|
||||||
#include <LibGfx/Color.h>
|
#include <LibGfx/Color.h>
|
||||||
|
#include <LibGfx/Cursor.h>
|
||||||
#include <LibGfx/ShareableBitmap.h>
|
#include <LibGfx/ShareableBitmap.h>
|
||||||
#include <LibURL/URL.h>
|
#include <LibURL/URL.h>
|
||||||
#include <LibWeb/Cookie/Cookie.h>
|
#include <LibWeb/Cookie/Cookie.h>
|
||||||
|
@ -24,7 +25,7 @@ endpoint WebContentClient
|
||||||
did_finish_loading(u64 page_id, URL::URL url) =|
|
did_finish_loading(u64 page_id, URL::URL url) =|
|
||||||
did_request_refresh(u64 page_id) =|
|
did_request_refresh(u64 page_id) =|
|
||||||
did_paint(u64 page_id, Gfx::IntRect content_rect, i32 bitmap_id) =|
|
did_paint(u64 page_id, Gfx::IntRect content_rect, i32 bitmap_id) =|
|
||||||
did_request_cursor_change(u64 page_id, i32 cursor_type) =|
|
did_request_cursor_change(u64 page_id, Gfx::Cursor cursor) =|
|
||||||
did_change_title(u64 page_id, ByteString title) =|
|
did_change_title(u64 page_id, ByteString title) =|
|
||||||
did_change_url(u64 page_id, URL::URL url) =|
|
did_change_url(u64 page_id, URL::URL url) =|
|
||||||
did_request_tooltip_override(u64 page_id, Gfx::IntPoint position, ByteString title) =|
|
did_request_tooltip_override(u64 page_id, Gfx::IntPoint position, ByteString title) =|
|
||||||
|
|
|
@ -2,8 +2,8 @@ Harness status: OK
|
||||||
|
|
||||||
Found 42 tests
|
Found 42 tests
|
||||||
|
|
||||||
36 Pass
|
40 Pass
|
||||||
6 Fail
|
2 Fail
|
||||||
Pass e.style['cursor'] = "auto" should set the property value
|
Pass e.style['cursor'] = "auto" should set the property value
|
||||||
Pass e.style['cursor'] = "default" should set the property value
|
Pass e.style['cursor'] = "default" should set the property value
|
||||||
Pass e.style['cursor'] = "none" should set the property value
|
Pass e.style['cursor'] = "none" should set the property value
|
||||||
|
@ -40,9 +40,9 @@ Pass e.style['cursor'] = "row-resize" should set the property value
|
||||||
Pass e.style['cursor'] = "all-scroll" should set the property value
|
Pass e.style['cursor'] = "all-scroll" should set the property value
|
||||||
Pass e.style['cursor'] = "zoom-in" should set the property value
|
Pass e.style['cursor'] = "zoom-in" should set the property value
|
||||||
Pass e.style['cursor'] = "zoom-out" should set the property value
|
Pass e.style['cursor'] = "zoom-out" should set the property value
|
||||||
Fail e.style['cursor'] = "url(\"https://example.com/\"), alias" should set the property value
|
Pass e.style['cursor'] = "url(\"https://example.com/\"), alias" should set the property value
|
||||||
Fail e.style['cursor'] = "url(\"https://example.com/\") 1 calc(2 + 0), copy" should set the property value
|
Pass e.style['cursor'] = "url(\"https://example.com/\") 1 calc(2 + 0), copy" should set the property value
|
||||||
Fail e.style['cursor'] = "url(\"https://example.com/\"), url(\"https://example.com/\") 3 -4, move" should set the property value
|
Pass e.style['cursor'] = "url(\"https://example.com/\"), url(\"https://example.com/\") 3 -4, move" should set the property value
|
||||||
Fail e.style['cursor'] = "url(\"https://example.com/\") 5 6, grab" should set the property value
|
Pass e.style['cursor'] = "url(\"https://example.com/\") 5 6, grab" should set the property value
|
||||||
Fail e.style['cursor'] = "image-set(\"https://example.com/\" 1x) 5 6, grab" should set the property value
|
Fail e.style['cursor'] = "image-set(\"https://example.com/\" 1x) 5 6, grab" should set the property value
|
||||||
Fail e.style['cursor'] = "image-set(\"https://example.com/\" 1x, \"https://example.com/highres\" 2x) 5 6, grab" should set the property value
|
Fail e.style['cursor'] = "image-set(\"https://example.com/\" 1x, \"https://example.com/highres\" 2x) 5 6, grab" should set the property value
|
|
@ -450,7 +450,15 @@ static void copy_data_to_clipboard(StringView data, NSPasteboardType pasteboard_
|
||||||
if (self == nil) {
|
if (self == nil) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (cursor == Gfx::StandardCursor::Hidden) {
|
if (!cursor.template has<Gfx::StandardCursor>()) {
|
||||||
|
// FIXME: Implement image cursors in AppKit.
|
||||||
|
[[NSCursor arrowCursor] set];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto standard_cursor = cursor.template get<Gfx::StandardCursor>();
|
||||||
|
|
||||||
|
if (standard_cursor == Gfx::StandardCursor::Hidden) {
|
||||||
if (!m_hidden_cursor.has_value()) {
|
if (!m_hidden_cursor.has_value()) {
|
||||||
m_hidden_cursor.emplace();
|
m_hidden_cursor.emplace();
|
||||||
}
|
}
|
||||||
|
@ -460,7 +468,7 @@ static void copy_data_to_clipboard(StringView data, NSPasteboardType pasteboard_
|
||||||
|
|
||||||
m_hidden_cursor.clear();
|
m_hidden_cursor.clear();
|
||||||
|
|
||||||
switch (cursor) {
|
switch (standard_cursor) {
|
||||||
case Gfx::StandardCursor::Arrow:
|
case Gfx::StandardCursor::Arrow:
|
||||||
[[NSCursor arrowCursor] set];
|
[[NSCursor arrowCursor] set];
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022-2023, Andreas Kling <andreas@ladybird.org>
|
* Copyright (c) 2022-2023, Andreas Kling <andreas@ladybird.org>
|
||||||
* Copyright (c) 2023, Linus Groh <linusg@serenityos.org>
|
* Copyright (c) 2023, Linus Groh <linusg@serenityos.org>
|
||||||
|
* Copyright (c) 2024-2025, Sam Atkins <sam@ladybird.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -630,9 +631,10 @@ void WebContentView::initialize_client(WebView::ViewImplementation::CreateNewCli
|
||||||
update_screen_rects();
|
update_screen_rects();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebContentView::update_cursor(Gfx::StandardCursor cursor)
|
void WebContentView::update_cursor(Gfx::Cursor cursor)
|
||||||
{
|
{
|
||||||
switch (cursor) {
|
cursor.visit([this](Gfx::StandardCursor standard_cursor) {
|
||||||
|
switch (standard_cursor) {
|
||||||
case Gfx::StandardCursor::Hidden:
|
case Gfx::StandardCursor::Hidden:
|
||||||
setCursor(Qt::BlankCursor);
|
setCursor(Qt::BlankCursor);
|
||||||
break;
|
break;
|
||||||
|
@ -690,7 +692,25 @@ void WebContentView::update_cursor(Gfx::StandardCursor cursor)
|
||||||
default:
|
default:
|
||||||
setCursor(Qt::ArrowCursor);
|
setCursor(Qt::ArrowCursor);
|
||||||
break;
|
break;
|
||||||
|
} },
|
||||||
|
[this](Gfx::ImageCursor const& image_cursor) {
|
||||||
|
if (!image_cursor.bitmap.is_valid()) {
|
||||||
|
dbgln("Failed to set cursor: Bitmap is invalid.");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
auto const& bitmap = *image_cursor.bitmap.bitmap();
|
||||||
|
auto qimage = QImage { bitmap.scanline_u8(0), bitmap.width(), bitmap.height(), QImage::Format_ARGB32 };
|
||||||
|
if (qimage.isNull()) {
|
||||||
|
dbgln("Failed to set cursor: Null QImage.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto qpixmap = QPixmap::fromImage(qimage);
|
||||||
|
if (qimage.isNull()) {
|
||||||
|
dbgln("Failed to set cursor: Couldn't create QPixmap from QImage.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setCursor(QCursor { qpixmap, image_cursor.hotspot.x(), image_cursor.hotspot.y() });
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Web::DevicePixelSize WebContentView::viewport_size() const
|
Web::DevicePixelSize WebContentView::viewport_size() const
|
||||||
|
|
|
@ -100,7 +100,7 @@ private:
|
||||||
virtual Gfx::IntPoint to_widget_position(Gfx::IntPoint content_position) const override;
|
virtual Gfx::IntPoint to_widget_position(Gfx::IntPoint content_position) const override;
|
||||||
|
|
||||||
void update_viewport_size();
|
void update_viewport_size();
|
||||||
void update_cursor(Gfx::StandardCursor cursor);
|
void update_cursor(Gfx::Cursor cursor);
|
||||||
|
|
||||||
void enqueue_native_event(Web::MouseEvent::Type, QSinglePointEvent const& event);
|
void enqueue_native_event(Web::MouseEvent::Type, QSinglePointEvent const& event);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue