Skip to content

Add clipboard history carousel - #1166

Open
qpd-v wants to merge 1 commit into
sabrogden:masterfrom
qpd-v:feature/clipboard-carousel-slim
Open

qpd-v wants to merge 1 commit into
sabrogden:masterfrom
qpd-v:feature/clipboard-carousel-slim

Conversation

@qpd-v

@qpd-v qpd-v commented Oct 12, 2026 •

Copy link
Copy Markdown

Summary

Adds an opt-in Clipboard Carousel for browsing saved clips in a lightweight popup without opening the full Quick Paste list.

The main workflow is: open with a configured shortcut, scroll to a clip, optionally select part of its text, then click an editable destination to paste.

Motivation

Related to #151. This provides the global history-cycling and visible-preview workflow, but uses press-to-open with explicit confirmation rather than confirming when a modifier is released. It does not claim to implement that issue's exact interaction model.

A typical use case is retrieving an earlier snippet or image while working in another application, without navigating the full Ditto window. Text selection also lets the user paste only the useful part of a saved clip.

Behavior

  • Disabled and unassigned by default. Enable it under Options → Clipboard Carousel and assign a keyboard shortcut or Middle Button/XButton1/XButton2, with optional Ctrl/Shift/Alt modifiers. Newly assigning a bare mouse button warns that it replaces that button's normal action while enabled.
  • Opens on the most recent clip in Ditto's clipOrder. Down/wheel down moves toward older entries; Up/wheel up moves toward newer entries. Navigation wraps at both ends.
  • Shows bounded text previews, file paths/lists, or proportional DIB/PNG thumbnails in a fixed-size, DPI-scaled popup. HTML/RTF use available plain-text descriptions for preview; whole-clip confirmation preserves the stored formats.
  • Enter confirms to the original target and honors Ditto's existing send-paste preference. Escape cancels.
  • Paste when clicking outside the carousel is checked by default within the opt-in feature. An outside left click uses the clicked application's focused control as the destination. Turning it off makes outside clicks cancel instead. The destination must accept the pasted content.
  • Clicking inside the popup never counts as clicking away. Text can be selected; confirmation pastes only a non-empty selection as plain Unicode text. Ctrl+A/C work in the text preview, and Ctrl+C keeps the carousel open and follows normal capture/duplicate/filter settings.
  • Merely browsing or canceling does not load clips onto the clipboard, reorder history, or mark clips pasted. Explicit Ctrl+C is an intentional copy operation. Full-clip confirmation uses normal Ditto paste/history semantics; pasting a selected fragment does not mark the full source clip as pasted.

There is also a desktop image workflow: when the session starts on the desktop, selecting an image and left-clicking the desktop with outside-click paste enabled exports the original-resolution image as a uniquely named PNG. It uses Windows' configured Desktop folder and never overwrites an existing file. Export does not alter the clipboard or paste/history bookkeeping. Text/file clips, taskbar clicks, Enter, and application-started sessions do not export desktop files.

Implementation

  • Separate controller, popup, input handler, and small options page; lifecycle and dispatch are integrated with MainFrm.
  • History is read in indexed 100-row keyset batches, with a bounded 300-item cache and session bounds rather than total-history loading or counting. Group containers are excluded; saved clips inside groups remain available. Ordering follows clipOrder, not Quick Paste's current group/filter/sticky presentation.
  • Reuses CProcessPaste/COleClipSource, CClip, GetClipData, existing image helpers, and ExternalWindowTracker. Previews read saved data directly. A background thumbnail worker rejects stale results and stops before database shutdown.
  • Keyboard activation uses RegisterHotKey. Global mouse activation requires a low-level mouse hook; session wheel/outside-click handling and temporary keyboard controls share a dedicated hook thread. Callbacks dispatch to the UI thread rather than querying the database or pasting. Keyboard/session hooks drain consumed releases before cleanup; an idle mouse hook remains only for configured mouse activation.
  • Uses existing profile storage and language string lookups with English fallbacks. There is no new localization mechanism or translation-file sweep.
  • Adds a small Alt-release correction in SendKeys to avoid unpaired Alt key-ups triggering Windows 11 Notepad's access-key hints.

Testing

Clean Release x64 rebuild passed with MSVC v145, dynamic MFC, and Windows SDK 10.0.26100.0. There were no carousel-origin warnings or errors; existing warnings were not suppressed. This matches the solution/configuration used by the repository's PR workflow.

MSBuild.exe CP_Main_10.sln /t:Rebuild /m:1 /p:Configuration=Release /p:Platform=x64 /nologo

The following GUI checks were performed manually on Windows 11 against the same source now consolidated into this commit; they are not automated CI tests:

  • Normal Ctrl+C capture, Quick Paste opening, and normal paste before/after carousel use.
  • Configured keyboard/mouse activation, modifier persistence, normal mouse behavior after closing, and browser Back/Forward.
  • Older/newer navigation, wrapping, rapid cycling, repeated sessions, Enter, Escape, and disabled outside-click paste.
  • Text/URL/HTML/RTF/image/file previews, fixed size, proportional images, and overflow-only scrollbar.
  • Unicode/multiline selection, Ctrl+C without closing, selection reset, whole-clip paste, and selected-text paste.
  • Click-to-paste between Notepad and Chrome, original-target restoration, and no stuck modifiers/access-key hints.
  • Explorer F2 rename, Explorer address bar, and VS Code Ctrl+P destinations.
  • First opening in both directions between 4K/250% and 1080p/100% monitors, plus screen-edge placement.
  • Preview/cancel clipboard and history semantics, and newly copied content appearing in the next session.
  • Desktop PNG export, original dimensions, both monitors, unique filenames, unchanged clipboard/history, cancellation, and ordinary application paste regression.

Additional source checks:

  • Synthetic 50,000-entry endpoint queries: both wrap directions, repeated loads, single-item endpoints, and retained session bounds.
  • git diff --check upstream/master...HEAD.

No automated end-to-end GUI suite is included. Elevated/protected targets, older Windows versions, and deliberately induced corrupt-image/disk-failure cases remain untested. Normal Windows paste restrictions still apply. Selecting a fragment is limited to the displayed 4,096-character preview; whole-clip confirmation is not truncated. Full-resolution desktop export may take longer for very large images.

Demo

GIF:
ditto-carousel-demo-v2

Video (with sound):

ditto-carousel-trailer-v2.mp4

The demonstration uses synthetic history in an isolated portable profile. A demo-only accommodation for automated input is not included in this PR; production input filtering is unchanged. Trailer camera effects are presentation, not application behavior. No screenshots, recordings, test databases, or recording tools are committed to the repository.

Add opt-in keyboard or mouse activation, lightweight text/image previews, wrapping history navigation, selected-text copy/paste, and optional paste into a clicked destination. Reuse Ditto clipboard and target-tracking machinery. Include desktop image export and the reviewed popup styling.
@qpd-v qpd-v closed this Oct 14, 2026
@qpd-v qpd-v reopened this Oct 14, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant