Skip to content

feat: separate search boxes for Frozen and Preview sections#97

Merged
Limitex merged 13 commits into
mainfrom
feat/search-box
Apr 18, 2026
Merged

feat: separate search boxes for Frozen and Preview sections#97
Limitex merged 13 commits into
mainfrom
feat/search-box

Conversation

@Limitex
Copy link
Copy Markdown
Owner

@Limitex Limitex commented Apr 14, 2026

Summary by CodeRabbit

  • New Features

    • Search box shows "Showing X of Y" and a "{N} hidden" indicator; preview label now renders below thumbnails.
  • Refactoring

    • Independent, native search fields for Preview and Frozen sections; search state and counting simplified and moved into those sections.
  • Bug Fixes

    • Preview entries disable correctly after freeze/unfreeze; frozen search updates when assets are renamed/moved.
  • Style

    • Hidden-count centered; added centered bold and clipped label styles.
  • Tests

    • Expanded tests for search behavior, counting/caching, clear behavior, and path-cache.

@Limitex Limitex self-assigned this Apr 14, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 14, 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: eaeac628-f9f3-4056-ae6f-d957282dadda

📥 Commits

Reviewing files that changed from the base of the PR and between d852730 and b5753a8.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • Editor/Common/UI/Styles/EditorStylesCache.cs
  • Editor/TextureCompressor/UI/Preview/PreviewSection.cs
  • Editor/TextureCompressor/UI/Sections/FrozenTexturesSection.cs
🚧 Files skipped from review as they are similar to previous changes (1)
  • CHANGELOG.md

📝 Walkthrough

Walkthrough

Search UI moved from a custom IMGUI control to Unity's SearchField and into section-owned instances. Index-based match caching was replaced by collection-based count caching and fixed-arity match APIs. Added a GUID→path in-memory cache invalidated on project changes and a helper to draw hidden-count labels.

Changes

Cohort / File(s) Summary
Search Control Refactor
Editor/Common/UI/Controls/SearchBoxControl.cs
Replaced custom IMGUI with UnityEditor.IMGUI.Controls.SearchField; removed index/event-based match APIs; added ctor, CountMatches<T>(IReadOnlyCollection<T>, Func<T,bool>), InvalidateCountCache(), fixed-arity MatchesSearchAny overloads, and changed Draw signature to Draw(int matchedCount = -1, int totalCount = -1).
Style & Draw Utilities
Editor/Common/UI/Styles/EditorStylesCache.cs, Editor/Common/UI/Utils/EditorDrawUtils.cs
Removed PlaceholderStyle/HitCountStyle; added CenteredBoldLabel and ClippedBoldLabel; adjusted HiddenCountStyle alignment to MiddleCenter; added EditorDrawUtils.DrawHiddenCount(int).
GUID→Path Cache
Editor/Common/UI/Utils/GuidPathCache.cs, Editor/Common/UI/Utils/GuidPathCache.cs.meta
Added [InitializeOnLoad] static GuidPathCache with GetPath(string) and Clear() storing GUID→path mappings and clearing them on EditorApplication.projectChanged; included .meta.
Preview Section State
Editor/TextureCompressor/UI/Preview/PreviewSection.cs
PreviewSection now owns its _searchBox; Draw no longer accepts external SearchBoxControl; replaced prior count/match helpers with _searchBox.CountMatches(...)/_searchBox.MatchesSearchAny(...); resolves paths via GuidPathCache.GetPath(...); draws hidden count via EditorDrawUtils.DrawHiddenCount(...); removed public InvalidateCache().
Frozen Textures Section State
Editor/TextureCompressor/UI/Sections/FrozenTexturesSection.cs
Converted from static to instance type; encapsulates _searchBox, _showSection, _scrollPosition; matching/counting uses _searchBox; path resolution via GuidPathCache.GetPath(...); removed local GUID cache.
Editor Integration
Editor/TextureCompressor/TextureCompressorEditor.cs
Removed editor-shared SearchBoxControl; added a FrozenTexturesSection instance on the editor and call sites updated to Draw(config); removed editor-level frozen UI state passed by ref.
Tests
Tests/Editor/UI/SearchBoxControlTests.cs, Tests/Editor/UI/GuidPathCacheTests.cs, Tests/Editor/UI/GuidPathCacheTests.cs.meta
Updated tests for new SearchBoxControl ctor, revised matching semantics, collection-based CountMatches and cache invalidation; added GuidPathCacheTests creating/renaming assets to validate cache behavior; added .meta.
Changelog
CHANGELOG.md
Documented independent search state for Frozen/Preview sections, preview label reposition, freeze/unfreeze UI adjustments, and GUID→path cache invalidation on project changes.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant UI as PreviewSection UI
    participant Search as SearchBoxControl
    participant Cache as GuidPathCache
    participant AssetDB as AssetDatabase

    UI->>Search: Draw(matchedCount?, totalCount?)
    UI->>Search: CountMatches(items, predicate)
    Note over Search,Cache: predicate resolves item GUID → path
    Search->>Cache: GetPath(guid)
    Cache->>AssetDB: GUIDToAssetPath(guid) if not cached
    AssetDB-->>Cache: returns path
    Cache-->>Search: cached path
    Search-->>UI: returns match booleans / matched count
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 I hopped through code and found a way,
SearchField now leads the UI ballet.
Paths cached, hidden counts in sight,
Sections hold their search, tidy and light.
Hop, test, and build—carrots for the night.

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning No pull request description was provided; the author left it entirely blank, violating the repository's description template requirements. Add a comprehensive description addressing the template sections: type (Feature), summary of changes, related issue, bulleted list of modifications, breaking changes declaration, testing confirmation, and any specific review guidance.
Docstring Coverage ⚠️ Warning Docstring coverage is 25.76% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: separate search boxes for Frozen and Preview sections' accurately summarizes the main architectural change of the PR—converting from a shared search box to independent search state per section.

✏️ 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 feat/search-box

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

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: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Editor/TextureCompressor/UI/Preview/PreviewSection.cs (1)

64-69: ⚠️ Potential issue | 🟡 Minor

Invalidate the search count cache here too.

GeneratePreview() already clears _searchBox's count cache on Lines 76-77, but InvalidateCache() only drops _previewData and _guidPathCache. If a search was active, _searchBox still holds the old preview collection reference, which keeps invalidated data alive longer than necessary and can leak stale counts into the next reuse of this section.

Suggested fix
 public void InvalidateCache()
 {
     _previewData = null;
     _previewSettingsHash = 0;
     _guidPathCache.Clear();
+    _searchBox.InvalidateCountCache();
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Editor/TextureCompressor/UI/Preview/PreviewSection.cs` around lines 64 - 69,
InvalidateCache currently nulls _previewData, resets _previewSettingsHash and
clears _guidPathCache but does not clear _searchBox's cached counts, leaving
stale preview references alive; update InvalidateCache to also perform the same
_searchBox count/cache clearing that GeneratePreview does (i.e. invoke the same
method or logic used in GeneratePreview to reset _searchBox's count cache so the
search box no longer holds references to the old preview collection).
🧹 Nitpick comments (1)
Editor/TextureCompressor/UI/Sections/FrozenTexturesSection.cs (1)

26-27: Recompute search state after drawing the search field.

isSearching/filteredCount are captured before _searchBox.Draw(...), so filtering and “no results/hidden count” can be one interaction behind while typing.

Suggested patch
-            bool isSearching = _searchBox.IsSearching;
-            int filteredCount = _searchBox.CountMatches(config.FrozenTextures, MatchesFrozenSearch);
+            bool isSearching = _searchBox.IsSearching;
+            int filteredCount = _searchBox.CountMatches(config.FrozenTextures, MatchesFrozenSearch);
@@
             // Search box for frozen textures
             _searchBox.Draw(filteredCount, frozenCount);
+            // SearchField updates text during Draw; refresh derived values for this frame.
+            isSearching = _searchBox.IsSearching;
+            filteredCount = _searchBox.CountMatches(config.FrozenTextures, MatchesFrozenSearch);

Also applies to: 50-57, 77-79, 90-93

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Editor/TextureCompressor/UI/Sections/FrozenTexturesSection.cs` around lines
26 - 27, The code captures _searchBox.IsSearching and
_searchBox.CountMatches(config.FrozenTextures, MatchesFrozenSearch) before
calling _searchBox.Draw(...), causing UI state (isSearching/filteredCount) to
lag; fix by moving the reads of _searchBox.IsSearching and the CountMatches call
to immediately after each corresponding _searchBox.Draw(...) invocation so the
search state is recomputed using the latest input; apply this change for the
occurrences around the blocks that use _searchBox.Draw, MatchesFrozenSearch and
the captured isSearching/filteredCount (the occurrences noted at the other
ranges as well).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Editor/Common/UI/Controls/SearchBoxControl.cs`:
- Around line 108-135: The CountMatches method's cache key omits the predicate,
so different predicates over the same collection can return a stale count;
modify the caching in CountMatches to include the predicate identity (e.g.,
store a reference to the passed Func<T,bool> in a new field like
_cachedPredicate or compute a stable predicate fingerprint and compare it along
with _cachedCountSearchText, _cachedCountUseFuzzy, _cachedSourceRef, and
_cachedSourceCount) and only reuse _cachedCount when the predicate matches;
update assignments to record the current predicate when recalculating; also add
a regression test exercising two different predicates over the same source to
ensure counts differ.
- Around line 57-97: The Draw method renders input and toggles (via
_searchField.OnGUI(SearchText) and the UseFuzzySearch toggle) but also displays
matchedCount/totalCount in the same IMGUI pass, causing a stale "Showing x of y"
because caller computes counts before Draw updates SearchText/UseFuzzySearch;
fix by removing the count label from Draw and exposing a separate API so callers
recompute after inputs are applied — either add a new method (e.g.,
DrawCounts(int matchedCount, int totalCount) or GetCountLabel()) or change Draw
to return a flag or out value indicating "inputs changed" so the caller can
recompute counts and then call the new DrawCounts to render up-to-date values;
reference Draw, SearchText, UseFuzzySearch, _searchField.OnGUI and ensure
callers use the new pattern to recalc counts after detecting changed == true.

In `@Editor/TextureCompressor/UI/Sections/FrozenTexturesSection.cs`:
- Around line 264-271: MatchesFrozenSearch currently uses an empty string when
assetPath resolution fails, so rows that display frozen.TextureGuid aren't
searchable; update the logic in MatchesFrozenSearch to use the GUID string as
the textureName fallback (e.g. frozen.TextureGuid.ToString()) when assetPath is
null/empty, and then call _searchBox.MatchesSearchAny(textureName, assetPath) so
the visible GUID text is included in search matching; locate this change in the
MatchesFrozenSearch method which references _guidPathCache.GetPath,
frozen.TextureGuid, and _searchBox.MatchesSearchAny.

---

Outside diff comments:
In `@Editor/TextureCompressor/UI/Preview/PreviewSection.cs`:
- Around line 64-69: InvalidateCache currently nulls _previewData, resets
_previewSettingsHash and clears _guidPathCache but does not clear _searchBox's
cached counts, leaving stale preview references alive; update InvalidateCache to
also perform the same _searchBox count/cache clearing that GeneratePreview does
(i.e. invoke the same method or logic used in GeneratePreview to reset
_searchBox's count cache so the search box no longer holds references to the old
preview collection).

---

Nitpick comments:
In `@Editor/TextureCompressor/UI/Sections/FrozenTexturesSection.cs`:
- Around line 26-27: The code captures _searchBox.IsSearching and
_searchBox.CountMatches(config.FrozenTextures, MatchesFrozenSearch) before
calling _searchBox.Draw(...), causing UI state (isSearching/filteredCount) to
lag; fix by moving the reads of _searchBox.IsSearching and the CountMatches call
to immediately after each corresponding _searchBox.Draw(...) invocation so the
search state is recomputed using the latest input; apply this change for the
occurrences around the blocks that use _searchBox.Draw, MatchesFrozenSearch and
the captured isSearching/filteredCount (the occurrences noted at the other
ranges as well).
🪄 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: d21771ab-0d98-45ea-81f3-a67480976fc6

📥 Commits

Reviewing files that changed from the base of the PR and between 2dcea66 and 440d559.

📒 Files selected for processing (9)
  • Editor/Common/UI/Controls/SearchBoxControl.cs
  • Editor/Common/UI/Styles/EditorStylesCache.cs
  • Editor/Common/UI/Utils/EditorDrawUtils.cs
  • Editor/Common/UI/Utils/GuidPathCache.cs
  • Editor/Common/UI/Utils/GuidPathCache.cs.meta
  • Editor/TextureCompressor/TextureCompressorEditor.cs
  • Editor/TextureCompressor/UI/Preview/PreviewSection.cs
  • Editor/TextureCompressor/UI/Sections/FrozenTexturesSection.cs
  • Tests/Editor/UI/SearchBoxControlTests.cs

Comment thread Editor/Common/UI/Controls/SearchBoxControl.cs
Comment thread Editor/Common/UI/Controls/SearchBoxControl.cs
Comment thread Editor/TextureCompressor/UI/Sections/FrozenTexturesSection.cs
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.

🧹 Nitpick comments (1)
Editor/TextureCompressor/UI/Preview/PreviewSection.cs (1)

226-230: Consider clearing _guidPathCache on close for consistency.

InvalidateCache() clears _guidPathCache, but closing the preview doesn't. While not a functional bug (the cache is cleared on next preview generation), clearing it here would be consistent and release memory sooner.

♻️ Suggested change
 if (GUILayout.Button("Close Preview"))
 {
     _showPreview = false;
     _previewData = null;
+    _guidPathCache.Clear();
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Editor/TextureCompressor/UI/Preview/PreviewSection.cs` around lines 226 -
230, When handling the "Close Preview" button (the GUILayout.Button("Close
Preview") branch where _showPreview is set to false and _previewData is set to
null), also clear the preview GUID cache to free memory and keep behavior
consistent with InvalidateCache(); either call InvalidateCache() there or
explicitly clear/reset _guidPathCache (e.g. _guidPathCache.Clear() or
_guidPathCache = null), ensuring you reference the same _guidPathCache symbol
used elsewhere.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@Editor/TextureCompressor/UI/Preview/PreviewSection.cs`:
- Around line 226-230: When handling the "Close Preview" button (the
GUILayout.Button("Close Preview") branch where _showPreview is set to false and
_previewData is set to null), also clear the preview GUID cache to free memory
and keep behavior consistent with InvalidateCache(); either call
InvalidateCache() there or explicitly clear/reset _guidPathCache (e.g.
_guidPathCache.Clear() or _guidPathCache = null), ensuring you reference the
same _guidPathCache symbol used elsewhere.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e75b3809-f8cb-47c9-857b-ad3dd028e681

📥 Commits

Reviewing files that changed from the base of the PR and between a749173 and c3ef7b7.

📒 Files selected for processing (3)
  • Editor/Common/UI/Styles/EditorStylesCache.cs
  • Editor/TextureCompressor/UI/Preview/PreviewSection.cs
  • Tests/Editor/UI/SearchBoxControlTests.cs
🚧 Files skipped from review as they are similar to previous changes (2)
  • Editor/Common/UI/Styles/EditorStylesCache.cs
  • Tests/Editor/UI/SearchBoxControlTests.cs

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: 1

🧹 Nitpick comments (1)
Tests/Editor/UI/GuidPathCacheTests.cs (1)

31-39: Make teardown deletion independent of successful import.

Cleanup currently deletes only when LoadAssetAtPath resolves an asset. If import fails, tracked files can be left behind. Prefer deleting by tracked path directly.

♻️ Suggested cleanup hardening
             foreach (var path in _createdAssetPaths)
             {
-                if (
-                    !string.IsNullOrEmpty(path)
-                    && AssetDatabase.LoadAssetAtPath<Object>(path) != null
-                )
+                if (!string.IsNullOrEmpty(path))
                 {
                     AssetDatabase.DeleteAsset(path);
                 }
             }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Tests/Editor/UI/GuidPathCacheTests.cs` around lines 31 - 39, Teardown
currently only deletes assets when AssetDatabase.LoadAssetAtPath<Object>(path)
returns non-null; change the cleanup to delete by tracked path regardless of
import success: iterate _createdAssetPaths, skip null/empty strings, and call
AssetDatabase.DeleteAsset(path) for each entry (optionally wrapping the call in
a try/catch or checking the boolean return to log failures). Update the foreach
in the teardown that references _createdAssetPaths and replace the conditional
that uses AssetDatabase.LoadAssetAtPath with a direct delete call to ensure
leftover files are removed even if import failed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Tests/Editor/UI/GuidPathCacheTests.cs`:
- Line 62: The single-line call assigning renameError should be reformatted to
the repository's multi-line call layout: replace the single-line
AssetDatabase.RenameAsset(originalPath, "GuidPathCacheTextureRenamed")
invocation with a multi-line invocation where AssetDatabase.RenameAsset is
called with each argument on its own line and the closing parenthesis on its own
line, preserving the assignment to the variable renameError (reference:
AssetDatabase.RenameAsset and renameError in GuidPathCacheTests).

---

Nitpick comments:
In `@Tests/Editor/UI/GuidPathCacheTests.cs`:
- Around line 31-39: Teardown currently only deletes assets when
AssetDatabase.LoadAssetAtPath<Object>(path) returns non-null; change the cleanup
to delete by tracked path regardless of import success: iterate
_createdAssetPaths, skip null/empty strings, and call
AssetDatabase.DeleteAsset(path) for each entry (optionally wrapping the call in
a try/catch or checking the boolean return to log failures). Update the foreach
in the teardown that references _createdAssetPaths and replace the conditional
that uses AssetDatabase.LoadAssetAtPath with a direct delete call to ensure
leftover files are removed even if import failed.
🪄 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: c7120672-acc1-4ca5-9132-fb1ed8697d83

📥 Commits

Reviewing files that changed from the base of the PR and between c3ef7b7 and 6eeb109.

📒 Files selected for processing (4)
  • Editor/Common/UI/Utils/GuidPathCache.cs
  • Tests/Editor/UI/GuidPathCacheTests.cs
  • Tests/Editor/UI/GuidPathCacheTests.cs.meta
  • Tests/Editor/UI/SearchBoxControlTests.cs
✅ Files skipped from review due to trivial changes (2)
  • Tests/Editor/UI/GuidPathCacheTests.cs.meta
  • Tests/Editor/UI/SearchBoxControlTests.cs
🚧 Files skipped from review as they are similar to previous changes (1)
  • Editor/Common/UI/Utils/GuidPathCache.cs

Comment thread Tests/Editor/UI/GuidPathCacheTests.cs Outdated
@Limitex Limitex merged commit 15b4605 into main Apr 18, 2026
5 checks passed
@Limitex Limitex deleted the feat/search-box branch April 18, 2026 03:19
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