webdrop-bridge/docs/HOVER_EFFECTS_ANALYSIS.md
claudi 810baf65d9
Some checks are pending
Tests & Quality Checks / Test on Python 3.11 (push) Waiting to run
Tests & Quality Checks / Test on Python 3.12 (push) Waiting to run
Tests & Quality Checks / Test on Python 3.11-1 (push) Waiting to run
Tests & Quality Checks / Test on Python 3.12-1 (push) Waiting to run
Tests & Quality Checks / Test on Python 3.10 (push) Waiting to run
Tests & Quality Checks / Test on Python 3.11-2 (push) Waiting to run
Tests & Quality Checks / Test on Python 3.12-2 (push) Waiting to run
Tests & Quality Checks / Build Artifacts (push) Blocked by required conditions
Tests & Quality Checks / Build Artifacts-1 (push) Blocked by required conditions
feat: implement mouse event emulator for Qt WebEngineView to enhance hover effects
2026-03-04 14:49:40 +01:00

7.4 KiB

Hover Effects Analysis - Qt WebEngineView Limitation

Executive Summary

Status: Hover effects partially functional in Qt WebEngineView, with a clear Qt limitation identified.

  • Checkbox hover: Works correctly
  • Event detection: Polling-based detection functional
  • Menu expansion via :hover: Does NOT work (Qt limitation)
  • Tailwind CSS :hover-based effects: Do NOT work in Qt

Investigation Results

Test Environment

  • Framework: PySide6 QWebEngineView
  • Web App: Angular + Tailwind CSS
  • Browser Test: Google Chrome (reference)
  • Test Date: March 4, 2026

Chrome Browser Results

Both menu expansion and checkbox hover work perfectly in Chrome browser. This confirms the issue is Qt-specific, not a web application problem.

Qt WebEngineView Results

What Works

  1. Checkbox hover effects

    • Checkboxes appear on hover
    • CSS-based simulation via .__mouse_hover class works correctly
    • input[type="checkbox"].__mouse_hover CSS selector successfully applied
  2. Event detection

    • Mouse position tracking: Working
    • document.elementFromPoint() polling: Working (50ms interval)
    • mouseover, mouseenter, mouseleave, mousemove event dispatching: Working
    • Angular event listeners: Receiving dispatched events correctly
  3. DOM element access

    • Menu element found with querySelectorAll()
    • Event listeners identified: {click: Array(1)}
    • Not in Shadow DOM (accessible from JavaScript)

What Doesn't Work

  1. Menu expansion via Tailwind :hover

    • Menu element: .group class with hover:bg-neutral-300
    • Menu children have: .group-hover:w-full (Tailwind pattern)
    • Expected behavior: .group:hover > .group-hover:w-full triggers on hover
    • Actual behavior: No expansion (:hover pseudo-selector not activated)
  2. Tailwind CSS :hover-based styles

    • Pattern: .group:hover > .group-hover:* (Tailwind generated)
    • Root cause: Qt doesn't properly set :hover pseudo-selector state for dispatched events
    • Impact: Any CSS rule depending on :hover pseudo-selector won't work

Technical Analysis

The Core Issue

Qt WebEngineView doesn't forward native mouse events to JavaScript in a way that properly triggers the CSS :hover pseudo-selector. When we dispatch synthetic events:

element.dispatchEvent(new MouseEvent("mouseover", {...}));
element.dispatchEvent(new MouseEvent("mouseenter", {...}));

The browser's CSS engine does not update the :hover pseudo-selector state. This is different from a native browser, where:

  1. User moves mouse
  2. Browser kernel detects native hover
  3. :hover pseudo-selector activates
  4. CSS rules matching :hover are applied

Evidence

Chrome DevTools inspection revealed:

Event Listeners: {click: Array(1)}  // Only CLICK handler, NO hover handlers
Menu element className: "flex h-14 w-full items-center p-2 transition-colors hover:bg-neutral-300 ... group"

The Angular app handles UI in two ways:

  1. Click events: Directly handled by JavaScript listeners → Works
  2. Hover effects: Rely on CSS :hover pseudo-selector → Doesn't work in Qt

Why This Is a Limitation

This is not fixable by JavaScript injection because:

  1. JavaScript can't activate CSS :hover: The :hover pseudo-selector is a browser-native feature that only CSS engines can modify. JavaScript can't directly trigger it.

  2. Tailwind CSS is static: Tailwind generates CSS rules like .group:hover > .group-hover:w-full { width: 11rem; }. These rules expect the :hover pseudo-selector to be active—JavaScript can't force them to apply.

  3. Qt engine limitation: Qt WebEngineView's Chromium engine doesn't properly handle :hover for non-native events.

What We Tried

Approach Result Notes
Direct CSS class injection Failed .group.__mouse_hover doesn't trigger Tailwind rules
PointerEvent dispatch Failed Modern API didn't help
JavaScript style manipulation Failed Can't force Tailwind CSS rules via JS
Polling + synthetic mouse events ⚠️ Partial Works for custom handlers, not for :hover

Implementation Status

Current Solution

File: mouse_event_emulator.js

What it does:

  1. Polls document.elementFromPoint() every 50ms to detect element changes
  2. Dispatches mouseover, mouseenter, mouseleave, mousemove events
  3. Applies .__mouse_hover CSS class for custom hover simulation
  4. Works for elements with JavaScript event handlers

What it doesn't do:

  1. Cannot activate :hover pseudo-selector
  2. Cannot trigger Tailwind CSS hover-based rules
  3. Cannot fix Qt's limitation

Performance

  • CPU overhead: Minimal (polling every 50ms on idle)
  • Startup impact: Negligible
  • Memory footprint: ~2KB script size

Verification Steps

To verify this limitation exists in your Qt environment:

Chrome Test

  1. Open web app in Chrome
  2. Hover over menu → Menu expands
  3. Hover over checkbox → Checkbox appears

Qt Test

  1. Run application in Qt
  2. Hover over menu → Menu does NOT expand (known limitation)
  3. Hover over checkbox → Checkbox appears (works via CSS class)

Debug Verification (if needed)

In Chrome DevTools console:

// Find menu element
const menuGroup = document.querySelector('[class*="group"]');
console.log("Menu group:", menuGroup?.className);

// Check for Shadow DOM
const inShadow = menuGroup?.getRootNode() !== document;
console.log("In Shadow DOM:", inShadow);  // Should be false

// Check event listeners
console.log("Event Listeners:", getEventListeners(menuGroup));  // Shows if handlers exist

Results:

  • Menu element: Found
  • Shadow DOM: No
  • Event listeners: {click: Array(1)} (only click, no hover handlers)

Recommendations

What Developers Should Know

  1. Don't expect :hover effects to work in Qt WebEngineView

    • This is a known limitation, not a bug in WebDrop Bridge
    • The application itself works correctly in Chrome
  2. Workarounds for your web app

    • Replace :hover with JavaScript click handlers
    • Add click-to-toggle functionality instead of hover
    • This is outside the scope of WebDrop Bridge
  3. For similar Qt projects

    • Be aware of this :hover pseudo-selector limitation when embedding web content
    • Consider detecting Qt environment and serving alternative UI
    • Test web apps in actual Chrome browser before embedding in Qt

Future Improvements (Not Feasible)

The following would require Qt framework modifications:

  • Improving QWebEngineView's :hover pseudo-selector support
  • Better mouse event forwarding to browser CSS engine
  • Custom CSS selector handling in embedded browser

None of these are achievable through application-level code.

Summary

WebDrop Bridge successfully emulates hover behavior for elements with JavaScript event handlers (like checkboxes). However, Tailwind CSS and other frameworks that rely on the CSS :hover pseudo-selector will not work fully in Qt WebEngineView due to an inherent limitation in how Qt forwards mouse events to the browser's CSS engine.

This is not a defect in WebDrop Bridge, but rather a limitation of embedding web content in Qt applications. The web application works perfectly in standard browsers like Chrome.


Status: Issue Closed - Limitation Documented
Last Updated: March 4, 2026
Severity: Low (UI-only, core functionality unaffected)