Skip to content

Fix bug in cropping, update Turbojpeg search#90

Merged
AlanRockefeller merged 2 commits intomainfrom
pr/update-editor
May 4, 2026
Merged

Fix bug in cropping, update Turbojpeg search#90
AlanRockefeller merged 2 commits intomainfrom
pr/update-editor

Conversation

@AlanRockefeller
Copy link
Copy Markdown
Owner

@AlanRockefeller AlanRockefeller commented May 4, 2026

Summary by CodeRabbit

  • Improvements

    • Single, clearer fallback warning when TurboJPEG is unavailable, now including platform-specific install hints.
    • Smoother, more responsive crop overlay behavior during zooming, panning, and dragging.
    • QML data properties now provide JS-friendly native types for more reliable JavaScript access.
  • Tests

    • Updated tests to expect the revised TurboJPEG warning text.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 4, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b3a30881-25d2-40a1-aa53-dbc5f06573b7

📥 Commits

Reviewing files that changed from the base of the PR and between 770aa5a and 5cccef5.

📒 Files selected for processing (4)
  • faststack/imaging/turbo.py
  • faststack/qml/Components.qml
  • faststack/tests/test_turbo.py
  • faststack/ui/provider.py
✅ Files skipped from review due to trivial changes (1)
  • faststack/tests/test_turbo.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • faststack/ui/provider.py
  • faststack/imaging/turbo.py
  • faststack/qml/Components.qml

Walkthrough

Three independent changes: (1) TurboJPEG discovery is cached per candidate set with consolidated one-time fallback warnings and platform-specific install hints; (2) QML crop overlay was refactored from imperative rect updates to binding-driven geometry with revision tracking; (3) UIState QML properties now expose concrete JS-friendly types (QVariantMap and QVariantList) and return native dict/list.

Changes

TurboJPEG Initialization Caching

Layer / File(s) Summary
Module Setup & Warning State
faststack/imaging/turbo.py
Adds module-level state and imports to track which fallback warnings have been emitted.
Platform Install Hint
faststack/imaging/turbo.py
Adds _install_hint() to produce concise, platform-specific libjpeg-turbo/TurboJPEG installation hints.
One-time Warning Helper
faststack/imaging/turbo.py
Adds _warn_fallback_once(message, *args) to ensure a fallback warning is logged only once per process.
Cached Probing Logic
faststack/imaging/turbo.py
Adds @lru_cache-decorated _create_turbojpeg_cached(_decoder_identity, candidates) that probes candidate library paths at most once per (TurboJPEG id, candidate tuple), emits consolidated one-time fallback warnings (including install hint) on failure, and returns (None, False) on failure or (decoder, True) on success.
API Delegation
faststack/imaging/turbo.py
create_turbojpeg() now delegates to the cached helper using candidates = tuple(_candidate_library_paths()) instead of probing and warning on every call.

QML Crop Overlay — Declarative Binding Refactor

Layer / File(s) Summary
Interaction Early-Return
faststack/qml/Components.qml (lines ~209–212)
When _lockedZoom is active, onZoomScaleChanged reverts zoomScale and returns early to avoid further updates.
Remove Imperative Crop Calls
faststack/qml/Components.qml (lines ~318–328)
panTransform handlers no longer call cropOverlay.updateCropRect(); they rely on bindings to update overlay geometry.
Binding-driven Overlay
faststack/qml/Components.qml (lines ~358–404)
Replaces imperative updateCropRect() and cropBox handling with _cropBoxRev, _liveCropBox(), computed flags (hasActiveCrop, hasDrawableCrop, showCropContent), and cropRect geometry bound to uiStateRef.currentCropBox. Dimmer visibility keys off hasDrawableCrop.

UIState QML Type Refinements

Layer / File(s) Summary
Internal Default Change
faststack/ui/provider.py
UIState.__init__ changes _current_crop_box default from list to tuple ((0,0,1000,1000)).
Histogram & Highlight Types
faststack/ui/provider.py (lines ~1092–1120)
UIState.histogramData and UIState.highlightState change property declarations from QVariant to QVariantMap; their getters now return plain dict objects (with {} fallback for histogram).
Current CropBox Type
faststack/ui/provider.py (lines ~1228–1235)
UIState.currentCropBox property now declared QVariantList; getter returns a plain list(self._current_crop_box) (or [] fallback) instead of returning a tuple directly to QML/JS.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • AlanRockefeller/faststack#58: Modifies faststack/imaging/turbo.py TurboJPEG discovery/initialization logic — overlaps with the caching and fallback changes.
  • AlanRockefeller/faststack#21: Touches faststack/qml/Components.qml and faststack/ui/provider.py crop/overlay and property-type interactions — related to the QML and UIState changes.
  • #71: Appears to overlap with crop/overlay handling and interaction logic in QML (potentially related; check diff for direct file overlap).
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 64.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the two main changes: a bug fix in cropping (Components.qml) and updates to TurboJPEG search logic (turbo.py).
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch pr/update-editor

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
faststack/qml/Components.qml (1)

361-361: 💤 Low value

Unused property can be removed.

The cropBox property appears to be dead code now that _liveCropBox() is used for all crop box access. The new revision-based pattern supersedes this direct property binding.

🧹 Remove unused property
-                    property var cropBox: loupeView.uiStateRef ? loupeView.uiStateRef.currentCropBox : [0, 0, 1000, 1000]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@faststack/qml/Components.qml` at line 361, The property declaration "property
var cropBox" is dead now that the code uses the revision-based getter
_liveCropBox(); remove the unused property declaration (property var cropBox
...) from Components.qml and update any remaining usages to call _liveCropBox()
(or use the existing uiStateRef.currentCropBox via _liveCropBox()) so nothing
references the removed symbol; ensure no other bindings depend on cropBox before
committing the deletion.
faststack/ui/provider.py (1)

1228-1235: 💤 Low value

Minor internal type inconsistency.

The getter converts to list, but internally _current_crop_box is initialized as a list (line 322) while the setter converts incoming values to tuple (line 1251). Since list != tuple in Python even when elements match, the comparison at line 1273 will be True on the first property set, causing a spurious signal emission.

Consider initializing with a tuple for consistency:

♻️ Optional: Initialize as tuple for consistency
-        self._current_crop_box = [0, 0, 1000, 1000]
+        self._current_crop_box = (0, 0, 1000, 1000)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@faststack/ui/provider.py` around lines 1228 - 1235, The getter currentCropBox
returns a list while the internal attribute _current_crop_box is set/compared as
a tuple by the setter, causing the first set to look different and emit a
spurious change signal; fix by making the internal representation
consistent—initialize _current_crop_box as a tuple (e.g., () instead of []) and
ensure the setter that currently converts incoming values to tuple keeps that
behavior so comparisons against _current_crop_box match, or alternatively change
the setter to store a list; update only _current_crop_box initialization to
tuple to match the setter and keep the currentCropBox getter converting to list
for QML.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@faststack/imaging/turbo.py`:
- Around line 116-120: The warning text passed to _warn_fallback_once was
changed from "PyTurboJPEG not found" to "PyTurboJPEG is not installed", which
breaks the existing unit test that asserts the exact substring; either revert
the message back to the original "PyTurboJPEG not found" in the call to
_warn_fallback_once (keeping _install_hint() as-is), or update the test named
test_turbo.py that asserts the old substring to expect the new "PyTurboJPEG is
not installed" wording instead — modify whichever you choose so the string in
the _warn_fallback_once(...) call and the test assertion match exactly.
- Around line 100-106: The current _warn_fallback_once uses a single global
boolean _fallback_warning_emitted which suppresses all subsequent warnings
regardless of message; change it to track deduplication per message by replacing
the boolean with a set (e.g., _fallback_warnings_emitted: set[str]) and update
_warn_fallback_once to check if the given message is already in that set, add it
when first emitted, and then call log.warning(message, *args) only for new
messages; keep the function name _warn_fallback_once and the log.warning call so
callers need no change.

---

Nitpick comments:
In `@faststack/qml/Components.qml`:
- Line 361: The property declaration "property var cropBox" is dead now that the
code uses the revision-based getter _liveCropBox(); remove the unused property
declaration (property var cropBox ...) from Components.qml and update any
remaining usages to call _liveCropBox() (or use the existing
uiStateRef.currentCropBox via _liveCropBox()) so nothing references the removed
symbol; ensure no other bindings depend on cropBox before committing the
deletion.

In `@faststack/ui/provider.py`:
- Around line 1228-1235: The getter currentCropBox returns a list while the
internal attribute _current_crop_box is set/compared as a tuple by the setter,
causing the first set to look different and emit a spurious change signal; fix
by making the internal representation consistent—initialize _current_crop_box as
a tuple (e.g., () instead of []) and ensure the setter that currently converts
incoming values to tuple keeps that behavior so comparisons against
_current_crop_box match, or alternatively change the setter to store a list;
update only _current_crop_box initialization to tuple to match the setter and
keep the currentCropBox getter converting to list for QML.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e855e6e6-d904-4392-b91b-3bbd32dd3e2f

📥 Commits

Reviewing files that changed from the base of the PR and between a8e83ad and 770aa5a.

📒 Files selected for processing (3)
  • faststack/imaging/turbo.py
  • faststack/qml/Components.qml
  • faststack/ui/provider.py

Comment thread faststack/imaging/turbo.py
Comment thread faststack/imaging/turbo.py
@AlanRockefeller AlanRockefeller merged commit 1e6d100 into main May 4, 2026
3 checks passed
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