Conversation
|
can you please remove all of the unnecessary files or open a fresh pr? |
|
the nix files are so it can be built with nix, |
There was a problem hiding this comment.
Pull request overview
This pull request adds scratchpad functionality to Rift, a tiling window manager. Scratchpads are floating windows that can be quickly shown/hidden on demand, similar to dropdown terminals in other window managers. The implementation includes named scratchpads (allowing different scratchpads for different purposes), app rule-based scratchpad assignment, and keyboard commands for managing scratchpads.
Changes:
- Added new
ScratchpadManagermodule with comprehensive test coverage for managing scratchpad windows - Extended app workspace rules to support scratchpad configuration (boolean or named)
- Added CLI commands (
add_scratchpad,toggle_scratchpad) and keyboard shortcuts for scratchpad operations - Modified
EventResponseto support hiding windows, enabling scratchpad windows to be hidden when inactive
Reviewed changes
Copilot reviewed 14 out of 17 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| src/layout_engine/scratchpad.rs | New module implementing scratchpad window management with full test coverage |
| src/layout_engine/engine.rs | Integration of scratchpad functionality into layout engine, added hide_windows to EventResponse |
| src/layout_engine/floating.rs | Added is_active method for checking floating window visibility state |
| src/layout_engine.rs | Module declarations for new scratchpad functionality |
| src/model/virtual_workspace.rs | Support for scratchpad in app rules, removed Copy trait due to String field |
| src/common/config.rs | Added ScratchpadConfig enum for app rule configuration |
| src/actor/reactor.rs | Implementation of window hiding logic for scratchpads |
| src/actor/app.rs | Added Activate request for focusing apps |
| src/bin/rift-cli.rs | CLI commands for scratchpad operations |
| rift.default.toml | Example scratchpad keybindings in default config |
| src/actor/reactor/tests.rs | Updated tests for new hide_windows field in EventResponse |
| src/actor/reactor/testing.rs | Test utility updates |
| flake.nix | Nix flake for development environment setup |
| flake.lock | Nix flake lock file |
| .vscode/settings.json | VS Code Nix environment configuration |
| .gitignore | Added Nix development directories |
| .envrc | direnv configuration for Nix development environment |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| #[derive(Clone, Debug)] | ||
| struct Screen { | ||
| frame: CGRect, | ||
| space: Option<SpaceId>, | ||
| display_uuid: String, | ||
| name: Option<String>, | ||
| screen_id: ScreenId, | ||
| } | ||
|
|
||
| impl Screen { | ||
| fn display_uuid_opt(&self) -> Option<&str> { | ||
| if self.display_uuid.is_empty() { | ||
| None | ||
| } else { | ||
| Some(self.display_uuid.as_str()) | ||
| } | ||
| } | ||
|
|
||
| fn display_uuid_owned(&self) -> Option<String> { | ||
| self.display_uuid_opt().map(|uuid| uuid.to_string()) | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
This Screen struct and its implementation appear to be unused in the codebase. If this was added in preparation for future work, it should be removed until actually needed, or if it is used, the usage is not visible in the diff.
| #[derive(Clone, Debug)] | |
| struct Screen { | |
| frame: CGRect, | |
| space: Option<SpaceId>, | |
| display_uuid: String, | |
| name: Option<String>, | |
| screen_id: ScreenId, | |
| } | |
| impl Screen { | |
| fn display_uuid_opt(&self) -> Option<&str> { | |
| if self.display_uuid.is_empty() { | |
| None | |
| } else { | |
| Some(self.display_uuid.as_str()) | |
| } | |
| } | |
| fn display_uuid_owned(&self) -> Option<String> { | |
| self.display_uuid_opt().map(|uuid| uuid.to_string()) | |
| } | |
| } |
| self.toggle_stack_for_workspace(workspace_id, layout, default_orientation); | ||
| let unstacked_windows = | ||
| self.workspace_tree_mut(workspace_id) | ||
| .unstack_parent_of_selection(layout, default_orientation); | ||
|
|
||
| if !unstacked_windows.is_empty() { | ||
| return EventResponse { | ||
| raise_windows: unstacked_windows, | ||
| focus_window: None, | ||
| ..Default::default() | ||
| }; | ||
| } | ||
|
|
||
| let stacked_windows = | ||
| self.workspace_tree_mut(workspace_id) | ||
| .apply_stacking_to_parent_of_selection(layout, default_orientation); | ||
| if !stacked_windows.is_empty() { | ||
| return EventResponse { | ||
| raise_windows: stacked_windows, | ||
| focus_window: None, | ||
| ..Default::default() | ||
| }; | ||
| } | ||
|
|
||
| let visible_windows = self.workspace_tree(workspace_id).visible_windows_in_layout(layout); | ||
| if !visible_windows.is_empty() { | ||
| EventResponse { | ||
| raise_windows: visible_windows, | ||
| focus_window: None, | ||
| ..Default::default() | ||
| } | ||
| } else { | ||
| EventResponse::default() | ||
| } |
There was a problem hiding this comment.
The return value of toggle_stack_for_workspace is being ignored, and then the same operations (unstack_parent_of_selection, apply_stacking_to_parent_of_selection, and visible_windows_in_layout) are being performed again. This duplicates the work already done inside toggle_stack_for_workspace. The line should be changed to return self.toggle_stack_for_workspace(workspace_id, layout, default_orientation); and lines 1562-1594 should be removed.
| self.toggle_stack_for_workspace(workspace_id, layout, default_orientation); | |
| let unstacked_windows = | |
| self.workspace_tree_mut(workspace_id) | |
| .unstack_parent_of_selection(layout, default_orientation); | |
| if !unstacked_windows.is_empty() { | |
| return EventResponse { | |
| raise_windows: unstacked_windows, | |
| focus_window: None, | |
| ..Default::default() | |
| }; | |
| } | |
| let stacked_windows = | |
| self.workspace_tree_mut(workspace_id) | |
| .apply_stacking_to_parent_of_selection(layout, default_orientation); | |
| if !stacked_windows.is_empty() { | |
| return EventResponse { | |
| raise_windows: stacked_windows, | |
| focus_window: None, | |
| ..Default::default() | |
| }; | |
| } | |
| let visible_windows = self.workspace_tree(workspace_id).visible_windows_in_layout(layout); | |
| if !visible_windows.is_empty() { | |
| EventResponse { | |
| raise_windows: visible_windows, | |
| focus_window: None, | |
| ..Default::default() | |
| } | |
| } else { | |
| EventResponse::default() | |
| } | |
| return self.toggle_stack_for_workspace(workspace_id, layout, default_orientation); |
| let hidden_rect = self | ||
| .layout_manager | ||
| .layout_engine | ||
| .virtual_workspace_manager | ||
| .calculate_hidden_position( | ||
| screen.frame, | ||
| 0, | ||
| w.frame_monotonic.size, | ||
| HideCorner::BottomRight, | ||
| bundle_id, | ||
| ); |
There was a problem hiding this comment.
Direct field access to virtual_workspace_manager breaks encapsulation. The code should use the existing accessor method virtual_workspace_manager() instead (e.g., self.layout_manager.layout_engine.virtual_workspace_manager().calculate_hidden_position(...)). This would allow keeping the field private in LayoutEngine, maintaining better encapsulation.
| #[serde(skip)] | ||
| focused_window: Option<WindowId>, | ||
| virtual_workspace_manager: VirtualWorkspaceManager, | ||
| pub virtual_workspace_manager: VirtualWorkspaceManager, |
There was a problem hiding this comment.
The virtual_workspace_manager field should remain private. If the direct field access at src/actor/reactor.rs:2124 is changed to use the accessor method virtual_workspace_manager(), this field can stay private, maintaining better encapsulation.
| pub virtual_workspace_manager: VirtualWorkspaceManager, | |
| virtual_workspace_manager: VirtualWorkspaceManager, |
| @@ -1122,9 +1141,28 @@ impl VirtualWorkspaceManager { | |||
| } else { | |||
| self.window_rule_floating.remove(&(space, window_id)); | |||
| } | |||
|
|
|||
| let scratchpad_name = match &rule.scratchpad { | |||
| crate::common::config::ScratchpadConfig::Boolean(true) => Some("_default_".to_string()), | |||
| crate::common::config::ScratchpadConfig::Boolean(false) => None, | |||
| crate::common::config::ScratchpadConfig::Named(n) => Some(n.clone()), | |||
| }; | |||
|
|
|||
| if let Some(ref name) = scratchpad_name { | |||
| tracing::info!( | |||
| "Assigning window {:?} to scratchpad {:?} based on app rule", | |||
| window_id, | |||
| scratchpad_name | |||
| ); | |||
| self.window_rule_scratchpad.insert((space, window_id), name.clone()); | |||
| } else { | |||
| self.window_rule_scratchpad.remove(&(space, window_id)); | |||
| } | |||
There was a problem hiding this comment.
The scratchpad name conversion logic (lines 1118-1122 and 1145-1149) is duplicated. Consider extracting this into a helper method to reduce code duplication and make maintenance easier.
| Self::toggle_orientation_for_system(s, layout, default_orientation); | ||
| if s.parent_of_selection_is_stacked(layout) { | ||
| let toggled_windows = s | ||
| .apply_stacking_to_parent_of_selection(layout, default_orientation); | ||
| if !toggled_windows.is_empty() { | ||
| return EventResponse { | ||
| raise_windows: toggled_windows, | ||
| focus_window: None, | ||
| ..Default::default() | ||
| }; | ||
| } | ||
| } else { | ||
| s.toggle_tile_orientation(layout); | ||
| } |
There was a problem hiding this comment.
For the Traditional layout system case, toggle_orientation_for_system is called on line 1609, but then the same logic from that method is duplicated in lines 1610-1622. This causes the operation to be performed twice. The duplicate logic should be removed, and the result of toggle_orientation_for_system should be returned directly, similar to how it's handled for Bsp, MasterStack, and Scrolling layout systems (lines 1629, 1636, 1643).
| Self::toggle_orientation_for_system(s, layout, default_orientation); | |
| if s.parent_of_selection_is_stacked(layout) { | |
| let toggled_windows = s | |
| .apply_stacking_to_parent_of_selection(layout, default_orientation); | |
| if !toggled_windows.is_empty() { | |
| return EventResponse { | |
| raise_windows: toggled_windows, | |
| focus_window: None, | |
| ..Default::default() | |
| }; | |
| } | |
| } else { | |
| s.toggle_tile_orientation(layout); | |
| } | |
| return Self::toggle_orientation_for_system(s, layout, default_orientation); |
| } | ||
| LayoutCommand::ToggleScratchpad => self.handle_toggle_scratchpad(space, None), | ||
| LayoutCommand::ToggleScratchpadNamed(name) => { | ||
| println!("ToggleScratchpadNamed: {}", name); |
There was a problem hiding this comment.
Debug print statement left in production code. This should be removed or converted to a proper logging statement using the tracing crate (as done elsewhere in the codebase).
| println!("ToggleScratchpadNamed: {}", name); | |
| info!("ToggleScratchpadNamed: {}", name); |
| tracing::info!( | ||
| "Assigning window {:?} to scratchpad {:?} based on app rule", | ||
| window_id, | ||
| scratchpad_name | ||
| ); |
There was a problem hiding this comment.
Inconsistent indentation: the tracing::info! block (lines 1152-1156) is indented inside the if let Some(ref name) block starting at line 1151, but line 1157 which is also part of the same if-block has different indentation. The entire block from lines 1152-1157 should have consistent indentation.
| tracing::info!( | |
| "Assigning window {:?} to scratchpad {:?} based on app rule", | |
| window_id, | |
| scratchpad_name | |
| ); | |
| tracing::info!( | |
| "Assigning window {:?} to scratchpad {:?} based on app rule", | |
| window_id, | |
| scratchpad_name | |
| ); |
No description provided.