Skip to content

fix(presentation): tool-specific cursor + Pen/Highlighter icons with outline pointing up-left#115

Merged
nelsonduarte merged 7 commits into
mainfrom
fix/presentation-hud-cursor-icons
Jul 11, 2026
Merged

fix(presentation): tool-specific cursor + Pen/Highlighter icons with outline pointing up-left#115
nelsonduarte merged 7 commits into
mainfrom
fix/presentation-hud-cursor-icons

Conversation

@nelsonduarte

Copy link
Copy Markdown
Owner

Summary

Three UX fixes for the presentation-mode annotation HUD:

  1. Cursor didn't reflect the active tool — clicking Pen/Highlighter/Eraser/Laser on the HUD only changed internal state; the on-screen cursor stayed as the default arrow.
  2. Pen/Highlighter icons pointed in a different direction than the pointer — the mouse-pointer icon points up-left, but Pen and Highlighter kept their FontAwesome default (bottom-right).
  3. Icons hard to see on light backgrounds — solid-fill icons blended into white slides. Now every rendered icon has a 1px black outline.

Fixes

_cursor_for_tool() helper (new, in annotation_layer.py)

  • Pointer -> ArrowCursor
  • Pen / Highlighter -> qtawesome icon rotated +90deg via QTransform.rotate(), with a 1px black outline composited via 8-direction QPainter dilation, hotspot at (4, 4) so the tip lands at the click position
  • Eraser -> outlined icon with centred hotspot
  • Laser -> BlankCursor (the red dot is painted manually in paintEvent)

_tool_qta_icon() helper (new, in annotation_hud.py)

  • Same outline + QTransform rotation applied to HUD buttons, so the toolbar row and the active cursor stay visually aligned.

Why QTransform and not qta.icon(..., rotated=90)

The initial fix used qtawesome's rotated option. Debug rendering showed it works offscreen, but user-side runtime reports were inconsistent — likely a corner case where the option doesn't propagate through the pixmap pipeline. Switching to Qt-native QTransform.rotate(90) on the resulting pixmap removes the dependency on qtawesome's kwargs and produces a deterministic result across all Qt versions.

Why the outline

Pen and highlighter tips are single-colour glyphs, and in dark-mode presentation they're rendered in white. On white or light-yellow slides the shape would blur into the background. An 8-direction offset dilation (drawing the same glyph in black at (+/-1, 0), (0, +/-1), and diagonals) produces a clean 1px outline without needing SVG editing or a bespoke stroke path.

Iteration record

The rotation value went through pixel-level verification: 12 candidate rotations were rendered offscreen at 48x48 and their darkest-pixel centroid was located. +90deg was the only value that produced a top-left tip matching the pointer, ruling out earlier guesses of -90deg and 180deg.

Tests

tests/test_annotation_hud.py — 10 source-level regression tests:

  • HUD Pen/Highlighter icons have rotation
  • Rotation table covers both tools
  • Overlay set_tool calls setCursor
  • Cursor helper covers all tools
  • HUD and cursor helper use matching rotation values
  • Laser uses BlankCursor
  • Icons have outline (both HUD and cursor)
  • Rotation applies reliably (via QTransform or qtawesome rotated=)

Validation

  • 5 atomic commits (2 initial + rotation correction + outline/QTransform fix + tests)
  • pytest tests/test_annotation_hud.py -> 10 passed
  • Manual QA on Windows: HUD icons visually match pointer; cursor changes shape per tool; hotspot lands at click point; outline visible on light slides
  • No APP_VERSION bump
  • No new i18n keys
  • No new dependencies

Manual QA before merge

Launch presentation mode (F5), open a light-coloured slide, verify:

  1. HUD Pen/Highlighter icons visually point up-left, matching the pointer arrow
  2. Icons have a visible black outline
  3. Cursor changes shape when clicking each HUD button
  4. Pen/Highlighter cursor tip lands where you click
  5. Theme switch re-tints the cursor

nelsonduarte and others added 5 commits July 11, 2026 11:42
The HUD toolbar changes the selected tool but the on-screen cursor
remained a generic system cursor (or the default arrow), giving no
visual feedback of which tool was active. Now setCursor() is called
on tool change with a pixmap built from qtawesome icons matching the
HUD.

- Pen / Highlighter: cursor is the tool icon rotated -90 so the tip
  points at the click position (hotspot at bottom-left)
- Eraser: qtawesome eraser icon with centred hotspot
- Laser: BlankCursor (the red dot is drawn manually)
- Pointer: default ArrowCursor via unsetCursor()

The cursor rebuild is driven by dark_mode so it re-tints on theme
switch (update_theme already calls _apply_cursor).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…irection

The HUD row placed the mouse-pointer icon with its tip toward the
bottom-left, but the Pen and Highlighter icons kept their default
FontAwesome orientation (tip toward the bottom-right), which read as
visually inconsistent. Both icons now go through a small rotation
registry (_ICON_ROTATION) that applies rotated=-90 so their writing
tips point in the same direction as the pointer arrow.

The registry is honoured by both _refresh_icons (base render) and
_refresh_active (per-state colour swap), so the rotation survives
active-state toggles and theme switches.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The mouse pointer icon points up-left (standard cursor convention),
but the fix from commit 2c23d14 used rotated=-90 which orientation
didn't match. Adjusted to rotated=180 (paired with a top-left hotspot
in the cursor) so Pen and Highlighter now visually align with the
pointer's direction.

Both the HUD icon table (_ICON_ROTATION) and the cursor helper
(_cursor_for_tool) use the same value, and the cursor hotspot moved
from (2, size-2) to (2, 2) so the tip of the tool anchors at the
top-left corner of the cursor pixmap.
Commit 51bc941 used rotated=180 assuming it would flip the icon to
up-left, but 180° is rotation (not flip) — it left the tip in the
top-right corner. Pixel analysis of qtawesome renders confirms that
rotated=90 (clockwise quarter turn) is what actually maps the native
pen tip (bottom-right) to top-left, matching the mouse-pointer.

Same value applied in both _ICON_ROTATION (HUD) and _cursor_for_tool
(overlay cursor) so HUD icons and active cursors stay visually
aligned. Hotspot for pen/highlighter stays at (2, 2) since the tip
still lands in the top-left corner after the 90° rotation.
Two related fixes:

1. qtawesome's rotated= parameter did not consistently apply to icons
   rendered as pixmaps (used for cursors), so the pen and highlighter
   cursors kept the default bottom-right tip orientation even though the
   HUD icons themselves rendered correctly at rotated=90. Rotation now
   applies via QTransform.rotate(90) on the resulting pixmap, which is
   Qt-native and reliable across icon -> pixmap conversions.

2. Icons now render with a 1px black outline (8-direction dilation
   composited via QPainter) so pen/highlighter/eraser cursors and HUD
   buttons remain readable on light-coloured slides.

Same compositing pattern used in both annotation_hud.py and
annotation_layer.py to keep visual consistency between the toolbar and
the active cursor.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@nelsonduarte nelsonduarte added the bug Something isn't working label Jul 11, 2026
Points defaultInterpreterPath to the local venv so Pylance resolves
imports like pypdf, fitz, PySide6 without needing manual interpreter
selection on every workspace open. Also whitelists workspaceFolder
in analysis.extraPaths so 'from app import ...' resolves.

Relaxes .gitignore to keep .vscode/settings.json and extensions.json
tracked while ignoring the rest of .vscode/. Includes an
extensions.json recommending the Python + Pylance + debugpy trio.

Fixes user-reported 'Import "pypdf" could not be resolved' errors
after venv recreation.
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 11, 2026

Copy link
Copy Markdown

Deploying pdfapps with  Cloudflare Pages  Cloudflare Pages

Latest commit: e1df565
Status: ✅  Deploy successful!
Preview URL: https://f9d85f22.pdfapps.pages.dev
Branch Preview URL: https://fix-presentation-hud-cursor.pdfapps.pages.dev

View logs

The black outline stroke added in the previous commit was necessary
for cursor icons - those float over arbitrary slide content and need
extra visibility. But it was also being applied to the HUD toolbar
icons, which sit on the fixed dark toolbar background and don't need
it. The result was cluttered-looking toolbar icons.

annotation_hud.py now renders icons cleanly (just rotation, no outline).
annotation_layer.py keeps the outline logic for cursor pixmaps.

Tests updated: former test_hud_icons_have_black_outline replaced with
test_hud_icons_have_no_outline (regression against re-adding outline
to HUD); test_cursor_icons_have_black_outline retained (regression
against losing outline on cursors).
@nelsonduarte nelsonduarte merged commit 6f4bbfd into main Jul 11, 2026
4 checks passed
@nelsonduarte nelsonduarte deleted the fix/presentation-hud-cursor-icons branch July 11, 2026 14:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant