mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-01-11 22:12:53 +01:00
feat(wm): add direction preselection
This commit adds a new feature to preselect the direction of the next spawned window with a corresponding komorebic preselect-direction command which takes an OperationDirection. If the OperationDirection is valid from the current position, it will be stored in the Workspace state, and then read, applied, and deleted when the next manage-able window is spawned. Direction preselection does not (yet?) support the Grid layout.
This commit is contained in:
16
docs/cli/preselect-direction.md
Normal file
16
docs/cli/preselect-direction.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# preselect-direction
|
||||
|
||||
```
|
||||
Preselect the specified direction for the next window to be spawned on supported layouts
|
||||
|
||||
Usage: komorebic.exe preselect-direction <OPERATION_DIRECTION>
|
||||
|
||||
Arguments:
|
||||
<OPERATION_DIRECTION>
|
||||
[possible values: left, right, up, down]
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
@@ -55,6 +55,7 @@ pub enum SocketMessage {
|
||||
// Window / Container Commands
|
||||
FocusWindow(OperationDirection),
|
||||
MoveWindow(OperationDirection),
|
||||
PreselectDirection(OperationDirection),
|
||||
CycleFocusWindow(CycleDirection),
|
||||
CycleMoveWindow(CycleDirection),
|
||||
StackWindow(OperationDirection),
|
||||
|
||||
@@ -303,6 +303,12 @@ impl WindowManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
SocketMessage::PreselectDirection(direction) => {
|
||||
let focused_workspace = self.focused_workspace()?;
|
||||
if matches!(focused_workspace.layer, WorkspaceLayer::Tiling) {
|
||||
self.preselect_container_in_direction(direction)?;
|
||||
}
|
||||
}
|
||||
SocketMessage::MoveWindow(direction) => {
|
||||
let focused_workspace = self.focused_workspace()?;
|
||||
match focused_workspace.layer {
|
||||
|
||||
@@ -272,6 +272,7 @@ impl From<&WindowManager> for State {
|
||||
globals: workspace.globals,
|
||||
wallpaper: workspace.wallpaper.clone(),
|
||||
workspace_config: None,
|
||||
preselected_container_idx: None,
|
||||
})
|
||||
.collect::<VecDeque<_>>();
|
||||
ws.focus(monitor.workspaces.focused_idx());
|
||||
|
||||
@@ -2042,6 +2042,53 @@ impl WindowManager {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub fn preselect_container_in_direction(
|
||||
&mut self,
|
||||
direction: OperationDirection,
|
||||
) -> eyre::Result<()> {
|
||||
let workspace = self.focused_workspace_mut()?;
|
||||
let focused_idx = workspace.focused_container_idx();
|
||||
|
||||
if matches!(workspace.layout, Layout::Default(DefaultLayout::Grid)) {
|
||||
tracing::warn!("preselection is not supported on the grid layout");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
tracing::info!("preselecting container");
|
||||
|
||||
let new_idx =
|
||||
if workspace.maximized_window.is_some() || workspace.monocle_container.is_some() {
|
||||
None
|
||||
} else {
|
||||
workspace.new_idx_for_direction(direction)
|
||||
};
|
||||
|
||||
match new_idx {
|
||||
Some(new_idx) => {
|
||||
let adjusted_idx = match direction {
|
||||
OperationDirection::Left | OperationDirection::Up => {
|
||||
if focused_idx.abs_diff(new_idx) == 1 {
|
||||
new_idx + 1
|
||||
} else {
|
||||
new_idx
|
||||
}
|
||||
}
|
||||
_ => new_idx,
|
||||
};
|
||||
|
||||
workspace.preselect_container_index(adjusted_idx);
|
||||
}
|
||||
None => {
|
||||
tracing::debug!(
|
||||
"this is not a valid preselection direction from the current position"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub fn focus_container_in_direction(
|
||||
&mut self,
|
||||
|
||||
@@ -78,6 +78,8 @@ pub struct Workspace {
|
||||
pub wallpaper: Option<Wallpaper>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub workspace_config: Option<WorkspaceConfig>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub preselected_container_idx: Option<usize>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
@@ -129,6 +131,7 @@ impl Default for Workspace {
|
||||
globals: Default::default(),
|
||||
workspace_config: None,
|
||||
wallpaper: None,
|
||||
preselected_container_idx: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -976,6 +979,10 @@ impl Workspace {
|
||||
container
|
||||
}
|
||||
|
||||
pub fn preselect_container_index(&mut self, insertion_index: usize) {
|
||||
self.preselected_container_idx = Some(insertion_index);
|
||||
}
|
||||
|
||||
pub fn new_idx_for_direction(&self, direction: OperationDirection) -> Option<usize> {
|
||||
let len = NonZeroUsize::new(self.containers().len())?;
|
||||
|
||||
@@ -1075,7 +1082,13 @@ impl Workspace {
|
||||
}
|
||||
|
||||
pub fn new_container_for_window(&mut self, window: Window) {
|
||||
let next_idx = if self.containers().is_empty() {
|
||||
let next_idx = if let Some(idx) = self.preselected_container_idx {
|
||||
let next = idx;
|
||||
|
||||
self.preselected_container_idx = None;
|
||||
|
||||
next
|
||||
} else if self.containers().is_empty() {
|
||||
0
|
||||
} else {
|
||||
self.focused_container_idx() + 1
|
||||
|
||||
@@ -151,6 +151,7 @@ macro_rules! gen_enum_subcommand_args {
|
||||
gen_enum_subcommand_args! {
|
||||
Focus: OperationDirection,
|
||||
Move: OperationDirection,
|
||||
PreselectDirection: OperationDirection,
|
||||
CycleFocus: CycleDirection,
|
||||
CycleMove: CycleDirection,
|
||||
CycleMoveToWorkspace: CycleDirection,
|
||||
@@ -1095,6 +1096,9 @@ enum SubCommand {
|
||||
/// Move the focused window in the specified direction
|
||||
#[clap(arg_required_else_help = true)]
|
||||
Move(Move),
|
||||
/// Preselect the specified direction for the next window to be spawned on supported layouts
|
||||
#[clap(arg_required_else_help = true)]
|
||||
PreselectDirection(PreselectDirection),
|
||||
/// Minimize the focused window
|
||||
Minimize,
|
||||
/// Close the focused window
|
||||
@@ -2038,6 +2042,9 @@ fn main() -> eyre::Result<()> {
|
||||
SubCommand::Move(arg) => {
|
||||
send_message(&SocketMessage::MoveWindow(arg.operation_direction))?;
|
||||
}
|
||||
SubCommand::PreselectDirection(arg) => {
|
||||
send_message(&SocketMessage::PreselectDirection(arg.operation_direction))?;
|
||||
}
|
||||
SubCommand::CycleFocus(arg) => {
|
||||
send_message(&SocketMessage::CycleFocusWindow(arg.cycle_direction))?;
|
||||
}
|
||||
|
||||
@@ -111,6 +111,7 @@ nav:
|
||||
- cli/load-resize.md
|
||||
- cli/focus.md
|
||||
- cli/move.md
|
||||
- cli/preselect-direction.md
|
||||
- cli/minimize.md
|
||||
- cli/close.md
|
||||
- cli/force-focus.md
|
||||
|
||||
Reference in New Issue
Block a user