How to Create Responsive Overlays with THTMLPopup
Overview
- THTMLPopup is a lightweight HTML-based popup control (commonly used with THtmlViewer/related Delphi components). Use it to show contextual overlays, tooltips, menus, or small dialogs that adapt to different window sizes and DPI.
Key principles
- Flexible sizing: size to content but enforce min/max dimensions.
- Adaptive positioning: keep the popup within screen/workarea bounds and reposition when the parent moves or window resizes.
- Responsive content: use CSS (percent widths, flexbox) and relative units (em, rem) inside the popup HTML.
- DPI-awareness: scale fonts and sizes based on current DPI (Delphi: Screen.PixelsPerInch or VCL scaling).
Implementation steps (Delphi / component-agnostic)
-
Create the popup
- Instantiate THTMLPopup (or equivalent) and set its parent/owner.
- Load HTML content via the component’s HTML property or SetDocumentStream.
-
Use responsive HTML/CSS
- Wrap content in a responsive container:
- width: 100%; max-width: 400px; box-sizing: border-box;
- Use flexbox for layout and media queries for small screens.
- Use relative units: font-size: 0.95rem; padding: 0.5rem.
- Wrap content in a responsive container:
-
Size to content with limits
- After loading content, measure rendered size (component provides content size or use viewer’s DocumentWidth/Height).
- Apply:
- width := Min(Max(contentWidth, minW), maxW)
- height := Min(Max(contentHeight, minH), maxH)
- Reflow if dimensions changed.
-
Smart positioning
- Preferred anchor: coordinates or control bounds.
- Compute popupRect anchored below/above or to the side.
- If popupRect extends beyond Monitor.WorkArea, flip anchor or shift to fit.
- Keep a small margin from screen edges (e.g., 8–12 px).
-
Handle window/monitor/DPI changes
- Subscribe to parent form’s OnResize/OnMove and to WMDPICHANGED (or Delphi scale events).
- On such events: recompute position and sizes; reload CSS scale if needed.
-
Close behavior & input focus
- Close on outside click: capture mouse or set popup to close on deactivation.
- Keyboard: handle Esc to close; manage Tab/Shift-Tab focus if interactive.
- If interactive content (links/forms), keep focus inside until explicit close.
-
Animation and performance
- Use CSS transitions for fade/slide; keep animations short (120–200 ms).
- Avoid heavy scripts inside popup; lazy-load images if needed.
Example CSS snippet (use inside popup HTML)
Code
.container { box-sizing: border-box; width: 100%; max-width: 420px; padding: 0.6rem; font-size: 0.95rem; display: flex; flex-direction: column; gap: 0.4rem; } @media (max-width: 360px) { .container { padding: 0.4rem; font-size: 0.9rem; } }
Practical tips
- Provide both max-width and max-height and enable internal scrolling when needed.
- Cache popup content when reused to avoid reload delays.
- Test across multiple DPI settings and secondary monitors.
- For touch, increase tap targets and add a small delay before auto-closing to avoid accidental dismissals.
Troubleshooting
- Popup flickers on parent resize: debounce reposition calls (50–120 ms).
- Content clipped on high-DPI monitors: recalculate sizes after WM_DPICHANGED.
- Outside-click not detected: ensure popup window style allows deactivation messages or implement a transparent click-catcher window.
If you want, I can generate a ready-to-use Delphi example (component creation, positioning routine, and event handlers).
Leave a Reply