Repository navigation
feat: pin static image translation results from Compact - #769
Conversation
|
First of all, thank you very much for contributing the code. I think the overall idea is great. I have one suggestion regarding the user experience and implementation complexity: Rather than treating Pinned as a third full-featured image translation mode, I think it would be better to let users complete OCR and translation in Compact mode first, and then provide a Pin button and keyboard shortcut to turn the current result into a standalone pinned window.
A pinned window would only need to retain:
Once pinned, the window will no longer support changes to OCR services, translation services, language, or word segmentation settings, nor will it rerun OCR or translation. (Alternatively, we could consider adding support for modifying these settings again in the future, in which case it would be acceptable to revert to the "compact" logic.) Those operations would remain the responsibility of the Compact window. If users need to make adjustments, they can reprocess the image in Compact mode and Pin the updated result again. This would make the responsibilities much clearer:
It could also significantly reduce the state-management and lifecycle complexity of the current implementation. For example, each pinned window would no longer need its own full ViewModel, automatic recomputation, debouncing, task generation tracking, service execution coordinator, or delayed service-release logic. A pinned window would only hold an immutable display snapshot, making it much less likely to be affected by service removal, settings changes, or background tasks. |
Add the Compact Pin button and independent static windows with source/translation selection, copying, context menus, shadow and active glow. Share frozen display results, release Compact after pinning, and hide pins during screenshot selection. Keep OCR and translation in Compact without adding a third mode.
Clip WPF effects to the four visible edge regions and draw the opaque center directly. Preserve the existing colors, blur, opacity and DPI sampling while reusing drawing during movement.
Bind the Compact shortcut to the existing Pin button command. Use the existing hotkey settings, conflict checks and persistence, with localized settings labels. Leave the shortcut unassigned by default and after reset.
29da2fb to
6837f32
Compare
`uk.xaml` was written against `main` @ `2a75118f`. Three commits that landed while STranslate#782 was open added keys to every other language, but could not add them to Ukrainian, because `uk.xaml` did not exist upstream yet: - 8 keys in `src/STranslate/Languages/uk.xaml` — pinned image translation (STranslate#769) and the notification-only fallback for text selection; - 78 keys in the Youdao plugin dictionary — the localized API error messages. So Ukrainian users saw English text in those places: the missing keys resolve through the English dictionaries, which stay merged for the whole session (`LoadDefaultLanguage` loads every plugin's `en.xaml` and then clears `_oldResources`, so `RemoveOldLanguageFiles` never takes them out). The one case where the raw `No Translation for key ...` shows up is a plugin installed in the current session: `LoadInstalledPluginLanguages` merges only the active language for it until the next restart. Both files are back to the `en.xaml` key set *and* order: 792/792 for the main dictionary, 82/82 for the plugin, with the blank-line section breaks in the same places, so each file is now line-for-line structurally identical to its `en.xaml`. Insertions only, no existing line is touched. Verified: `dotnet build -c Debug` 0 errors, `dotnet test` 296/296, every language dictionary in the tree loads through `XamlReader.Load` with the same key count as its `en.xaml` neighbour, and the new values pass LanguageTool with the `uk-UA` profile with no remaining warnings.

Thank you very much for taking the time to review my contribution and explain your suggestions in detail. Your feedback helped me understand where I had made the implementation unnecessarily complicated and how to better fit the existing project. I've reworked it around the flow you suggested: complete screenshot capture, OCR and translation in Compact, then use Pin to keep the result in an independent, always-on-top window. I'd be grateful for any further guidance on anything that still needs improvement.
Changes addressing the feedback:
The implementation uses the existing WPF/MVVM and dependency-injection structure. The main-window dispatch, service manager and OCR plugin interfaces are unchanged. Pin extends the existing window-hotkey settings and binds to the toolbar command through WPF; it adds no global hotkey registration.
PinnedWindowControllermanages pin lifetime and screenshot visibility; the existing screenshot methods acquire a hide/restore scope. The image-translation ViewModel exposes completed results and pin availability, with cancellation checks before publishing results. The content window reusesImageZoom; selection helpers use paragraph membership from the existing layout analysis without changing segmentation rules, retain clipped text for copying, and clear stale selection when the text source changes. Settings gains one shadow-default value.Static pins have no polling or background recomputation, and zoom animations are disabled. Shadow/glow effects are clipped to four edge regions, with the opaque center drawn directly. Drawing is rebuilt on size, DPI or activation changes; moving an unchanged pin reuses it.
Validation: Debug and Release builds succeed; Release completes with zero warnings and errors. The linked test code passes 326 regression tests, and the submitted code passes all 274 existing upstream tests. Rendering checks include 168 pixel comparisons against the expected shadow/glow appearance across sizes, state changes and 100%–300% DPI. Basic functional checks and manual testing cover the screenshot/OCR/translation-to-pin flow, appearance, selection, copying and menus. Shortcut checks cover saved settings, defaults, clearing, resetting and conflicts. A compiled Compact-window test with a prepared result verifies live key bindings, rejection of unfinished results, and the shared Pin command for user-defined keys, including Ctrl+Alt+F8. Resetting leaves the shortcut unassigned, and the Pin button remains usable without a shortcut. Window-level checks cover screenshot hide/restore, creation and closure during capture, and release after closing; closed-window weak-reference counts are 0 for the 1-, 10- and 30-window groups.
See Test code and Performance tests and raw results.
Performance: snapshot creation averages 8.58 μs over 1,000 iterations, with 12,032 bytes of managed allocation per snapshot. The 30-window hide/restore median is 33.09 ms, including two DWM synchronizations. Process CPU-time increments during three-second idle samples are 109.38 / 62.50 / 46.88 ms for 1 / 10 / 30 windows. Creating and initializing the windows takes 152 ms for one pin and 2.85 s for a rapid batch of 30.
Measurements use Windows 11 build 26200, x64, .NET 10.0.11, 96 DPI and WPF Tier 2, with 800×400 BGRA source/annotated image pairs and 73 characters per text layer. Benchmark windows are shown off-screen. Component-memory measurements use 30 independent image pairs and pins, with three fresh processes per configuration after a one-window warm-up.
The component measurement adds images, text/snapshots, content windows and chrome in stages, ending with 29 shadows and one active glow. Each stage waits for Dispatcher idle, 500 ms and a full GC. The median total private-memory increment is 355.79 MiB; its component breakdown is:
Percentages are rounded. These process increments include rendering resources and runtime caches. The component measurement starts with image creation; actual pinning shares Compact's existing images. A separate measurement using the production controller to open 30 pins sequentially, allowing each to render while active, records a median total increment of 354.36 MiB after a two-second wait and full GC. Content rendering retains ImageZoom's hardware rendering and transparent-image support.
Below is a screenshot from testing in a real usage scenario: