-
Notifications
You must be signed in to change notification settings - Fork 0
tiling #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
This comment was marked as outdated.
This comment was marked as outdated.
WalkthroughThe updates introduce comprehensive enhancements to macOS window and workspace management. New methods enable batch window movement, workspace detection, and improved window enumeration using process IDs and the Accessibility API. The Changes
Sequence Diagram(s)sequenceDiagram
participant Manager as WindowManager
participant WS as MacOSWindowSystem
participant AM as AccessibilityManager
participant OS as macOS
Manager->>WS: apply_layout(layouts, windows)
WS->>AM: move_all_windows(layouts, windows)
AM->>OS: Enumerate all windows by PID
AM->>OS: Move each window via Accessibility API
AM-->>WS: Result (success/failure)
WS-->>Manager: Result (success/failure)
sequenceDiagram
participant Manager as WindowManager
participant WS as MacOSWindowSystem
participant OS as macOS
Manager->>WS: get_current_workspace()
WS->>OS: CGSGetActiveSpace()
OS-->>WS: workspace_id
WS-->>Manager: workspace_id
sequenceDiagram
participant Observer as WindowDragNotificationObserver
participant macOS as Cocoa NotificationCenter
participant EventLoop as Tokio mpsc::Sender
macOS->>Observer: NSWindowWillMoveNotification
Observer->>EventLoop: Send DragStarted event
macOS->>Observer: NSWindowDidMoveNotification
Observer->>EventLoop: Send DragEnded event
Poem
✨ Finishing Touches
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
🧹 Nitpick comments (5)
src/macos/cgwindow.rs (1)
111-114: Consider using -1 as the default PID value instead of 0.Since 0 is a valid PID on Unix systems (representing the kernel), using it as a default for "unknown" could be misleading. Consider using -1 to clearly indicate an unknown or invalid PID.
// Extract owner PID let owner_pid = Self::get_number_from_dict(dict, "kCGWindowOwnerPID") - .unwrap_or(0.0) as i32; + .unwrap_or(-1.0) as i32;src/macos/window_system.rs (1)
125-125: Consider the performance impact of the reduced monitoring interval.Reducing the interval from 500ms to 200ms means window enumeration happens 5 times per second instead of 2. Since
CGWindowListCopyWindowInfois a relatively expensive operation, this could impact:
- CPU usage
- Battery life on laptops
- Overall system responsiveness with many windows
Consider making this interval configurable or implementing a more efficient change detection mechanism.
src/window_manager.rs (1)
406-408: Redundant workspace fetch.The current workspace is already updated in
refresh_windows()which is called beforeapply_layout(). This additional fetch seems redundant.-// Get current macOS workspace/Space -let current_workspace = self.macos.get_current_workspace().await?; +// Use the workspace already updated in refresh_windows +let current_workspace = self.current_workspace;src/macos/accessibility.rs (2)
400-411: Consider removing or deprecating redundant methods.These methods are marked as redundant/legacy but remain in the codebase:
try_get_windows_from_other_apps- explicitly says it's redundantget_windows_for_app_by_name- marked as legacy, returns empty VecConsider either:
- Removing these methods entirely if no other code depends on them
- Adding
#[deprecated]attributes with migration guidance- Adding clear documentation about their obsolete status
This will help prevent confusion for future maintainers.
545-603: Code duplication between move_window and move_window_element.This method duplicates the AXValue creation and positioning logic from the
move_windowmethod (lines 175-225). While the detailed error handling and logging are excellent, this creates maintenance overhead.Consider refactoring both methods to use a shared helper function for the AXValue creation and attribute setting:
fn set_window_position_and_size(&self, element: AXUIElementRef, rect: Rect) -> Result<()> { // Common AXValue creation and setting logic }This would eliminate duplication while maintaining the current functionality.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/macos/accessibility.rs(4 hunks)src/macos/cgwindow.rs(2 hunks)src/macos/window_system.rs(4 hunks)src/window_manager.rs(6 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/macos/window_system.rs (3)
src/macos/cgwindow.rs (2)
new(305-311)get_all_windows(65-97)src/macos/accessibility.rs (5)
new(79-98)move_all_windows(276-326)std(374-374)std(381-381)get_focused_window(100-145)src/window_manager.rs (1)
new(74-99)
src/window_manager.rs (1)
src/layout.rs (1)
get_current_layout(420-422)
🔇 Additional comments (21)
src/macos/cgwindow.rs (11)
129-132: LGTM!The workspace ID extraction is implemented correctly with a reasonable default value of 1.
136-138: Good debug logging additions!The enhanced debug messages provide valuable context for understanding window filtering decisions.
Also applies to: 143-145
148-153: LGTM! Good additions to the system apps exclusion list.Including "Menubar", "Menu Bar", and "SystemPreferences" ensures these system UI elements won't be incorrectly tiled.
159-163: Good improvement to system panel filtering logic!The more specific condition (empty title AND system-related owner) reduces false positives while still filtering out system panels effectively.
111-113: LGTM! Owner PID extraction implemented correctly.The extraction of owner process ID from the window dictionary follows the established pattern and provides valuable metadata for window management operations.
129-131: LGTM! Workspace ID detection properly implemented.The workspace ID extraction from the window dictionary replaces the previous hardcoded default and enables proper workspace-aware window management. The fallback to workspace 1 is reasonable for cases where the workspace information is unavailable.
136-137: Enhanced debug logging improves troubleshooting capabilities.The added debug logging for filtered windows provides valuable insights into why specific windows are excluded from management, which will help with debugging and fine-tuning the filtering logic.
Also applies to: 143-144
149-153: Expanded system application filtering improves tiling behavior.The addition of "Menubar", "Menu Bar", and "SystemPreferences" to the never-tile applications list is a good enhancement that prevents inappropriate tiling attempts on system UI elements.
160-162: Refined system window filtering with improved logging.The updated filtering logic for system panels is more precise, checking for empty titles combined with system-related owners or very short owner names. The debug logging helps track which windows are being filtered out.
165-165: Comprehensive success logging aids in debugging.The debug log for successfully parsed windows provides visibility into the window detection process, including the newly captured workspace information.
171-171: Window struct properly updated with new metadata fields.The Window struct construction correctly incorporates the newly extracted
owner_pidandworkspace_idfields, enabling enhanced window management capabilities throughout the application.Also applies to: 175-175
src/macos/window_system.rs (2)
133-137: Good debug logging additions!The enhanced logging provides valuable insights into window discovery and workspace tracking.
Also applies to: 158-158
11-15:❓ Verification inconclusive
⚠️ Using undocumented private macOS APIs - verify compatibility and add error handling.The
CGSGetActiveSpaceandCGSMainConnectionIDfunctions are private CoreGraphics APIs that are not officially documented or supported by Apple. This raises several concerns:
- These APIs could change or be removed in future macOS versions
- The function signatures might not be correct
- There's no verification that these symbols exist at runtime
Consider adding runtime symbol verification or implementing a fallback mechanism if these functions are not available.
Run the following script to check if these symbols are exported in the CoreGraphics framework:
🏁 Script executed:
#!/bin/bash # Check if the CGS functions are exported in CoreGraphics framework nm -g /System/Library/Frameworks/CoreGraphics.framework/CoreGraphics | grep -E "CGSGetActiveSpace|CGSMainConnectionID" || echo "Symbols not found in CoreGraphics framework" # Alternative: Check in SkyLight framework where these might actually be located if [ -f /System/Library/PrivateFrameworks/SkyLight.framework/SkyLight ]; then echo "Checking SkyLight framework..." nm -g /System/Library/PrivateFrameworks/SkyLight.framework/SkyLight | grep -E "CGSGetActiveSpace|CGSMainConnectionID" || echo "Symbols not found in SkyLight framework" fiLength of output: 385
⚠️ Private macOS APIs: verify existence and add runtime checksThe
extern "C"declarations forCGSGetActiveSpaceandCGSMainConnectionIDreference undocumented CoreGraphics/SkyLight functions that Apple can change or remove at any time. This may lead to signature mismatches or missing symbols at runtime, causing crashes.We attempted to locate these symbols using
nmin the sandbox but couldn’t (nonmavailable). Please verify on a macOS host:# Preferred: use otool to inspect exported symbols if [ -f "/System/Library/PrivateFrameworks/SkyLight.framework/SkyLight" ]; then otool -ov /System/Library/PrivateFrameworks/SkyLight.framework/SkyLight \ | grep -E "CGSGetActiveSpace|CGSMainConnectionID" \ || echo "Symbols not found in SkyLight" else echo "SkyLight framework not found" fi # Fallback: use strings + grep strings /System/Library/PrivateFrameworks/SkyLight.framework/SkyLight \ | grep -E "CGSGetActiveSpace|CGSMainConnectionID" \ || echo "Symbols not found"Action items:
- Confirm these symbols exist in the targeted macOS version(s).
- Load them at runtime (e.g., via
libloading) and wrap inOption<unsafe extern "C" fn…>so you can fall back gracefully if they’re missing.- Document compatibility and any fallbacks for unsupported macOS releases.
Location:
src/macos/window_system.rs(lines 11–15)src/window_manager.rs (5)
18-18: LGTM!The
owner_pidfield addition is consistent with the window metadata extraction incgwindow.rs.
109-114: Good addition for initial window layout!Applying layout to existing windows on startup ensures a consistent state when the window manager starts.
373-385: Good error handling for workspace detection!The implementation gracefully handles workspace detection failures by logging a warning and continuing with the last known workspace.
409-415: Address the unreliable workspace detection issue.The comment indicates workspace filtering is disabled due to unreliable detection. This is a significant limitation that should be addressed. Consider:
- Investigating why workspace detection is unreliable
- Implementing alternative workspace detection methods
- Adding a configuration option to enable/disable workspace filtering
436-476: Excellent implementation of batch window movement with fallback!The implementation:
- Attempts efficient batch movement first
- Falls back to individual moves with retries on failure
- Provides detailed logging for debugging
- Updates window state consistently
The retry logic with 3 attempts and 50ms delays is well-designed for handling transient failures.
src/macos/accessibility.rs (3)
2-57: LGTM! Well-structured FFI declarations.The new imports and external C function declarations are properly structured for the enhanced window management functionality. The CGPoint and CGSize structs correctly match the Core Foundation ABI.
175-225: Enhanced window movement implementation looks good.The rewritten method properly uses AXValue creation for direct window manipulation. Memory management is handled correctly with CFRelease calls for the created values.
However, verify that
find_window_elementcan reliably locate the correct window element, as the current implementation appears simplified.
364-469: Excellent process enumeration implementation.The PID enumeration with dynamic buffer resizing is well-handled:
- Proper loop with buffer doubling when needed
- Correct filtering of invalid PIDs (≤ 0)
- Good error handling for system call failures
The window enumeration by PID and tileability checking are also well-implemented with proper memory management.
There was a problem hiding this 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
♻️ Duplicate comments (2)
src/window_manager.rs (1)
115-118: Refresh interval rationale is well-documented.The comment clearly explains why the refresh interval differs from the window monitoring interval, addressing the previous concern about inconsistency.
src/macos/accessibility.rs (1)
267-291:⚠️ Potential issueWindow-to-layout mapping issue persists.
The current implementation maps windows to layouts by index, which assumes the order of window elements matches the order of windows in the provided array. This can lead to incorrect window positioning.
for window in windows { if element_index < all_window_elements.len() { element_to_window.insert(all_window_elements[element_index], window.id); element_index += 1; } }A more reliable approach would be to match windows based on properties like title or position rather than relying on order.
🧹 Nitpick comments (2)
src/window_manager.rs (1)
419-422: Consider tracking workspace filtering as a future enhancement.The decision to disable workspace filtering due to unreliable detection is pragmatic. However, this might lead to unexpected behavior when users have windows on multiple workspaces.
Would you like me to create an issue to track implementing reliable workspace detection as a future enhancement?
src/macos/accessibility.rs (1)
441-457: Consider adding a removal timeline for deprecated methods.The deprecated methods are properly marked but lack a removal timeline. Consider adding a comment indicating when these will be removed (e.g., "Will be removed in v2.0").
- #[deprecated(note = "Use enumerate_all_accessible_applications instead")] + #[deprecated(since = "1.0.0", note = "Use enumerate_all_accessible_applications instead. Will be removed in v2.0")] fn try_get_windows_from_other_apps(
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
src/hotkeys.rs(7 hunks)src/layout.rs(1 hunks)src/macos/accessibility.rs(5 hunks)src/macos/cgwindow.rs(7 hunks)src/macos/window_system.rs(4 hunks)src/window_manager.rs(10 hunks)
✅ Files skipped from review due to trivial changes (2)
- src/layout.rs
- src/hotkeys.rs
🚧 Files skipped from review as they are similar to previous changes (2)
- src/macos/window_system.rs
- src/macos/cgwindow.rs
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/window_manager.rs (2)
src/layout.rs (1)
get_current_layout(420-422)src/focus.rs (2)
get_focused_window_id(312-317)find_window_in_direction(205-268)
🔇 Additional comments (8)
src/window_manager.rs (4)
18-18: LGTM! Good addition for PID-based window management.The
owner_pidfield enables better window identification and correlation with the Accessibility API's PID-based enumeration.
109-114: LGTM! Excellent startup initialization.Applying the initial layout on startup ensures existing windows are properly tiled immediately, improving the user experience.
377-392: LGTM! Robust workspace detection with proper error handling.The implementation gracefully handles workspace detection failures with warnings while continuing operation, preventing crashes in environments where workspace detection might be unreliable.
450-513: LGTM! Well-designed batch window movement with robust fallback.The implementation provides optimal performance with batch operations while ensuring reliability through:
- Fallback to individual window moves on batch failure
- Retry logic (3 attempts) for transient failures
- Proper state synchronization after successful moves
- Comprehensive debug logging for troubleshooting
src/macos/accessibility.rs (4)
181-203: LGTM! Proper window movement with resource management.The rewritten method correctly:
- Uses
find_window_elementto locate windows by ID- Properly releases the retained element after use
- Provides clear debug logging for troubleshooting
205-228: LGTM! Properly implements window finding by ID.This implementation correctly addresses the previous concern by:
- Actually using the
window_idparameter to find specific windows- Checking cache and refreshing as needed
- Properly retaining elements before returning them
This fixes the critical issue where the method previously ignored the window_id parameter.
718-736: LGTM! Proper memory management for cached window elements.The implementation correctly:
- Retains window elements before caching (line 727)
- Releases them during cache refresh and in the Drop implementation
- Uses a stable window ID based on PID and index
This properly addresses the previous use-after-free concern.
492-503: LGTM! Correct element retention preventing dangling pointers.The code properly retains window elements (line 498) before they're returned, ensuring they remain valid after the windows array is released. This prevents the use-after-free issue identified in previous reviews.
There was a problem hiding this 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
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
src/macos/accessibility.rs(5 hunks)src/macos/window_system.rs(4 hunks)src/window_manager.rs(10 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/macos/window_system.rs
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/window_manager.rs (2)
src/layout.rs (1)
get_current_layout(420-422)src/focus.rs (2)
get_focused_window_id(312-317)find_window_in_direction(205-268)
src/macos/accessibility.rs (4)
src/macos/cgwindow.rs (4)
CFRelease(27-27)new(319-325)CFArrayGetCount(25-25)CFArrayGetValueAtIndex(26-26)src/macos/window_system.rs (3)
move_window(300-302)move_all_windows(304-310)new(32-41)src/ipc.rs (3)
move_window(343-355)new(31-36)new(290-292)src/lib.rs (1)
new(27-34)
🔇 Additional comments (13)
src/window_manager.rs (5)
18-18: LGTM: Well-integrated owner_pid field addition.The addition of the
owner_pidfield to the Window struct properly aligns with the accessibility manager's PID-based window identification strategy implemented insrc/macos/accessibility.rs. This enhancement supports the improved window enumeration and batch operations.
109-113: Excellent startup enhancement for consistent window state.Applying the initial layout to existing windows on startup ensures that the window manager maintains a consistent state from the beginning, rather than leaving pre-existing windows unmanaged until the next event.
115-121: Well-documented timer interval adjustment addresses past concerns.The detailed comments explaining the rationale for different refresh intervals (1000ms for refresh vs 200ms for monitoring) and the TODO for making them configurable demonstrate good engineering practices. This addresses the inconsistency noted in past reviews.
380-394: Robust workspace detection with proper error handling.The workspace detection logic properly handles both success and failure cases, logging workspace changes and gracefully handling errors without breaking the refresh flow.
453-515: Excellent batch operation implementation with robust fallback strategy.The implementation properly:
- Attempts batch window movement first for efficiency
- Falls back to individual moves with retry logic (up to 3 attempts)
- Updates internal window state in both success and failure cases
- Provides comprehensive logging for debugging
The retry mechanism with 50ms delays and the proper state synchronization demonstrate good error handling practices.
src/macos/accessibility.rs (8)
139-145: Significant improvement in window ID generation.The new hashing approach using multiple hash operations with different constants (0x9e3779b9, 0x85ebca6b) and combining PID with element pointer provides much better distribution and collision resistance compared to the previous simple pointer casting.
184-206: Excellent rewrite of move_window with proper accessibility API usage.The new implementation properly:
- Searches for the window element using
find_window_element- Uses the dedicated
set_window_recthelper for consistent positioning- Manages Core Foundation references with proper CFRelease
- Provides clear error messaging when windows cannot be found
233-303: Well-designed batch window movement with precise window matching.The implementation addresses the previous concern about window-to-layout mapping by:
- Building a HashMap for O(1) layout lookups by WindowId
- Grouping windows by PID for efficient enumeration
- Matching windows by PID and index within the PID group
- Properly managing Core Foundation references throughout the process
This ensures accurate window-to-layout correspondence.
305-360: Robust window positioning using proper AXValue creation.The
set_window_rectmethod properly:
- Creates AXValue objects for position and size using the correct Core Foundation types
- Handles null value creation failures gracefully
- Cleans up Core Foundation resources in all code paths
- Provides detailed error information for debugging
407-441: Comprehensive process enumeration with proper buffer management.The
get_all_running_pidsimplementation correctly:
- Uses dynamic buffer sizing to handle varying numbers of processes
- Properly handles the
proc_listpidsreturn value semantics- Filters out invalid PIDs (≤ 0)
- Implements a growth strategy for insufficient buffer sizes
731-752: Enhanced window ID generation with collision detection.The improved window ID generation now:
- Incorporates PID, array index, and element pointer for better uniqueness
- Detects and warns about ID collisions
- Implements a timestamp-based fallback for collision resolution
This addresses the stability concerns raised in past reviews.
659-667: Critical improvement: Proper Core Foundation resource management.The cache refresh now properly releases all cached elements before clearing the cache, preventing resource leaks. This addresses the memory management concerns identified in previous reviews.
728-730:✅ Verification successful
Verify CFRetain usage aligns with Core Foundation memory management.
The code retains window elements from
CFArrayGetValueAtIndexbefore caching them, which is correct since the array will be released. However, ensure this doesn't create over-retention when combined with the existing cache cleanup logic.Let me verify the Core Foundation reference counting patterns are consistent:
🏁 Script executed:
#!/bin/bash # Description: Check CFRetain/CFRelease patterns in accessibility.rs echo "=== CFRetain calls ===" rg "CFRetain" src/macos/accessibility.rs -A 2 -B 2 echo "=== CFRelease calls ===" rg "CFRelease" src/macos/accessibility.rs -A 2 -B 2 echo "=== Window cache insertions ===" rg "window_cache\.insert" src/macos/accessibility.rs -A 2 -B 2Length of output: 4478
No over-retention: CFRetain/CFRelease usage is balanced
I’ve verified that every call to CFRetain is paired with a corresponding CFRelease:
- In
process_windows_array, elements retained for tiling are released in the loop overapp_window_elements.- Cache updates release old entries before inserting new ones, and the Drop implementation releases all retained window elements (and
system_element) when the manager is torn down.No further changes needed here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
♻️ Duplicate comments (2)
src/window_manager.rs (2)
123-129: Good documentation of the interval differences.The comment clearly explains the rationale for different refresh intervals and provides a clear TODO for making them configurable. This appropriately addresses the concern about inconsistent intervals.
437-440: Workspace filtering is still disabled.This issue about disabled workspace filtering due to unreliable detection was previously raised and could lead to unexpected behavior where windows from inactive workspaces get repositioned.
🧹 Nitpick comments (4)
src/window_manager.rs (3)
87-90: Consider making the snap threshold configurable.The snap threshold is hardcoded to 50.0 pixels. Consider adding it to the configuration to allow users to customize snap sensitivity.
- let snap_manager = SnapManager::new(screen_rect, 50.0); // 50px snap threshold + let snap_threshold = config.general.snap_threshold.unwrap_or(50.0); + let snap_manager = SnapManager::new(screen_rect, snap_threshold);And add to your config structure:
pub struct GeneralConfig { // ... existing fields ... pub snap_threshold: Option<f64>, }
536-567: Well-implemented snap drag detection.The movement threshold (20px) and delayed check (800ms) provide good UX. However, consider handling the case where multiple rapid movements occur.
Consider canceling previous scheduled checks when a new drag starts to avoid queuing multiple snap checks:
// Add to WindowDragState: drag_check_handle: Option<tokio::task::JoinHandle<()>>, // In handle_window_move_snap, cancel previous check: if let Some(handle) = previous_drag_state.drag_check_handle.take() { handle.abort(); }
605-620: Placeholder method for future enhancement.This method is clearly marked as a placeholder. When implementing, consider tracking manual positions separately from layout-managed positions to preserve user adjustments.
Would you like me to help design the logic for updating the BSP tree based on manual window movements?
src/layout.rs (1)
289-356: Well-implemented BSP tree synchronization!The rewritten
compute_bsp_layoutproperly maintains consistency between the BSP tree and the current window set. The logic handles all cases: removing stale windows, adding new ones, and recreating the tree when needed.Consider renaming
collect_all_windows_statictocollect_all_window_idsfor clarity:- fn collect_all_windows_static(node: &BSPNode, windows: &mut Vec<WindowId>) { + fn collect_all_window_ids(node: &BSPNode, window_ids: &mut Vec<WindowId>) { if let Some(window_id) = node.window_id { - windows.push(window_id); + window_ids.push(window_id); } if let Some(ref left) = node.left { - Self::collect_all_windows_static(left, windows); + Self::collect_all_window_ids(left, window_ids); } if let Some(ref right) = node.right { - Self::collect_all_windows_static(right, windows); + Self::collect_all_window_ids(right, window_ids); } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
src/layout.rs(5 hunks)src/lib.rs(1 hunks)src/macos/accessibility.rs(5 hunks)src/snap.rs(1 hunks)src/window_manager.rs(15 hunks)
✅ Files skipped from review due to trivial changes (1)
- src/lib.rs
🔇 Additional comments (16)
src/snap.rs (4)
1-32: LGTM!The
SnapRegionenum and its implementation are well-structured for representing the nine standard snap zones.
34-54: LGTM!The struct definitions are well-designed with clear separation of concerns between detection bounds and snap target rectangles.
275-329: LGTM!The drag tracking implementation uses conservative thresholds (500ms duration, 100px distance) which helps prevent accidental snapping. The double-check for position changes is a good safeguard.
331-393: LGTM!The snap target finding logic correctly prioritizes corner zones over edges and center, which provides intuitive snapping behavior. The utility methods are well-implemented.
src/window_manager.rs (1)
468-531: Excellent implementation of batch window movement with fallback.The implementation properly attempts batch movement first and falls back to individual moves with retry logic. The error handling and logging are comprehensive.
src/layout.rs (2)
80-204: Excellent BSP tree enhancements!The implementation includes:
- Proper recursive rectangle updates maintaining tree consistency
- Clever spiral layout using alternating split directions by depth
- Correct tree collapsing logic when removing windows
- Efficient window containment checking
The alternating split pattern creates an intuitive i3/sway-like layout behavior.
246-268: LGTM!The window management methods properly handle BSP tree lifecycle, including creation when needed and cleanup when empty.
src/macos/accessibility.rs (9)
139-145: Improved window ID generation addresses previous concerns.The new hash-based approach using FNV-like multiplication and combining PID with element pointer is a significant improvement over the previous raw pointer casting. This reduces collision probability while maintaining reasonable performance.
However, as noted in previous reviews, this still has inherent limitations with pointer reuse and AX element lifecycle. Consider the web search recommendation about using
_AXUIElementGetWindowfor more stable CGWindowID extraction when feasible.
184-208: Well-structured window movement with proper error handling.The refactored
move_windowmethod properly separates concerns by delegating tofind_window_elementandset_window_rect. The graceful handling of missing windows with debug logging instead of hard failures is appropriate for window management scenarios.
210-233: Proper resource management in window element lookup.The
find_window_elementmethod correctly handles CFRetain/CFRelease for returned elements and implements a reasonable fallback strategy with cache refresh. This addresses the previous review concern about ignoring the window_id parameter.
235-305: Batch window movement logic addresses previous mapping concerns.The new implementation correctly maintains window ID to layout mapping using a HashMap lookup rather than sequential application. The PID-based grouping and index-within-PID matching strategy is more robust than the previous approach.
The resource cleanup at lines 292-296 properly releases retained window elements to prevent leaks.
307-362: Robust window positioning with proper AXValue usage.The
set_window_rectmethod correctly uses AXValue creation for Core Graphics types and implements proper resource cleanup. The error handling captures both position and size operation results.
463-524: Comprehensive window enumeration with proper filtering.The
get_windows_for_app_by_pidmethod implements good practices:
- Early return for invalid PIDs
- Proper resource management with CFRetain for elements
- Window tileability validation
- Comprehensive cleanup
526-556: Window tileability check ensures movement compatibility.The
is_window_tileablemethod properly validates that windows have the required position and size attributes before attempting to manage them. The resource cleanup for temporary values is handled correctly.
762-773: Drop implementation properly releases cached resources.The Drop implementation correctly iterates through cached window elements and releases them, preventing resource leaks when the AccessibilityManager is destroyed.
489-489:❓ Verification inconclusive
Verify Core Foundation array API usage.
The code uses
core_foundation::array::CFArrayRefand related functions, but I need to verify this is the correct way to access these APIs in the core_foundation crate.Also applies to: 724-724
🏁 Script executed:
#!/bin/bash # Check if the core_foundation crate exports array functions as used rg -A 3 "CFArrayRef\|CFArrayGetCount\|CFArrayGetValueAtIndex" --type rustLength of output: 75
Verify Core Foundation array API usage
Please confirm that the
core_foundationcrate you depend on exposes the raw CFArray APIs you’re casting to—namelyCFArrayRef,CFArrayGetCount, andCFArrayGetValueAtIndex. A codebase search won’t surface definitions for external crates, so double-check against the crate’s documentation or your localcore-foundationsource.Locations to verify:
- src/macos/accessibility.rs: line 489
- src/macos/accessibility.rs: line 724
There was a problem hiding this 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)
src/layout.rs (1)
319-387: Complex synchronization logic needs careful review.The enhanced
compute_bsp_layoutmethod includes sophisticated tree synchronization, but there are potential performance and correctness concerns:
Performance: The current approach collects all windows, then removes missing ones, then adds new ones. This could be inefficient for large window sets.
Edge case: If
collect_all_windows_staticis called on an empty tree, it might not handle all edge cases correctly.Consider optimizing the synchronization approach:
- // Sync the BSP tree with current windows - if let Some(ref mut root) = self.bsp_root { - // Remove windows that are no longer present - let mut to_remove = Vec::new(); - Self::collect_all_windows_static(root, &mut to_remove); - for window_id in to_remove { - if !window_ids.contains(&window_id) { - root.remove_window(window_id); - } - } + // More efficient: directly compare and sync + if let Some(ref mut root) = self.bsp_root { + // Remove windows that are no longer present + let mut current_windows = Vec::new(); + Self::collect_all_windows_static(root, &mut current_windows); + for &window_id in ¤t_windows { + if !window_ids.contains(&window_id) { + root.remove_window(window_id); + } + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (13)
src/bin/skew-cli.rs(0 hunks)src/config.rs(1 hunks)src/focus.rs(1 hunks)src/hotkeys.rs(8 hunks)src/ipc.rs(2 hunks)src/layout.rs(8 hunks)src/macos/accessibility.rs(9 hunks)src/macos/cgwindow.rs(8 hunks)src/macos/window_system.rs(7 hunks)src/main.rs(1 hunks)src/plugins.rs(1 hunks)src/snap.rs(1 hunks)src/window_manager.rs(15 hunks)
💤 Files with no reviewable changes (1)
- src/bin/skew-cli.rs
✅ Files skipped from review due to trivial changes (4)
- src/main.rs
- src/plugins.rs
- src/config.rs
- src/ipc.rs
🚧 Files skipped from review as they are similar to previous changes (4)
- src/hotkeys.rs
- src/macos/cgwindow.rs
- src/macos/window_system.rs
- src/window_manager.rs
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/snap.rs (2)
src/lib.rs (1)
new(28-35)src/window_manager.rs (1)
new(77-107)
🔇 Additional comments (15)
src/focus.rs (1)
341-344: Clean refactoring of the backward cycling logic!The simplified approach with explicit edge case handling is more readable and maintainable than modulo arithmetic.
src/macos/accessibility.rs (3)
177-201: Excellent refactoring of the move_window method!The implementation properly manages element lifecycles with retain/release and gracefully handles missing windows without failing the operation.
228-298: Well-designed batch window movement with proper window-to-layout mapping!The implementation correctly addresses the window matching concern by:
- Grouping windows by PID
- Matching window elements to layout rectangles using PID-specific indexing
- Properly managing element lifecycles with retain/release
This approach ensures each window gets its intended layout position.
300-357: Robust implementation of window rect setting!The method properly creates and manages AXValue objects with correct cleanup on all paths, including error cases.
src/snap.rs (1)
295-332: Excellent use of conservative thresholds for snap detection!The implementation prevents unintended snapping with:
- 500ms minimum drag duration
- 100px minimum drag distance
- Additional 10px threshold to avoid redundant moves
This ensures snapping only occurs for clearly intentional window movements.
src/layout.rs (10)
5-5: Good addition for debugging support.Adding the
Debugderive trait will improve debugging capabilities for theLayoutTypeenum.
80-115: Solid rectangle update implementation.The
update_rectandupdate_child_rectsmethods correctly handle rectangle calculations for BSP tree nodes. The logic properly splits rectangles horizontally and vertically based on the split ratio.
159-171: Efficient window counting implementation.The recursive window counting logic is correct and handles both leaf and container nodes appropriately.
221-233: Clean window containment check.The
contains_windowmethod correctly traverses the tree to check for window existence.
276-298: Well-structured window management methods.The
add_windowandremove_windowmethods forLayoutManagerproperly coordinate with the BSP tree, including cleanup when the tree becomes empty.
443-443: Correct float layout implementation.Using
w.rectdirectly for floating windows is the correct approach, as floating windows should maintain their current positions.
460-460: Good use of div_ceil for grid calculations.The
div_ceilmethod provides cleaner ceiling division logic for grid row calculations.
605-605: Excellent bounds checking for split ratio.Using
clamp(0.1, 0.9)ensures the split ratio stays within reasonable bounds, preventing edge cases where windows become too small.
117-157:❌ Incorrect review comment
Review the spiral layout insertion logic.
The insertion logic implements alternating splits based on depth, but there may be a conceptual issue with the spiral pattern.
let should_split_horizontal = depth % 2 == 0;This creates alternating splits (vertical, then horizontal), but the comment mentions "new windows go right, then down" which suggests a different pattern. Consider verifying that this depth-based alternating approach produces the intended spiral effect.
🏁 Script executed:
#!/bin/bash # Description: Check if there are any tests or documentation for spiral layout behavior # to verify the intended insertion pattern rg -A 10 -B 5 "spiral.*layout|spiral.*pattern" --type rustLength of output: 3572
🏁 Script executed:
#!/bin/bash # Extract the full implementation of compute_spiral_layout for review rg -A200 "fn compute_spiral_layout" --type rustLength of output: 7346
Ignore insertion logic for Spiral layout — it isn’t used by
compute_spiral_layout.The
insert_window_with_depthroutine builds the BSP tree for BSP layouts only. The Spiral layout’scompute_spiral_layoutfunction ignores that tree entirely, laying out windows directly from thewindowsslice andsplit_ratio. The depth-based split logic has no effect on Spiral, so this review comment can be dismissed.Likely an incorrect or invalid review comment.
173-219:❓ Verification inconclusive
Verify the newer Rust syntax compatibility.
The tree collapsing logic uses some newer Rust methods that may require verification:
.is_some_and(|left| left.remove_window(window_id)) .is_none_or(|n| n.count_windows() == 0)These methods were introduced in recent Rust versions. Ensure the project's MSRV (Minimum Supported Rust Version) supports these methods.
🏁 Script executed:
#!/bin/bash # Description: Check the project's Rust version requirements and verify method availability # Check Cargo.toml for rust-version or edition fd Cargo.toml --exec cat {} \; | grep -E "rust-version|edition" # Check if is_some_and and is_none_or are available in the target Rust version # is_some_and was stabilized in Rust 1.70.0 # is_none_or was stabilized in Rust 1.82.0 echo "Note: is_some_and requires Rust 1.70.0+, is_none_or requires Rust 1.82.0+"Length of output: 242
Verify the project’s MSRV before using
is_some_andandis_none_orThe code on src/layout.rs lines 173–219 uses:
.is_some_and(|left| left.remove_window(window_id)) // stabilised in Rust 1.70.0 .is_none_or(|n| n.count_windows() == 0) // stabilised in Rust 1.82.0Cargo.toml currently only declares:
edition = "2021"Please ensure the Minimum Supported Rust Version (MSRV) is at least 1.82.0. You can either:
- Add a
rust-version = "1.82.0"field to Cargo.toml- Or, if you must support older toolchains, replace these with equivalent expressions, e.g.:
.as_mut().map_or(false, |left| left.remove_window(window_id)) .as_ref().map_or(true, |n| n.count_windows() == 0)
There was a problem hiding this 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
♻️ Duplicate comments (6)
src/snap.rs (2)
52-53: The snap_threshold parameter remains unused despite past feedback.The
snap_thresholdfield is stored but never used in the snapping logic, which still uses hardcoded values (100ms, 20px) inend_window_drag. This misleads users who pass this parameter expecting it to control snapping behavior.
83-283: The update_snap_zones method is still overly verbose and repetitive.This method contains extensive code duplication for creating different snap zones. The repetitive patterns for bounds and snap rectangle creation make the code harder to maintain and modify.
src/macos/accessibility.rs (4)
2-2: Missing Core Foundation Array imports cause compilation errors.The code uses
core_foundation::array::CFArrayRefand related functions throughout but doesn't import the array module at the top of the file.
132-142: Improved window ID generation but still has collision risks.The enhanced hash function with FNV-like prime multiplication reduces collisions compared to raw pointer casting, but still suffers from pointer address reuse and AX element instability across API calls.
422-457: Process enumeration still has infinite loop risk.The
get_all_running_pidsmethod can potentially loop infinitely ifproc_listpidsconsistently returns a value equal to the buffer size.
958-988: Good collision detection but fallback ID still has collision risk.The collision detection is excellent defensive programming. However, the fallback ID generation using only timestamp and index could still cause collisions.
🧹 Nitpick comments (1)
src/snap.rs (1)
59-60: Unused fields with dead_code allowances suggest incomplete implementation.The
WindowDragStatestruct has multiple fields marked with#[allow(dead_code)], indicating either incomplete functionality or unnecessary data storage.Consider removing unused fields or implementing the missing functionality that would use them. If they're for future features, add TODO comments explaining their intended purpose.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
src/macos/accessibility.rs(9 hunks)src/macos/cgwindow.rs(8 hunks)src/macos/window_system.rs(7 hunks)src/main.rs(2 hunks)src/snap.rs(1 hunks)src/window_manager.rs(17 hunks)
✅ Files skipped from review due to trivial changes (1)
- src/main.rs
🚧 Files skipped from review as they are similar to previous changes (2)
- src/macos/cgwindow.rs
- src/macos/window_system.rs
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/snap.rs (2)
src/window_manager.rs (1)
new(80-111)src/lib.rs (1)
new(28-35)
🔇 Additional comments (5)
src/window_manager.rs (4)
19-19: LGTM: Well-integrated owner_pid field.The addition of
owner_pidto the Window struct aligns perfectly with the enhanced window identification in the macOS accessibility layer and supports more accurate window management.
69-69: LGTM: Smart defensive programming with programmatic move tracking.The
snap_managerintegration andprogrammatically_movingset provide an elegant solution to prevent recursive snap handling when the manager itself moves windows.Also applies to: 75-76
488-491: Good defensive programming: Marking all windows before batch move.The pattern of marking all windows as
programmatically_movingbefore the batch operation ensures consistent snap conflict prevention even if the batch operation partially fails.
496-556: Robust fallback strategy for batch window movements.The implementation properly handles batch move failures by falling back to individual moves with retry logic. The detailed logging helps with debugging layout application issues.
src/macos/accessibility.rs (1)
244-314: Improved batch window movement with proper PID-based matching.The new implementation correctly matches windows by PID and index rather than global ordering, addressing the previous window-to-layout mapping issue. The resource management with
CFRetain/CFReleaseis also properly handled.
|
@coderabbitai resolve |
✅ Actions performedComments resolved. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (1)
src/window_manager.rs (1)
460-463: Workspace filtering acknowledgment is good, but consider a configuration option.
🧹 Nitpick comments (5)
src/snap.rs (3)
85-86: Consider making border_size and margin configurable parameters.The hardcoded values for
border_size(120.0) andmargin(10.0) limit the flexibility of the snap zones. These should be configurable through theSnapManagerconstructor to allow users to customize their snapping experience.impl SnapManager { - pub fn new(screen_rect: Rect, snap_threshold: f64) -> Self { + pub fn new(screen_rect: Rect, snap_threshold: f64, border_size: f64, margin: f64) -> Self { let mut manager = Self { screen_rect, snap_zones: Vec::new(), snap_threshold, + border_size, + margin, window_drag_states: HashMap::new(), };And update the struct to store these values:
pub struct SnapManager { screen_rect: Rect, snap_zones: Vec<SnapZone>, snap_threshold: f64, + border_size: f64, + margin: f64, window_drag_states: HashMap<WindowId, WindowDragState>, }
160-160: Remove unused parameter to improve code clarity.The
_border_sizeparameter increate_zone_rectis marked as unused with an underscore prefix, but it's passed to every call. This suggests either the parameter should be used or removed entirely.- fn create_zone_rect( - &self, - screen_rect: Rect, - config: (f64, f64, f64, f64), - _border_size: f64, - margin: f64, - ) -> Rect { + fn create_zone_rect( + &self, + screen_rect: Rect, + config: (f64, f64, f64, f64), + margin: f64, + ) -> Rect {And update the call site:
- let bounds = self.create_zone_rect(screen_rect, bounds_config, border_size, margin); - let snap_rect = self.create_zone_rect(screen_rect, snap_config, border_size, margin); + let bounds = self.create_zone_rect(screen_rect, bounds_config, margin); + let snap_rect = self.create_zone_rect(screen_rect, snap_config, margin);
185-189: Complex margin calculation logic needs documentation.The margin calculation logic in the width calculation is non-trivial and could benefit from a comment explaining when and why the margin adjustment is applied.
let width = if w_config < 0.0 { -w_config } else if w_config <= 1.0 { + // Adjust for margin when zone starts at margin position to prevent overlap screen_rect.width * w_config - if x_config == margin { 2.0 * margin } else { 0.0 } } else { w_config };src/window_manager.rs (2)
597-607: Potential improvement: Consolidate duplicate timer spawning logic.The timer spawning logic for drag end checks is duplicated between the initial drag start and drag continuation cases. Consider extracting this into a helper method to reduce code duplication.
+ fn schedule_drag_end_check(&mut self, window_id: WindowId, rect: Rect, delay_ms: u64) { + let command_tx = self.command_tx.clone(); + let handle = tokio::spawn(async move { + tokio::time::sleep(tokio::time::Duration::from_millis(delay_ms)).await; + info!("⏰ SENDING DRAG END CHECK for window {:?}", window_id); + if let Err(e) = command_tx + .send(Command::CheckSnapDragEnd(window_id, rect)) + .await + { + warn!("Failed to send snap check command: {}", e); + } + }); + self.pending_drag_end = Some(handle); + } if !self.snap_manager.is_window_dragging(window_id) { info!("🚀 STARTING DRAG TRACKING for window {:?}", window_id); self.snap_manager.start_window_drag(window_id, old_rect); - // Schedule check for drag end - let command_tx = self.command_tx.clone(); - let window_id_copy = window_id; - let new_rect_copy = new_rect; - - let handle = tokio::spawn(async move { - tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; - info!("⏰ SENDING DRAG END CHECK for window {:?}", window_id_copy); - if let Err(e) = command_tx - .send(Command::CheckSnapDragEnd(window_id_copy, new_rect_copy)) - .await - { - warn!("Failed to send snap check command: {}", e); - } - }); - self.pending_drag_end = Some(handle); + self.schedule_drag_end_check(window_id, new_rect, 500); } else { info!("⏳ CONTINUING DRAG for window {:?}", window_id); self.snap_manager.update_window_drag(window_id, new_rect); - // Schedule a new drag end check - let command_tx = self.command_tx.clone(); - let window_id_copy = window_id; - let new_rect_copy = new_rect; - - let handle = tokio::spawn(async move { - tokio::time::sleep(tokio::time::Duration::from_millis(400)).await; - info!( - "⏰ SENDING DRAG END CHECK (continuation) for window {:?}", - window_id_copy - ); - if let Err(e) = command_tx - .send(Command::CheckSnapDragEnd(window_id_copy, new_rect_copy)) - .await - { - warn!("Failed to send snap check command: {}", e); - } - }); - self.pending_drag_end = Some(handle); + self.schedule_drag_end_check(window_id, new_rect, 400); }Also applies to: 617-630
773-796: Dead code should be removed or implemented.The
update_layout_for_manual_movemethod is marked with#[allow(dead_code)]and contains placeholder logic. If this functionality isn't needed, consider removing it. If it will be implemented later, add a TODO comment with specific plans.- #[allow(dead_code)] - async fn update_layout_for_manual_move( - &mut self, - window_id: WindowId, - new_rect: Rect, - ) -> Result<()> { - // For now, we'll just apply the existing layout logic - // In a more sophisticated implementation, we might update the BSP tree - // to reflect the manual positioning - debug!( - "Window {:?} manually moved to {:?}, updating layout", - window_id, new_rect - ); - - // You could implement logic here to: - // 1. Remove the window from its current position in the BSP tree - // 2. Find where it should be placed based on its new position - // 3. Rebuild the tree structure accordingly - - // For now, just ensure the layout is consistent - self.apply_layout().await?; - - Ok(()) - } + // TODO: Implement BSP tree update for manual window positioning + // This would allow preserving user-initiated window moves within the layout system
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
src/macos/accessibility.rs(9 hunks)src/snap.rs(1 hunks)src/window_manager.rs(17 hunks)
🔇 Additional comments (20)
src/snap.rs (2)
249-250: Good implementation of configurable snap thresholds.The snap threshold is now properly used for both time and distance calculations, addressing the previous issue where hardcoded values were used instead of the constructor parameter.
333-398: Well-structured snap target detection with clear priority.The method correctly prioritizes corners over edges over center, which provides intuitive snapping behavior. The extensive debug logging will be helpful for troubleshooting snap behavior.
src/window_manager.rs (4)
130-136: Excellent documentation of refresh interval decisions.The detailed comment explaining the different intervals (1000ms for refresh vs 200ms for monitoring) and suggesting future configurability addresses the previous concern about inconsistent intervals.
583-586: Good implementation of timer cancellation.The addition of timer cancellation logic properly addresses the race condition issue identified in previous reviews. This ensures only the latest drag-end check runs.
496-558: Excellent batch window movement implementation with fallback.The implementation properly attempts batch window moves first, then falls back to individual moves with retry logic if the batch operation fails. This provides both performance optimization and reliability.
686-687: Good threshold for avoiding redundant snap moves.The 5-pixel threshold prevents unnecessary window movements when the snap position is very close to the current position, improving user experience.
src/macos/accessibility.rs (14)
2-13: LGTM - Import fixes resolve compilation issues.The addition of
core_foundation::arrayimports fixes the previous compilation errors, and the newproc_listpidsfunction declaration is necessary for system-wide process enumeration functionality.
15-66: LGTM - Standard macOS API constants and structures.These additions correctly define the necessary constants and structures for Core Foundation and Accessibility API integration.
134-141: Improved window ID generation approach.The change from raw pointer casting to the
generate_window_idmethod addresses previous stability concerns with window identification. The new approach incorporates PID and hash-based generation for more reliable window IDs.
174-214: Excellent robust window movement implementation.The rewritten method properly handles cache refreshing, includes a fallback mechanism using CGWindow data for brute force searching, and has correct memory management with appropriate CFRelease calls. This addresses previous concerns about window finding reliability.
216-239: Well-implemented window element finder.This method correctly implements window finding with proper cache management, memory safety through CFRetain calls, and appropriate fallback to cache refresh when windows aren't found.
241-311: Excellent batch window movement implementation.This method properly addresses the previous window-to-layout mapping concerns by grouping windows by PID and matching by index within each PID. The memory management is correct with proper CFRelease calls, and the error handling is comprehensive.
313-370: Correct implementation of window positioning.The method properly uses AXValue objects for setting window position and size, includes appropriate error checking, and handles memory management correctly with CFRelease calls for all created AXValue objects.
372-461: Comprehensive window enumeration implementation.The process enumeration correctly implements buffer management with safety limits to prevent infinite loops. The approach of enumerating all running processes provides comprehensive window discovery capability.
463-487: Good deprecation practice.The deprecated methods are properly marked with version information and provide clear migration guidance while maintaining backward compatibility.
489-581: Solid PID-based window enumeration.The implementation properly filters system processes, checks window tileability through attribute verification, and handles memory management correctly with appropriate CFRetain/CFRelease calls.
677-729: Well-designed cache refresh mechanism.The cache refresh methods properly handle memory cleanup, include throttling to prevent excessive refreshes, and provide appropriate fallback mechanisms. The 100ms throttling prevents performance issues while maintaining responsiveness.
731-886: Comprehensive window finding and rect extraction.These methods properly implement window finding through multiple strategies and correctly extract actual values from AXValue objects, addressing previous concerns about incomplete value extraction. Memory management is handled appropriately throughout.
888-1042: Robust enumeration and caching with collision handling.The implementation correctly handles window enumeration across all applications, includes collision detection with fallback ID generation, and properly manages memory with CFRetain for cached elements. The hash-based window ID generation incorporating PID and index provides good uniqueness.
1045-1055: Proper resource cleanup in Drop implementation.The Drop implementation correctly releases all cached window elements and the system element, ensuring proper cleanup of Core Foundation resources and preventing memory leaks.
There was a problem hiding this 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
♻️ Duplicate comments (2)
src/snap.rs (2)
82-230: Excellent refactoring addresses previous maintainability concerns!The
update_snap_zonesmethod has been significantly improved from the previous version. The data-driven approach using zone configurations in an array eliminates the repetitive code patterns that were flagged in past reviews. The code is now much more maintainable and easier to understand.
307-309: Good fix for the hardcoded threshold issue!The snap thresholds are now properly calculated from the configurable
snap_thresholdparameter, addressing the previous concern about ignoring constructor parameters. The scaling approach for distance threshold is well thought out.
🧹 Nitpick comments (6)
src/snap.rs (4)
58-59: Consider removing the dead_code attribute if the field is truly unused.The
window_idfield inWindowDragStateis marked with#[allow(dead_code)]but it's only used for initialization. If this field serves no purpose, consider removing it to simplify the struct.#[derive(Debug, Clone)] struct WindowDragState { - #[allow(dead_code)] - window_id: WindowId, initial_rect: Rect, is_dragging: bool, drag_start_time: std::time::Instant, }And update the initialization:
self.window_drag_states.insert( window_id, WindowDragState { - window_id, initial_rect: current_rect, is_dragging: true, drag_start_time: std::time::Instant::now(), }, );
232-261: Consider adding documentation for the mixed coordinate system.The
create_absolute_zone_rectmethod handles both relative (0-1) and absolute coordinates, which could be confusing for maintainers. Consider adding a doc comment explaining the coordinate system convention.+ /// Creates a rectangle from configuration that supports both relative and absolute coordinates. + /// Values <= 1.0 are treated as relative percentages of screen dimensions. + /// Values > 1.0 are treated as absolute pixel coordinates. fn create_absolute_zone_rect(&self, screen_rect: Rect, config: (f64, f64, f64, f64)) -> Rect {
350-351: Consider making position/size difference thresholds configurable.The hardcoded 10.0 pixel threshold for determining if a window needs to snap could be made configurable for better user experience customization.
+ // Add to SnapManager struct + position_tolerance: f64,- if dx > 10.0 || dy > 10.0 || dw > 10.0 || dh > 10.0 { + let tolerance = self.position_tolerance; + if dx > tolerance || dy > tolerance || dw > tolerance || dh > tolerance {
390-455: Optimize zone lookup with early termination.The
find_snap_targetmethod iterates through zones multiple times for prioritization. Consider creating a single prioritized iterator or caching the prioritized zones for better performance.pub fn find_snap_target(&self, window_rect: Rect) -> Option<Rect> { let center_x = window_rect.x + window_rect.width / 2.0; let center_y = window_rect.y + window_rect.height / 2.0; + // Create prioritized zones: corners first, then edges, then center + let prioritized_regions = [ + &[SnapRegion::NorthWest, SnapRegion::NorthEast, SnapRegion::SouthWest, SnapRegion::SouthEast], + &[SnapRegion::North, SnapRegion::South, SnapRegion::East, SnapRegion::West], + &[SnapRegion::Center], + ]; + + for region_group in prioritized_regions { for zone in &self.snap_zones { - if matches!( - zone.region, - SnapRegion::NorthWest - | SnapRegion::NorthEast - | SnapRegion::SouthWest - | SnapRegion::SouthEast - ) && self.point_in_rect(center_x, center_y, &zone.bounds) - { - debug!( - "Window center ({}, {}) in {} zone", - center_x, - center_y, - zone.region.name() - ); - return Some(zone.snap_rect); + if region_group.contains(&zone.region) && self.point_in_rect(center_x, center_y, &zone.bounds) { + debug!("Window center ({}, {}) in {} zone", center_x, center_y, zone.region.name()); + return Some(zone.snap_rect); } } + }llms.txt (2)
9-9: Fix redundant acronym usage.The phrase "CLI Interface" contains a redundancy since "CLI" already stands for "Command-Line Interface."
-- **CLI Interface** (`main.rs`): Command-line entry point +- **CLI** (`main.rs`): Command-line entry point🧰 Tools
🪛 LanguageTool
[style] ~9-~9: This phrase is redundant (‘I’ stands for ‘Interface’). Use simply “CLIInterface”.
Context: ...ssibility APIs. ## Key Components - CLI Interface (main.rs): Command-line entry point...(ACRONYM_TAUTOLOGY)
56-56: Add missing article for better grammar.The phrase "with macOS window system" is missing the definite article "the."
-- Integrates with macOS window system through Accessibility bindings +- Integrates with the macOS window system through Accessibility bindings🧰 Tools
🪛 LanguageTool
[uncategorized] ~56-~56: You might be missing the article “the” here.
Context: ...for window operations - Integrates with macOS window system through Accessibility bin...(AI_EN_LECTOR_MISSING_DETERMINER_THE)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
llms.txt(1 hunks)src/snap.rs(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/snap.rs (2)
src/window_manager.rs (1)
new(82-114)src/lib.rs (1)
new(28-35)
🪛 LanguageTool
llms.txt
[style] ~9-~9: This phrase is redundant (‘I’ stands for ‘Interface’). Use simply “CLIInterface”.
Context: ...ssibility APIs. ## Key Components - CLI Interface (main.rs): Command-line entry point...
(ACRONYM_TAUTOLOGY)
[grammar] ~17-~17: The operating system from Apple is written “macOS”.
Context: ...ng integration - macOS Integration (macos/): Accessibility API bindings, Core Gr...
(MAC_OS)
[uncategorized] ~56-~56: You might be missing the article “the” here.
Context: ...for window operations - Integrates with macOS window system through Accessibility bin...
(AI_EN_LECTOR_MISSING_DETERMINER_THE)
🔇 Additional comments (1)
llms.txt (1)
1-81: Excellent comprehensive project documentation!This documentation provides valuable context for the tiling window manager project, covering architecture, dependencies, development workflow, and important considerations. The structure is clear and will be very helpful for contributors and maintainers.
🧰 Tools
🪛 LanguageTool
[style] ~9-~9: This phrase is redundant (‘I’ stands for ‘Interface’). Use simply “CLIInterface”.
Context: ...ssibility APIs. ## Key Components - CLI Interface (main.rs): Command-line entry point...(ACRONYM_TAUTOLOGY)
[grammar] ~17-~17: The operating system from Apple is written “macOS”.
Context: ...ng integration - macOS Integration (macos/): Accessibility API bindings, Core Gr...(MAC_OS)
[uncategorized] ~56-~56: You might be missing the article “the” here.
Context: ...for window operations - Integrates with macOS window system through Accessibility bin...(AI_EN_LECTOR_MISSING_DETERMINER_THE)
There was a problem hiding this 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
♻️ Duplicate comments (1)
src/window_manager.rs (1)
140-140: Refresh interval consistency has been addressed.The comment clearly explains the rationale for different intervals between window refresh (1000ms) and monitoring (200ms), and suggests making them configurable.
🧹 Nitpick comments (4)
src/macos/window_notifications.rs (1)
269-289: Consider safer memory management pattern for stored callback data.The current implementation uses a complex pattern with
Box::from_rawandBox::into_rawto avoid dropping the data. While functional, this pattern is error-prone and could lead to memory leaks if not handled carefully.Consider using a more straightforward approach with
Rcor storing the data directly in a static variable:-unsafe fn get_dragging_windows(observer: &Object) -> Option<Arc<Mutex<HashMap<WindowId, Rect>>>> { - let ptr: *const std::ffi::c_void = *observer.get_ivar("dragging_windows"); - if ptr.is_null() { - return None; - } - let boxed = Box::from_raw(ptr as *mut Arc<Mutex<HashMap<WindowId, Rect>>>); - let result = Some((*boxed).clone()); - let _ = Box::into_raw(boxed); // Don't drop it - result -} +unsafe fn get_dragging_windows(observer: &Object) -> Option<Arc<Mutex<HashMap<WindowId, Rect>>>> { + let ptr: *const std::ffi::c_void = *observer.get_ivar("dragging_windows"); + if ptr.is_null() { + return None; + } + // Cast directly to Arc reference without taking ownership + let arc_ref = &*(ptr as *const Arc<Mutex<HashMap<WindowId, Rect>>>); + Some(arc_ref.clone()) +}The same pattern should be applied to
get_event_sender.src/window_manager.rs (3)
189-221: Consider extracting drag detection constants and simplifying the logic.The drag detection logic uses a hardcoded distance threshold of 5 pixels and has complex nested conditions. This could be made more maintainable.
+ const DRAG_END_THRESHOLD: f64 = 5.0; + + fn calculate_distance(rect1: &Rect, rect2: &Rect) -> f64 { + ((rect2.x - rect1.x).powi(2) + (rect2.y - rect1.y).powi(2)).sqrt() + } + // Check if this is the end of a user drag by comparing with previous position if let Some(&previous_rect) = self.previous_window_positions.get(&id) { - let distance = ((new_rect.x - previous_rect.x).powi(2) + (new_rect.y - previous_rect.y).powi(2)).sqrt(); + let distance = Self::calculate_distance(&previous_rect, &new_rect); // If the window hasn't moved much, this might be the end of a drag - if distance < 5.0 { + if distance < Self::DRAG_END_THRESHOLD {
73-74: Remove or utilize the dead code field.The
command_txfield is marked with#[allow(dead_code)]but appears to be used in the codebase (e.g., passed to various managers).If this field is actually being used, remove the
#[allow(dead_code)]attribute:- #[allow(dead_code)] command_tx: mpsc::Sender<Command>,
890-913: Document the purpose of placeholder method or implement it.The
update_layout_for_manual_movemethod is marked as dead code and contains only placeholder logic.The comment indicates this is for future BSP tree updates. Would you like me to:
- Implement the BSP tree update logic based on the new window position?
- Create a GitHub issue to track this TODO?
- Remove the method if it's not needed in the near future?
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/macos/mod.rs(1 hunks)src/macos/window_notifications.rs(1 hunks)src/snap.rs(1 hunks)src/window_manager.rs(14 hunks)
✅ Files skipped from review due to trivial changes (1)
- src/macos/mod.rs
🚧 Files skipped from review as they are similar to previous changes (1)
- src/snap.rs
There was a problem hiding this 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)
src/macos/window_notifications.rs (1)
56-63: Consider safer memory management for cross-boundary data storage.The current approach stores raw pointers to boxed data in Objective-C instance variables, which could lead to memory safety issues if not handled carefully. While the current implementation manually manages the lifecycle, this pattern is error-prone.
Consider using a safer approach with reference counting or storing the data in a static registry indexed by observer pointer:
// Alternative approach - use a static registry use std::sync::RwLock; use std::collections::HashMap; static OBSERVER_DATA: RwLock<HashMap<*const Object, (Arc<Mutex<HashMap<WindowId, Rect>>>, mpsc::Sender<WindowDragEvent>)>> = RwLock::new(HashMap::new()); // In start_observing: -(*observer).set_ivar( - "dragging_windows", - Box::into_raw(Box::new(dragging_windows)) as *const _ as *const std::ffi::c_void, -); -(*observer).set_ivar( - "event_sender", - Box::into_raw(Box::new(event_sender)) as *const _ as *const std::ffi::c_void, -); +OBSERVER_DATA.write().unwrap().insert(observer, (dragging_windows, event_sender));src/window_manager.rs (1)
765-788: Placeholder method needs implementation guidance.The
update_layout_for_manual_movemethod is marked as dead code and only contains placeholder logic. Consider either implementing the BSP tree update logic or removing it if not needed.The method outlines the intended functionality in comments. Would you like me to help implement the BSP tree update logic or should this method be removed if it's not currently needed?
+// If implementing BSP tree updates: +// 1. Find the window's current position in the BSP tree +// 2. Calculate the new logical position based on screen coordinates +// 3. Restructure the tree to reflect the new layout +// 4. Apply the updated layout to all affected windows
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/macos/window_notifications.rs(1 hunks)src/window_manager.rs(14 hunks)
🔇 Additional comments (6)
src/macos/window_notifications.rs (1)
260-277: PID extraction implementation looks correct.The updated implementation properly extracts the process ID using the NSRunningApplication API, which addresses the previous concern about the hardcoded placeholder value.
The approach using
msg_send![window, app]followed bymsg_send![app, runningApplication]and thenmsg_send![running_app, processIdentifier]is the correct way to get the owner PID from an NSWindow.src/window_manager.rs (5)
134-140: Well-documented refresh interval configuration.The detailed comments explaining the refresh timer intervals and suggesting future configuration options provide excellent context for the design decisions. The 1000ms refresh interval is now properly justified in relation to the 200ms monitoring interval.
405-438: Effective workspace detection improves reliability.The new
get_effective_current_workspacemethod provides a robust fallback mechanism that prioritizes focused window workspace, then most common workspace, and finally the stored workspace. This addresses workspace detection reliability issues.The implementation correctly handles edge cases and provides clear debug logging for workspace detection decisions.
577-640: Batch window movement with proper fallback.The implementation attempts to use the new
move_all_windowsmethod for efficiency, with appropriate fallback to individual moves. The retry logic with exponential backoff is well-implemented.The error handling gracefully degrades performance while maintaining functionality, and the logging provides good visibility into which approach was used.
646-763: Comprehensive drag end handling implementation.The
handle_drag_endmethod properly integrates with the snap manager and handles all drag result types (snap, swap, return, no action). The programmatic move tracking prevents recursive drag detection.The implementation correctly:
- Uses effective workspace for collision detection
- Marks windows as programmatically moving to prevent recursive snapping
- Updates internal state after moves
- Clears drag state when complete
511-515: Window position tracking initialization.Good practice to initialize position tracking for new windows during refresh. This ensures drag detection works correctly for all windows from the start.
There was a problem hiding this 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
♻️ Duplicate comments (1)
src/snap.rs (1)
457-459: Fix boundary overlap issue in point detection.Using
<=for both bounds checks means points exactly on the right/bottom edges belong to multiple zones, which can cause unexpected behavior with overlapping zones.- fn point_in_rect(&self, x: f64, y: f64, rect: &Rect) -> bool { - x >= rect.x && x <= rect.x + rect.width && y >= rect.y && y <= rect.y + rect.height - } + fn point_in_rect(&self, x: f64, y: f64, rect: &Rect) -> bool { + x >= rect.x && x < rect.x + rect.width && y >= rect.y && y < rect.y + rect.height + }
🧹 Nitpick comments (2)
src/snap.rs (2)
52-52: Unclear parameter naming: snap_threshold used for distance.The field name
snap_thresholdis ambiguous since it's used for distance thresholds but could be confused with time thresholds. Consider renaming for clarity.- snap_threshold: f64, + snap_distance_threshold: f64,
350-350: Magic number threshold should be configurable.The 10.0 pixel threshold for determining if a window is "already close to warp target" is hardcoded. Consider making this configurable or using a consistent threshold system.
+ const SNAP_PRECISION_THRESHOLD: f64 = 10.0; + // In the method: - if dx > 10.0 || dy > 10.0 || dw > 10.0 || dh > 10.0 { + if dx > Self::SNAP_PRECISION_THRESHOLD || dy > Self::SNAP_PRECISION_THRESHOLD || + dw > Self::SNAP_PRECISION_THRESHOLD || dh > Self::SNAP_PRECISION_THRESHOLD {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/snap.rs(1 hunks)src/window_manager.rs(15 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/window_manager.rs
🔇 Additional comments (6)
src/snap.rs (6)
1-4: Clean imports and dependencies.The module imports are well-organized and include only necessary dependencies.
5-32: Well-designed SnapRegion enum with helpful utilities.The enum covers all necessary screen regions for comprehensive window snapping, and the
name()method provides useful string representations for debugging.
41-47: DragResult enum effectively models all drag outcomes.The enum variants appropriately handle the different scenarios: snapping, swapping (with original rect preservation), returning to original position, and no action.
82-230: Well-structured zone configuration system.The current implementation using a zones array with tuple configurations is much more maintainable than repetitive hardcoded zone creation. The helper method
create_absolute_zone_rectprovides good abstraction for coordinate handling.
497-521: Efficient window detection algorithm.The
find_window_under_dragmethod uses a simple center-point collision detection which is appropriate for the use case and efficiently excludes the dragged window itself.
523-537: Clean utility methods with appropriate interfaces.The getter methods and state management functions provide a clean interface for external consumers while maintaining proper encapsulation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (3)
src/window_manager.rs (3)
414-504: Complex but well-structured immediate positioning logic.The implementation correctly handles:
- Distance-based movement detection (20px threshold is reasonable)
- Zone-based actions (center→swap, edges→snap, outside→return)
- Redundant move prevention (10px tolerance)
- Proper error handling with fallbacks
The complexity is warranted for the functionality, but consider adding inline comments for the zone logic to improve maintainability.
696-800: Excellent layout application improvements with robust error handling.Key improvements:
- Uses effective workspace detection for better reliability
- Bulk window moves with individual move fallback
- 3-attempt retry logic with 50ms delays for transient failures
- Comprehensive logging for debugging
- Proper programmatic move tracking
The retry logic effectively handles the window positioning challenges mentioned in the AI summary.
Consider making the retry count and delay configurable via the config file for different system performance characteristics.
969-992: Consider removing or better documenting this placeholder method.The
update_layout_for_manual_movemethod is marked as dead code and contains mostly TODO comments. Consider either:
- Remove it entirely if the functionality isn't planned for the near term
- Add a clear comment explaining when this will be implemented and its intended purpose
- Move it to a separate module for future BSP tree enhancements
This reduces code clutter and makes the current functionality clearer.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/snap.rs(1 hunks)src/window_manager.rs(15 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/snap.rs
🔇 Additional comments (12)
src/window_manager.rs (12)
5-5: LGTM! New imports and Window struct enhancement look good.The additions integrate well with the overall architecture:
- Window drag notifications import enables native macOS drag detection
- Snap manager import provides the snapping functionality
- The
owner_pidfield addition aligns with the enhanced window metadata extraction mentioned in the AI summaryAlso applies to: 8-8, 20-20
69-88: Well-designed state tracking for drag management.The new fields effectively separate concerns:
snap_managerhandles snapping logicdrag_observeranddrag_event_rxenable native drag detection- State tracking sets (
programmatically_moving,user_dragging_windows) prevent feedback loopsprevious_window_positionsenables proper drag distance calculationsThis design should prevent the race conditions mentioned in past reviews.
102-130: Solid initialization with proper error handling.The setup correctly:
- Initializes drag observer with error propagation
- Sets reasonable snap threshold (50px)
- Properly initializes all state tracking collections
- Uses screen rect for snap manager bounds
141-171: Good improvements to startup and event loop.Positive changes:
- Initial layout application ensures consistent startup state
- Drag event handling integrates cleanly into the main loop
- The interval documentation addresses past review concerns
The TODO comment about making intervals configurable is helpful for future improvement.
196-214: Excellent move event handling with proper state management.The logic correctly differentiates between:
- Programmatic moves (cleanup and state update)
- NSWindow-tracked user drags (state update only)
- Other user moves (immediate snap processing)
This should eliminate the feedback loop issues mentioned in past reviews.
269-269: Consistent programmatic move tracking prevents feedback loops.The pattern of marking windows as
programmatically_movingbefore repositioning them is correctly applied across all relevant commands. This ensures these moves won't trigger unintended snapping behavior.Also applies to: 291-293, 326-326, 350-352
384-412: Well-implemented drag event handling with good integration.The method correctly:
- Tracks user drag state via
user_dragging_windows- Integrates with snap manager for drag tracking
- Processes drag end only for managed windows
- Includes helpful debug logging
The emoji logging adds good visual distinction for debugging drag operations.
506-559: Well-implemented helper methods with consistent patterns.Both
swap_windowsandreturn_window_to_originalcorrectly:
- Mark windows as programmatically moving to prevent feedback
- Handle errors gracefully with appropriate logging
- Update internal state (
previous_window_positions)- Follow the established error handling patterns
565-598: Excellent workspace detection with robust fallback strategy.The multi-tier approach significantly improves reliability:
- Focused window's workspace (most reliable)
- Most common workspace among visible windows
- Stored current workspace (fallback)
This addresses the workspace filtering concerns from past reviews and provides much more reliable workspace awareness.
608-608: Consistent use of improved workspace detection.The switch to
get_effective_current_workspace()makes directional window navigation more reliable by using the enhanced workspace detection logic.Also applies to: 613-613
652-676: Good improvements to window refresh with better workspace tracking.The enhancements include:
- Workspace change detection with proper logging
- Initial position tracking for new windows (prevents missing position data)
- Graceful error handling for workspace detection failures
These changes support the improved drag and positioning functionality.
806-967: Comprehensive drag end handling with excellent error recovery.The method expertly handles all drag scenarios:
- SnapToZone: Checks for redundant moves before snapping
- SwapWithWindow: Uses bulk moves with individual move fallback
- ReturnToOriginal: Restores previous position
- NoAction: No operation needed
The comprehensive logging and proper state cleanup (
clear_drag_state) ensure reliable operation and prevent state leaks.
|
@coderabbitai resolve |
✅ Actions performedComments resolved. |
There was a problem hiding this 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
🧹 Nitpick comments (3)
src/window_manager.rs (3)
388-472: Consider decomposing the drag event handler.This method handles two distinct event types and is quite long. Consider splitting it into separate methods:
async fn handle_drag_event(&mut self, event: WindowDragEvent) -> Result<()> { match event { - WindowDragEvent::DragStarted { window_id, initial_rect, owner_pid } => { - // ... complex start logic - } - WindowDragEvent::DragEnded { window_id, final_rect, owner_pid } => { - // ... complex end logic - } + WindowDragEvent::DragStarted { window_id, initial_rect, owner_pid } => { + self.handle_drag_started(window_id, initial_rect, owner_pid).await + } + WindowDragEvent::DragEnded { window_id, final_rect, owner_pid } => { + self.handle_drag_ended(window_id, final_rect, owner_pid).await + } } - Ok(()) }This would improve maintainability and make the code easier to test.
875-983: Extract retry logic for better maintainability.The
apply_layoutmethod is comprehensive but quite long. Consider extracting the retry logic for individual window moves:+async fn move_window_with_retry(&mut self, window_id: WindowId, rect: Rect, max_attempts: usize) -> Result<()> { + for attempt in 0..max_attempts { + match self.macos.move_window(window_id, rect).await { + Ok(_) => { + debug!("Successfully moved window {:?} on attempt {}", window_id, attempt + 1); + return Ok(()); + } + Err(e) if attempt < max_attempts - 1 => { + debug!("Failed to move window {:?} on attempt {}: {}, retrying", window_id, attempt + 1, e); + tokio::time::sleep(Duration::from_millis(50)).await; + } + Err(e) => { + warn!("Failed to move window {:?} after {} attempts: {}", window_id, max_attempts, e); + return Err(e); + } + } + } + unreachable!() +}This would make the main layout logic cleaner and the retry logic reusable.
1-1188: Consider organizing the large file for better maintainability.This file has grown quite large (1188 lines) with several long methods. Consider:
- Extract common patterns: Workspace filtering, window position updates, and programmatic move tracking appear in multiple places
- Consider module organization: Drag handling, layout application, and window swapping could be separate modules
- Helper methods: Extract common operations like "mark as programmatic move and update position"
For example, a helper for common window state updates:
fn update_window_state(&mut self, window_id: WindowId, new_rect: Rect, mark_programmatic: bool) { if mark_programmatic { self.programmatically_moving.insert(window_id); } if let Some(window) = self.windows.get_mut(&window_id) { window.rect = new_rect; } self.previous_window_positions.insert(window_id, new_rect); }This would reduce duplication and improve consistency.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
README.md(1 hunks)src/window_manager.rs(15 hunks)
✅ Files skipped from review due to trivial changes (1)
- README.md
🔇 Additional comments (2)
src/window_manager.rs (2)
20-20: Good addition for process-based window management.The
owner_pidfield addition aligns well with the enhanced window enumeration and tracking capabilities mentioned in the PR summary.
745-778: Excellent workspace detection strategy.The fallback approach using focused window → most common workspace → stored value is a robust solution to the workspace detection reliability issues mentioned in previous reviews. The comprehensive logging will also help debug any remaining workspace detection problems.
| WindowEvent::WindowMoved(id, new_rect) => { | ||
| if let Some(window) = self.windows.get_mut(&id) { | ||
| window.rect = new_rect; | ||
| // Handle programmatic move cleanup | ||
| if self.programmatically_moving.contains(&id) { | ||
| debug!("Ignoring programmatic move for window {:?}", id); | ||
| self.programmatically_moving.remove(&id); | ||
| if let Some(window) = self.windows.get_mut(&id) { | ||
| window.rect = new_rect; | ||
| } | ||
| // Update previous position tracking for programmatic moves | ||
| self.previous_window_positions.insert(id, new_rect); | ||
| } else if self.user_dragging_windows.contains(&id) { | ||
| // This is a user drag that NSWindow notifications already started tracking | ||
| debug!("Window {:?} moved during NSWindow drag to {:?}", id, new_rect); | ||
| if let Some(window) = self.windows.get_mut(&id) { | ||
| window.rect = new_rect; | ||
| } | ||
| // Update position tracking but don't trigger immediate positioning | ||
| self.previous_window_positions.insert(id, new_rect); | ||
| } else { | ||
| // This is a user move - process for potential snapping | ||
| debug!("Window {:?} moved to {:?}", id, new_rect); | ||
| self.handle_immediate_window_positioning(id, new_rect).await?; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Refactor complex window move handling for better maintainability.
The window move handling has complex branching logic that could become brittle. Consider extracting each case into separate methods for better clarity and testing:
WindowEvent::WindowMoved(id, new_rect) => {
- // Handle programmatic move cleanup
- if self.programmatically_moving.contains(&id) {
- debug!("Ignoring programmatic move for window {:?}", id);
- // ... rest of programmatic logic
- } else if self.user_dragging_windows.contains(&id) {
- // ... rest of user drag logic
- } else {
- // ... rest of immediate positioning logic
- }
+ if self.programmatically_moving.contains(&id) {
+ self.handle_programmatic_move(id, new_rect).await?;
+ } else if self.user_dragging_windows.contains(&id) {
+ self.handle_user_drag_move(id, new_rect).await?;
+ } else {
+ self.handle_immediate_window_positioning(id, new_rect).await?;
+ }
}This would improve readability, testability, and reduce the risk of state inconsistencies.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/window_manager.rs around lines 196 to 218, the window move event handling
contains complex branching logic that reduces readability and maintainability.
Refactor this by extracting each branch of the if-else statement (programmatic
move, user dragging, and user move) into separate dedicated methods. Each method
should encapsulate the specific logic for updating window state and positions,
improving clarity and making the code easier to test and maintain.
| async fn handle_drag_end( | ||
| &mut self, | ||
| window_id: WindowId, | ||
| _initial_rect: Rect, | ||
| final_rect: Rect, | ||
| ) -> Result<()> { | ||
| // Get current workspace from focused window for reliable workspace detection | ||
| let effective_workspace = self.get_effective_current_workspace(); | ||
|
|
||
| // Get all windows for collision detection (filter by effective workspace) | ||
| let workspace_windows: Vec<&Window> = self | ||
| .windows | ||
| .values() | ||
| .filter(|w| w.workspace_id == effective_workspace && !w.is_minimized) | ||
| .collect(); | ||
|
|
||
| info!( | ||
| "🔍 Found {} windows in workspace for collision detection", | ||
| workspace_windows.len() | ||
| ); | ||
|
|
||
| // Check what should happen with this drag | ||
| let drag_result = | ||
| self.snap_manager | ||
| .end_window_drag(window_id, final_rect, &workspace_windows); | ||
|
|
||
| info!("🎯 Drag result: {:?}", drag_result); | ||
|
|
||
| match drag_result { | ||
| DragResult::SnapToZone(snap_rect) => { | ||
| // Check if window needs to be moved (avoid redundant moves) | ||
| let dx = (snap_rect.x - final_rect.x).abs(); | ||
| let dy = (snap_rect.y - final_rect.y).abs(); | ||
| let dw = (snap_rect.width - final_rect.width).abs(); | ||
| let dh = (snap_rect.height - final_rect.height).abs(); | ||
|
|
||
| if dx > 5.0 || dy > 5.0 || dw > 5.0 || dh > 5.0 { | ||
| info!( | ||
| "📍 Snapping window {:?} to zone at {:?}", | ||
| window_id, snap_rect | ||
| ); | ||
|
|
||
| // Mark as programmatic move | ||
| self.programmatically_moving.insert(window_id); | ||
|
|
||
| // Move the window to snap position (no delay needed with proper notifications) | ||
| match self.macos.move_window(window_id, snap_rect).await { | ||
| Ok(_) => info!("✅ Successfully snapped window {:?} to zone", window_id), | ||
| Err(e) => warn!("❌ Failed to snap window {:?}: {}", window_id, e), | ||
| } | ||
|
|
||
| // Update our internal state | ||
| if let Some(window) = self.windows.get_mut(&window_id) { | ||
| window.rect = snap_rect; | ||
| } | ||
| } | ||
| } | ||
| DragResult::SwapWithWindow(target_window_id, original_rect) => { | ||
| info!( | ||
| "🔄 Swapping window {:?} with window {:?}", | ||
| window_id, target_window_id | ||
| ); | ||
|
|
||
| // Get current window positions for accuracy | ||
| let current_windows = self.macos.get_windows().await?; | ||
| let target_window_current = current_windows.iter().find(|w| w.id == target_window_id); | ||
|
|
||
| if let Some(target_window) = target_window_current { | ||
| let target_rect = target_window.rect; | ||
|
|
||
| // Mark both windows as programmatic moves | ||
| self.programmatically_moving.insert(window_id); | ||
| self.programmatically_moving.insert(target_window_id); | ||
|
|
||
| // Create layouts for both windows in their swapped positions | ||
| let mut swap_layouts = HashMap::new(); | ||
| swap_layouts.insert(window_id, target_rect); | ||
| swap_layouts.insert(target_window_id, original_rect); | ||
|
|
||
| // Get both window objects for the bulk move API | ||
| let both_windows: Vec<crate::Window> = [window_id, target_window_id] | ||
| .iter() | ||
| .filter_map(|id| current_windows.iter().find(|w| w.id == *id).cloned()) | ||
| .collect(); | ||
|
|
||
| info!("🔄 Executing swap: dragged window {:?} -> {:?}, target window {:?} -> {:?}", | ||
| window_id, target_rect, target_window_id, original_rect); | ||
|
|
||
| // Use the bulk move API which tends to be more reliable | ||
| match self.macos.move_all_windows(&swap_layouts, &both_windows).await { | ||
| Ok(_) => { | ||
| // Update our internal state | ||
| if let Some(window) = self.windows.get_mut(&window_id) { | ||
| window.rect = target_rect; | ||
| } | ||
| if let Some(window) = self.windows.get_mut(&target_window_id) { | ||
| window.rect = original_rect; | ||
| } | ||
|
|
||
| // Update position tracking | ||
| self.previous_window_positions.insert(window_id, target_rect); | ||
| self.previous_window_positions.insert(target_window_id, original_rect); | ||
|
|
||
| info!( | ||
| "✅ Successfully swapped windows {:?} and {:?}", | ||
| window_id, target_window_id | ||
| ); | ||
| } | ||
| Err(e) => { | ||
| warn!("Bulk swap failed, trying individual moves: {}", e); | ||
|
|
||
| // Fallback to individual moves | ||
| match self.macos.move_window(window_id, target_rect).await { | ||
| Ok(_) => { | ||
| info!("✅ Moved dragged window to target position"); | ||
| if let Some(window) = self.windows.get_mut(&window_id) { | ||
| window.rect = target_rect; | ||
| } | ||
| self.previous_window_positions.insert(window_id, target_rect); | ||
| } | ||
| Err(e) => warn!("❌ Failed to move dragged window: {}", e), | ||
| } | ||
|
|
||
| match self.macos.move_window(target_window_id, original_rect).await { | ||
| Ok(_) => { | ||
| info!("✅ Moved target window to original position"); | ||
| if let Some(window) = self.windows.get_mut(&target_window_id) { | ||
| window.rect = original_rect; | ||
| } | ||
| self.previous_window_positions.insert(target_window_id, original_rect); | ||
| } | ||
| Err(e) => warn!("❌ Failed to move target window: {}", e), | ||
| } | ||
|
|
||
| info!( | ||
| "✅ Completed swap with individual moves: {:?} and {:?}", | ||
| window_id, target_window_id | ||
| ); | ||
| } | ||
| } | ||
| } else { | ||
| warn!("❌ Target window {:?} not found in current windows", target_window_id); | ||
| } | ||
| } | ||
| DragResult::ReturnToOriginal(original_rect) => { | ||
| info!( | ||
| "↩️ Returning window {:?} to original position {:?}", | ||
| window_id, original_rect | ||
| ); | ||
|
|
||
| // Mark as programmatic move | ||
| self.programmatically_moving.insert(window_id); | ||
|
|
||
| // Move the window back to its original position | ||
| match self.macos.move_window(window_id, original_rect).await { | ||
| Ok(_) => { | ||
| info!("✅ Successfully returned window {:?} to original position", window_id); | ||
| // Update our internal state | ||
| if let Some(window) = self.windows.get_mut(&window_id) { | ||
| window.rect = original_rect; | ||
| } | ||
| self.previous_window_positions.insert(window_id, original_rect); | ||
| } | ||
| Err(e) => warn!("❌ Failed to return window {:?} to original position: {}", window_id, e), | ||
| } | ||
| } | ||
| DragResult::NoAction => { | ||
| info!("❌ No action needed for window {:?}", window_id); | ||
| } | ||
| } | ||
|
|
||
| // Always clear the drag state when we're done | ||
| self.snap_manager.clear_drag_state(window_id); | ||
| info!("🧹 Cleared drag state for window {:?}", window_id); | ||
|
|
||
| Ok(()) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Clarify the purpose of this drag end handler.
This method appears to duplicate much of the drag end logic found in handle_drag_event. Consider:
- If this is redundant: Remove it to eliminate code duplication
- If it serves a different purpose: Add clear documentation explaining when each method should be used
- If both are needed: Extract common logic into shared helper methods
The method is also quite long and could benefit from decomposition into smaller, focused methods for each drag result type.
🏁 Script executed:
#!/bin/bash
# Search for calls to handle_drag_end to understand its usage
rg "handle_drag_end" -A 3 -B 3Length of output: 299
🏁 Script executed:
#!/bin/bash
# Locate definition and usages of handle_drag_event
rg "async fn handle_drag_event" -A50 -B0 src/window_manager.rs
# Search for handle_drag_end references inside handle_drag_event
rg "handle_drag_end" -A5 -B5 src/window_manager.rsLength of output: 3538
Consolidate drag-end handling and remove unused duplication
The handle_drag_end method (src/window_manager.rs:986–1162) duplicates the DragEnded arm in handle_drag_event but is never invoked. To clean this up:
- Remove the unused
handle_drag_endmethod, or - Refactor
handle_drag_eventto delegate itsDragEndedlogic tohandle_drag_endand eliminate the inline duplication. - In either case, extract each
DragResultbranch (snap, swap, return, no-action) into small, focused helpers for readability and maintainability.
🤖 Prompt for AI Agents
In src/window_manager.rs from lines 986 to 1162, the handle_drag_end method
duplicates the drag end logic already present inline in the DragEnded arm of
handle_drag_event but is never called. To fix this, either remove the unused
handle_drag_end method entirely to eliminate dead code or refactor
handle_drag_event to delegate its DragEnded case to handle_drag_end, removing
the inline duplication. Additionally, extract each DragResult match branch
(SnapToZone, SwapWithWindow, ReturnToOriginal, NoAction) into separate small
helper functions to improve readability and maintainability.
| snap_manager: SnapManager, | ||
|
|
||
| event_rx: mpsc::Receiver<WindowEvent>, | ||
| command_rx: mpsc::Receiver<Command>, | ||
| #[allow(dead_code)] | ||
| command_tx: mpsc::Sender<Command>, | ||
|
|
||
| // Drag notification system | ||
| drag_observer: WindowDragNotificationObserver, | ||
| drag_event_rx: mpsc::Receiver<WindowDragEvent>, | ||
|
|
||
| // Track windows being moved programmatically to avoid snap conflicts | ||
| programmatically_moving: std::collections::HashSet<WindowId>, | ||
|
|
||
| // Track actual user drag state (via NSWindow notifications) | ||
| user_dragging_windows: std::collections::HashSet<WindowId>, | ||
|
|
||
| // Track window previous positions for immediate drag detection | ||
| previous_window_positions: std::collections::HashMap<WindowId, Rect>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Ensure proper cleanup of tracking collections.
The new tracking fields (programmatically_moving, user_dragging_windows, previous_window_positions) are well-structured for their purposes. However, ensure these collections are properly cleaned up when windows are destroyed to prevent memory leaks.
Consider adding cleanup logic in the WindowDestroyed event handler:
WindowEvent::WindowDestroyed(id) => {
if let Some(window) = self.windows.remove(&id) {
+ // Clean up tracking collections
+ self.programmatically_moving.remove(&id);
+ self.user_dragging_windows.remove(&id);
+ self.previous_window_positions.remove(&id);
+ self.snap_manager.clear_drag_state(id);
self.apply_layout().await?;
self.plugin_manager.on_window_destroyed(&window)?;
}
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| snap_manager: SnapManager, | |
| event_rx: mpsc::Receiver<WindowEvent>, | |
| command_rx: mpsc::Receiver<Command>, | |
| #[allow(dead_code)] | |
| command_tx: mpsc::Sender<Command>, | |
| // Drag notification system | |
| drag_observer: WindowDragNotificationObserver, | |
| drag_event_rx: mpsc::Receiver<WindowDragEvent>, | |
| // Track windows being moved programmatically to avoid snap conflicts | |
| programmatically_moving: std::collections::HashSet<WindowId>, | |
| // Track actual user drag state (via NSWindow notifications) | |
| user_dragging_windows: std::collections::HashSet<WindowId>, | |
| // Track window previous positions for immediate drag detection | |
| previous_window_positions: std::collections::HashMap<WindowId, Rect>, | |
| WindowEvent::WindowDestroyed(id) => { | |
| if let Some(window) = self.windows.remove(&id) { | |
| // Clean up tracking collections | |
| self.programmatically_moving.remove(&id); | |
| self.user_dragging_windows.remove(&id); | |
| self.previous_window_positions.remove(&id); | |
| self.snap_manager.clear_drag_state(id); | |
| self.apply_layout().await?; | |
| self.plugin_manager.on_window_destroyed(&window)?; | |
| } | |
| } |
🤖 Prompt for AI Agents
In src/window_manager.rs around lines 69 to 87, the new tracking collections for
window states need proper cleanup to avoid memory leaks. Update the
WindowDestroyed event handler to remove the destroyed window's ID from
programmatically_moving, user_dragging_windows, and previous_window_positions
collections. This ensures that all references to the destroyed window are
cleared promptly.
Summary by CodeRabbit