mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-03-21 00:49:25 +01:00
refactor(wm): move 'locked' flag down to containers
Key Changes - Added `locked: bool` field directly to the `Container` struct. - Removed `locked_containers` from `Workspace`. - Updated `komorebi_bar` to access `locked` directly from `Container`. Insert and swap operations respects `locked` container indexes in the sequence
This commit is contained in:
@@ -846,11 +846,12 @@ impl KomorebiNotificationState {
|
||||
}
|
||||
|
||||
let focused_workspace = &monitor.workspaces()[focused_workspace_idx];
|
||||
let is_focused = focused_workspace
|
||||
.locked_containers()
|
||||
.contains(&focused_workspace.focused_container_idx());
|
||||
let is_locked = match focused_workspace.focused_container() {
|
||||
Some(container) => container.locked(),
|
||||
None => false,
|
||||
};
|
||||
|
||||
self.focused_container_information = (is_focused, focused_workspace.into());
|
||||
self.focused_container_information = (is_locked, focused_workspace.into());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -255,7 +255,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
|
||||
let window_kind = if idx != ws.focused_container_idx()
|
||||
|| monitor_idx != focused_monitor_idx
|
||||
{
|
||||
if ws.locked_containers().contains(&idx) {
|
||||
if c.locked() {
|
||||
WindowKind::UnfocusedLocked
|
||||
} else {
|
||||
WindowKind::Unfocused
|
||||
@@ -563,7 +563,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
|
||||
|| monitor_idx != focused_monitor_idx
|
||||
|| focused_window_hwnd != foreground_window
|
||||
{
|
||||
if ws.locked_containers().contains(&idx) {
|
||||
if c.locked() {
|
||||
WindowKind::UnfocusedLocked
|
||||
} else {
|
||||
WindowKind::Unfocused
|
||||
|
||||
@@ -1,18 +1,24 @@
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use getset::CopyGetters;
|
||||
use getset::Getters;
|
||||
use getset::Setters;
|
||||
use nanoid::nanoid;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::ring::Ring;
|
||||
use crate::window::Window;
|
||||
use crate::Lockable;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Getters)]
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Getters, CopyGetters, Setters)]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub struct Container {
|
||||
#[getset(get = "pub")]
|
||||
id: String,
|
||||
#[serde(default)]
|
||||
#[getset(get_copy = "pub", set = "pub")]
|
||||
locked: bool,
|
||||
windows: Ring<Window>,
|
||||
}
|
||||
|
||||
@@ -22,11 +28,23 @@ impl Default for Container {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
id: nanoid!(),
|
||||
locked: false,
|
||||
windows: Ring::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Lockable for Container {
|
||||
fn locked(&self) -> bool {
|
||||
self.locked
|
||||
}
|
||||
|
||||
fn set_locked(&mut self, locked: bool) -> &mut Self {
|
||||
self.locked = locked;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Container {
|
||||
pub fn hide(&self, omit: Option<isize>) {
|
||||
for window in self.windows().iter().rev() {
|
||||
@@ -144,6 +162,7 @@ impl Container {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde_json;
|
||||
|
||||
#[test]
|
||||
fn test_contains_window() {
|
||||
@@ -250,4 +269,40 @@ mod tests {
|
||||
// Should return None since window 4 doesn't exist
|
||||
assert_eq!(container.idx_for_window(4), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserializes_with_missing_locked_field_defaults_to_false() {
|
||||
let json = r#"{
|
||||
"id": "test-1",
|
||||
"windows": { "elements": [], "focused": 0 }
|
||||
}"#;
|
||||
let container: Container = serde_json::from_str(json).expect("Should deserialize");
|
||||
|
||||
assert!(!container.locked());
|
||||
assert_eq!(container.id(), "test-1");
|
||||
assert!(container.windows().is_empty());
|
||||
|
||||
let json = r#"{
|
||||
"id": "test-2",
|
||||
"windows": { "elements": [ { "hwnd": 5 }, { "hwnd": 9 } ], "focused": 1 }
|
||||
}"#;
|
||||
let container: Container = serde_json::from_str(json).unwrap();
|
||||
assert_eq!(container.id(), "test-2");
|
||||
assert!(!container.locked());
|
||||
assert_eq!(container.windows(), &[Window::from(5), Window::from(9)]);
|
||||
assert_eq!(container.focused_window_idx(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serializes_and_deserializes() {
|
||||
let mut container = Container::default();
|
||||
container.set_locked(true);
|
||||
|
||||
let serialized = serde_json::to_string(&container).expect("Should serialize");
|
||||
let deserialized: Container =
|
||||
serde_json::from_str(&serialized).expect("Should deserialize");
|
||||
|
||||
assert_eq!(deserialized.locked(), true);
|
||||
assert_eq!(deserialized.id(), container.id());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ pub mod ring;
|
||||
pub mod container;
|
||||
pub mod core;
|
||||
pub mod focus_manager;
|
||||
pub mod locked_deque;
|
||||
pub mod lockable_sequence;
|
||||
pub mod monitor;
|
||||
pub mod monitor_reconciliator;
|
||||
pub mod process_command;
|
||||
@@ -255,6 +255,14 @@ pub static WINDOW_HANDLING_BEHAVIOUR: AtomicCell<WindowHandlingBehaviour> =
|
||||
|
||||
shadow_rs::shadow!(build);
|
||||
|
||||
/// A trait for types that can be marked as locked or unlocked.
|
||||
pub trait Lockable {
|
||||
/// Returns `true` if the item is locked.
|
||||
fn locked(&self) -> bool;
|
||||
/// Sets the locked state of the item.
|
||||
fn set_locked(&mut self, locked: bool) -> &mut Self;
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn current_virtual_desktop() -> Option<Vec<u8>> {
|
||||
let hkcu = RegKey::predef(HKEY_CURRENT_USER);
|
||||
|
||||
357
komorebi/src/lockable_sequence.rs
Normal file
357
komorebi/src/lockable_sequence.rs
Normal file
@@ -0,0 +1,357 @@
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use crate::Lockable;
|
||||
|
||||
/// A sequence supporting insertion, removal, and swapping of elements while preserving the absolute
|
||||
/// positions of locked items.
|
||||
pub trait LockableSequence<T: Lockable> {
|
||||
/// Inserts a value at `idx`, keeping locked elements at their absolute positions.
|
||||
fn insert_respecting_locks(&mut self, idx: usize, value: T) -> usize;
|
||||
/// Removes the element at `idx`, keeping locked elements at their absolute positions.
|
||||
fn remove_respecting_locks(&mut self, idx: usize) -> Option<T>;
|
||||
/// Swaps the elements at indices `i` and `j`, keeping locked elements at their absolute positions.
|
||||
fn swap_respecting_locks(&mut self, i: usize, j: usize);
|
||||
}
|
||||
|
||||
impl<T: Lockable> LockableSequence<T> for VecDeque<T> {
|
||||
/// Insert `value` at logical index `idx`, with trying to keep locked elements
|
||||
/// (`is_locked()`) anchored at their original positions.
|
||||
///
|
||||
/// Returns the final index of the inserted element.
|
||||
fn insert_respecting_locks(&mut self, mut idx: usize, value: T) -> usize {
|
||||
// 1. Bounds check: if index is out of range, simply append.
|
||||
if idx >= self.len() {
|
||||
self.push_back(value);
|
||||
return self.len() - 1; // last index
|
||||
}
|
||||
|
||||
// 2. Normal VecDeque insertion
|
||||
self.insert(idx, value);
|
||||
|
||||
// 3. Walk left-to-right once, swapping any misplaced locked element. After
|
||||
// the VecDeque::insert all items after `idx` have moved right by one. For every locked
|
||||
// element that is now to the right of an unlocked one, swap it back left exactly once.
|
||||
for index in (idx + 1)..self.len() {
|
||||
if self[index].locked() && !self[index - 1].locked() {
|
||||
self.swap(index - 1, index);
|
||||
|
||||
// If the element we just inserted participated in the swap,
|
||||
// update `idx` so we can return its final location.
|
||||
if idx == index - 1 {
|
||||
idx = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
idx
|
||||
}
|
||||
|
||||
/// Remove element at `idx`, with trying to keep locked elements
|
||||
/// (`is_locked()`) anchored at their original positions.
|
||||
///
|
||||
/// Returns the removed element, or `None` if `idx` is out of bounds.
|
||||
fn remove_respecting_locks(&mut self, idx: usize) -> Option<T> {
|
||||
// 1. Bounds check: if index is out of range, do nothing.
|
||||
if idx >= self.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// 2. Remove the element at the requested index.
|
||||
// All elements after idx are now shifted left by 1.
|
||||
let removed = self.remove(idx)?;
|
||||
|
||||
// 3. If less than 2 elements remain, nothing to shift.
|
||||
if self.len() < 2 {
|
||||
return Some(removed);
|
||||
}
|
||||
|
||||
// 4. Iterate from the element just after the removed spot up to the second-to-last
|
||||
// element, right-to-left. This loop "fixes" locked elements that were shifted left
|
||||
// off their anchored positions: If a locked element now has an unlocked element
|
||||
// to its right, swap them back to restore locked order.
|
||||
for index in (idx..self.len() - 1).rev() {
|
||||
// If current is locked and the next one is not locked, swap them.
|
||||
if self[index].locked() && !self[index + 1].locked() {
|
||||
self.swap(index, index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Return the removed value.
|
||||
Some(removed)
|
||||
}
|
||||
|
||||
/// Swaps the elements at indices `i` and `j`, along with their `locked` status, ensuring
|
||||
/// the lock state remains associated with the position rather than the element itself.
|
||||
fn swap_respecting_locks(&mut self, i: usize, j: usize) {
|
||||
self.swap(i, j);
|
||||
let locked_i = self[i].locked();
|
||||
let locked_j = self[j].locked();
|
||||
self[i].set_locked(locked_j);
|
||||
self[j].set_locked(locked_i);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct TestItem {
|
||||
val: i32,
|
||||
locked: bool,
|
||||
}
|
||||
|
||||
impl Lockable for TestItem {
|
||||
fn locked(&self) -> bool {
|
||||
self.locked
|
||||
}
|
||||
|
||||
fn set_locked(&mut self, locked: bool) -> &mut Self {
|
||||
self.locked = locked;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
fn vals(v: &VecDeque<TestItem>) -> Vec<i32> {
|
||||
v.iter().map(|x| x.val).collect()
|
||||
}
|
||||
|
||||
fn test_deque(items: &[(i32, bool)]) -> VecDeque<TestItem> {
|
||||
items
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|(val, locked)| TestItem { val, locked })
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_insert_respecting_locks() {
|
||||
// Test case 1: Basic insertion with locked index
|
||||
{
|
||||
// Lock index 2
|
||||
let mut ring = test_deque(&[(0, false), (1, false), (2, true), (3, false), (4, false)]);
|
||||
// Insert at index 0, should shift elements while keeping index 2 locked
|
||||
ring.insert_respecting_locks(
|
||||
0,
|
||||
TestItem {
|
||||
val: 99,
|
||||
locked: false,
|
||||
},
|
||||
);
|
||||
// Element '2' remains at index 2, element '1' that was at index 1 is now at index 3
|
||||
assert_eq!(vals(&ring), vec![99, 0, 2, 1, 3, 4]);
|
||||
}
|
||||
|
||||
// Test case 2: Insert at a locked index (should insert after locked)
|
||||
{
|
||||
// Lock index 2
|
||||
let mut ring = test_deque(&[(0, false), (1, false), (2, true), (3, false), (4, false)]);
|
||||
// Try to insert at locked index 2, should insert at index 3 instead
|
||||
let actual_index = ring.insert_respecting_locks(
|
||||
2,
|
||||
TestItem {
|
||||
val: 99,
|
||||
locked: false,
|
||||
},
|
||||
);
|
||||
assert_eq!(actual_index, 3);
|
||||
assert_eq!(vals(&ring), vec![0, 1, 2, 99, 3, 4]);
|
||||
}
|
||||
|
||||
// Test case 3: Multiple locked indices
|
||||
{
|
||||
// Lock index 1 and 3
|
||||
let mut ring = test_deque(&[(0, false), (1, true), (2, false), (3, true), (4, false)]);
|
||||
// Insert at index 0, should maintain locked indices
|
||||
ring.insert_respecting_locks(
|
||||
0,
|
||||
TestItem {
|
||||
val: 99,
|
||||
locked: false,
|
||||
},
|
||||
);
|
||||
// Elements '1' and '3' remain at indices 1 and 3
|
||||
assert_eq!(vals(&ring), vec![99, 1, 0, 3, 2, 4]);
|
||||
}
|
||||
|
||||
// Test case 4: Insert at end
|
||||
{
|
||||
// Lock index 2
|
||||
let mut ring = test_deque(&[(0, false), (1, false), (2, true), (3, false), (4, false)]);
|
||||
let actual_index = ring.insert_respecting_locks(
|
||||
5,
|
||||
TestItem {
|
||||
val: 99,
|
||||
locked: false,
|
||||
},
|
||||
);
|
||||
assert_eq!(actual_index, 5);
|
||||
assert_eq!(vals(&ring), vec![0, 1, 2, 3, 4, 99]);
|
||||
}
|
||||
|
||||
// Test case 5: Empty ring
|
||||
{
|
||||
let mut ring = test_deque(&[]);
|
||||
// Insert into empty deque
|
||||
let actual_index = ring.insert_respecting_locks(
|
||||
0,
|
||||
TestItem {
|
||||
val: 99,
|
||||
locked: false,
|
||||
},
|
||||
);
|
||||
assert_eq!(actual_index, 0);
|
||||
assert_eq!(vals(&ring), vec![99]);
|
||||
}
|
||||
|
||||
// Test case 6: All indices locked
|
||||
{
|
||||
// Lock all indices
|
||||
let mut ring = test_deque(&[(0, true), (1, true), (2, true), (3, true), (4, true)]);
|
||||
// Try to insert at index 2, should insert at the end
|
||||
let actual_index = ring.insert_respecting_locks(
|
||||
2,
|
||||
TestItem {
|
||||
val: 99,
|
||||
locked: false,
|
||||
},
|
||||
);
|
||||
assert_eq!(actual_index, 5);
|
||||
assert_eq!(vals(&ring), vec![0, 1, 2, 3, 4, 99]);
|
||||
}
|
||||
|
||||
// Test case 7: Consecutive locked indices
|
||||
{
|
||||
// Lock index 2 and 3
|
||||
let mut ring = test_deque(&[(0, false), (1, false), (2, true), (3, true), (4, false)]);
|
||||
// Insert at index 1, should maintain consecutive locked indices
|
||||
ring.insert_respecting_locks(
|
||||
1,
|
||||
TestItem {
|
||||
val: 99,
|
||||
locked: false,
|
||||
},
|
||||
);
|
||||
// Elements '2' and '3' remain at indices 2 and 3
|
||||
assert_eq!(vals(&ring), vec![0, 99, 2, 3, 1, 4]);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_remove_respecting_locks() {
|
||||
// Test case 1: Remove a non-locked index before a locked index
|
||||
{
|
||||
// Lock index 2
|
||||
let mut ring = test_deque(&[(0, false), (1, false), (2, true), (3, false), (4, false)]);
|
||||
let removed = ring.remove_respecting_locks(0);
|
||||
assert_eq!(removed.map(|x| x.val), Some(0));
|
||||
// Elements '2' remain at index 2
|
||||
assert_eq!(vals(&ring), vec![1, 3, 2, 4]);
|
||||
}
|
||||
|
||||
// Test case 2: Remove a locked index
|
||||
{
|
||||
// Lock index 2
|
||||
let mut ring = test_deque(&[(0, false), (1, false), (2, true), (3, false), (4, false)]);
|
||||
let removed = ring.remove_respecting_locks(2);
|
||||
assert_eq!(removed.map(|x| x.val), Some(2));
|
||||
// Elements should stay at the same places
|
||||
assert_eq!(vals(&ring), vec![0, 1, 3, 4]);
|
||||
}
|
||||
|
||||
// Test case 3: Remove an index after a locked index
|
||||
{
|
||||
// Lock index 1
|
||||
let mut ring = test_deque(&[(0, false), (1, true), (2, false), (3, false), (4, false)]);
|
||||
let removed = ring.remove_respecting_locks(3);
|
||||
assert_eq!(removed.map(|x| x.val), Some(3));
|
||||
// Elements should stay at the same places
|
||||
assert_eq!(vals(&ring), vec![0, 1, 2, 4]);
|
||||
}
|
||||
|
||||
// Test case 4: Multiple locked indices
|
||||
{
|
||||
// Lock index 1 and 3
|
||||
let mut ring = test_deque(&[(0, false), (1, true), (2, false), (3, true), (4, false)]);
|
||||
let removed = ring.remove_respecting_locks(0);
|
||||
assert_eq!(removed.map(|x| x.val), Some(0));
|
||||
// Elements '1' and '3' remain at indices '1' and '3'
|
||||
assert_eq!(vals(&ring), vec![2, 1, 4, 3]);
|
||||
}
|
||||
|
||||
// Test case 5: Remove the last element
|
||||
{
|
||||
// Lock index 2
|
||||
let mut ring = test_deque(&[(0, false), (1, false), (2, true), (3, false), (4, false)]);
|
||||
let removed = ring.remove_respecting_locks(4);
|
||||
assert_eq!(removed.map(|x| x.val), Some(4));
|
||||
// Index 2 should still be at the same place
|
||||
assert_eq!(vals(&ring), vec![0, 1, 2, 3]);
|
||||
}
|
||||
|
||||
// Test case 6: Invalid index
|
||||
{
|
||||
// Lock index 2
|
||||
let mut ring = test_deque(&[(0, false), (1, false), (2, true), (3, false), (4, false)]);
|
||||
let removed = ring.remove_respecting_locks(10);
|
||||
assert_eq!(removed, None);
|
||||
// Deque unchanged
|
||||
assert_eq!(vals(&ring), vec![0, 1, 2, 3, 4]);
|
||||
}
|
||||
|
||||
// Test case 7: Remove enough elements to make a locked index invalid
|
||||
{
|
||||
// Lock index 2
|
||||
let mut ring = test_deque(&[(0, false), (1, false), (2, true)]);
|
||||
ring.remove_respecting_locks(0);
|
||||
// Index 2 should now be '1'
|
||||
assert_eq!(vals(&ring), vec![1, 2]);
|
||||
}
|
||||
|
||||
// Test case 8: Removing an element before multiple locked indices
|
||||
{
|
||||
// Lock index 2 and 4
|
||||
let mut ring = test_deque(&[
|
||||
(0, false),
|
||||
(1, false),
|
||||
(2, true),
|
||||
(3, false),
|
||||
(4, true),
|
||||
(5, false),
|
||||
]);
|
||||
let removed = ring.remove_respecting_locks(1);
|
||||
assert_eq!(removed.map(|x| x.val), Some(1));
|
||||
// Both indices should still be at the same place
|
||||
assert_eq!(vals(&ring), vec![0, 3, 2, 5, 4]);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_swap_respecting_locks_various_cases() {
|
||||
// Swap unlocked and locked
|
||||
let mut ring = test_deque(&[(0, false), (1, true), (2, false), (3, false)]);
|
||||
ring.swap_respecting_locks(0, 1);
|
||||
assert_eq!(vals(&ring), vec![1, 0, 2, 3]);
|
||||
assert_eq!(ring[0].locked, false);
|
||||
assert_eq!(ring[1].locked, true);
|
||||
ring.swap_respecting_locks(0, 1);
|
||||
assert_eq!(vals(&ring), vec![0, 1, 2, 3]);
|
||||
assert_eq!(ring[0].locked, false);
|
||||
assert_eq!(ring[1].locked, true);
|
||||
|
||||
// Both locked
|
||||
let mut ring = test_deque(&[(0, true), (1, false), (2, true)]);
|
||||
ring.swap_respecting_locks(0, 2);
|
||||
assert_eq!(vals(&ring), vec![2, 1, 0]);
|
||||
assert!(ring[0].locked);
|
||||
assert!(!ring[1].locked);
|
||||
assert!(ring[2].locked);
|
||||
|
||||
// Both unlocked
|
||||
let mut ring = test_deque(&[(0, false), (1, true), (2, false)]);
|
||||
ring.swap_respecting_locks(0, 2);
|
||||
assert_eq!(vals(&ring), vec![2, 1, 0]);
|
||||
assert!(!ring[0].locked);
|
||||
assert!(ring[1].locked);
|
||||
assert!(!ring[2].locked);
|
||||
}
|
||||
}
|
||||
@@ -1,316 +0,0 @@
|
||||
use std::collections::BTreeSet;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
pub struct LockedDeque<'a, T> {
|
||||
deque: &'a mut VecDeque<T>,
|
||||
locked_indices: &'a mut BTreeSet<usize>,
|
||||
}
|
||||
|
||||
impl<'a, T: PartialEq> LockedDeque<'a, T> {
|
||||
pub fn new(deque: &'a mut VecDeque<T>, locked_indices: &'a mut BTreeSet<usize>) -> Self {
|
||||
Self {
|
||||
deque,
|
||||
locked_indices,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, index: usize, value: T) -> usize {
|
||||
insert_respecting_locks(self.deque, self.locked_indices, index, value)
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, index: usize) -> Option<T> {
|
||||
remove_respecting_locks(self.deque, self.locked_indices, index)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_respecting_locks<T>(
|
||||
deque: &mut VecDeque<T>,
|
||||
locked_idx: &mut BTreeSet<usize>,
|
||||
idx: usize,
|
||||
value: T,
|
||||
) -> usize {
|
||||
if idx == deque.len() {
|
||||
deque.push_back(value);
|
||||
return idx;
|
||||
}
|
||||
|
||||
let mut new_deque = VecDeque::with_capacity(deque.len() + 1);
|
||||
let mut temp_locked_deque = VecDeque::new();
|
||||
let mut j = 0;
|
||||
let mut corrected_idx = idx;
|
||||
|
||||
for (i, el) in deque.drain(..).enumerate() {
|
||||
if i == idx {
|
||||
corrected_idx = j;
|
||||
}
|
||||
if locked_idx.contains(&i) {
|
||||
temp_locked_deque.push_back(el);
|
||||
} else {
|
||||
new_deque.push_back(el);
|
||||
j += 1;
|
||||
}
|
||||
}
|
||||
|
||||
new_deque.insert(corrected_idx, value);
|
||||
|
||||
for (locked_el, locked_idx) in temp_locked_deque.into_iter().zip(locked_idx.iter()) {
|
||||
new_deque.insert(*locked_idx, locked_el);
|
||||
if *locked_idx <= corrected_idx {
|
||||
corrected_idx += 1;
|
||||
}
|
||||
}
|
||||
|
||||
*deque = new_deque;
|
||||
|
||||
corrected_idx
|
||||
}
|
||||
|
||||
pub fn remove_respecting_locks<T>(
|
||||
deque: &mut VecDeque<T>,
|
||||
locked_idx: &mut BTreeSet<usize>,
|
||||
idx: usize,
|
||||
) -> Option<T> {
|
||||
if idx >= deque.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let final_size = deque.len() - 1;
|
||||
|
||||
let mut new_deque = VecDeque::with_capacity(final_size);
|
||||
let mut temp_locked_deque = VecDeque::new();
|
||||
let mut removed = None;
|
||||
let mut removed_locked_idx = None;
|
||||
|
||||
for (i, el) in deque.drain(..).enumerate() {
|
||||
if i == idx {
|
||||
removed = Some(el);
|
||||
removed_locked_idx = locked_idx.contains(&i).then_some(i);
|
||||
} else if locked_idx.contains(&i) {
|
||||
temp_locked_deque.push_back(el);
|
||||
} else {
|
||||
new_deque.push_back(el);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(i) = removed_locked_idx {
|
||||
let mut above = locked_idx.split_off(&i);
|
||||
above.pop_first();
|
||||
locked_idx.extend(above.into_iter().map(|i| i - 1));
|
||||
}
|
||||
|
||||
while locked_idx.last().is_some_and(|i| *i >= final_size) {
|
||||
locked_idx.pop_last();
|
||||
}
|
||||
|
||||
let extra_invalid_idx = (new_deque.len()
|
||||
..(new_deque.len() + temp_locked_deque.len() - locked_idx.len()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for (locked_el, locked_idx) in temp_locked_deque
|
||||
.into_iter()
|
||||
.zip(locked_idx.iter().chain(extra_invalid_idx.iter()))
|
||||
{
|
||||
new_deque.insert(*locked_idx, locked_el);
|
||||
}
|
||||
|
||||
*deque = new_deque;
|
||||
|
||||
removed
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::collections::BTreeSet;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
#[test]
|
||||
fn test_insert_respecting_locks() {
|
||||
// Test case 1: Basic insertion with locked index
|
||||
{
|
||||
let mut deque = VecDeque::from(vec![0, 1, 2, 3, 4]);
|
||||
let mut locked = BTreeSet::new();
|
||||
locked.insert(2); // Lock index 2
|
||||
|
||||
// Insert at index 0, should shift elements while keeping index 2 locked
|
||||
insert_respecting_locks(&mut deque, &mut locked, 0, 99);
|
||||
assert_eq!(deque, VecDeque::from(vec![99, 0, 2, 1, 3, 4]));
|
||||
// Element '2' remains at index 2, element '1' that was at index 1 is now at index 3
|
||||
}
|
||||
|
||||
// Test case 2: Insert at a locked index
|
||||
{
|
||||
let mut deque = VecDeque::from(vec![0, 1, 2, 3, 4]);
|
||||
let mut locked = BTreeSet::new();
|
||||
locked.insert(2); // Lock index 2
|
||||
|
||||
// Try to insert at locked index 2, should insert at index 3 instead
|
||||
let actual_index = insert_respecting_locks(&mut deque, &mut locked, 2, 99);
|
||||
assert_eq!(actual_index, 3);
|
||||
assert_eq!(deque, VecDeque::from(vec![0, 1, 2, 99, 3, 4]));
|
||||
}
|
||||
|
||||
// Test case 3: Multiple locked indices
|
||||
{
|
||||
let mut deque = VecDeque::from(vec![0, 1, 2, 3, 4]);
|
||||
let mut locked = BTreeSet::new();
|
||||
locked.insert(1); // Lock index 1
|
||||
locked.insert(3); // Lock index 3
|
||||
|
||||
// Insert at index 0, should maintain locked indices
|
||||
insert_respecting_locks(&mut deque, &mut locked, 0, 99);
|
||||
assert_eq!(deque, VecDeque::from(vec![99, 1, 0, 3, 2, 4]));
|
||||
// Elements '1' and '3' remain at indices 1 and 3
|
||||
}
|
||||
|
||||
// Test case 4: Insert at end
|
||||
{
|
||||
let mut deque = VecDeque::from(vec![0, 1, 2, 3, 4]);
|
||||
let mut locked = BTreeSet::new();
|
||||
locked.insert(2); // Lock index 2
|
||||
|
||||
// Insert at end of deque
|
||||
let actual_index = insert_respecting_locks(&mut deque, &mut locked, 5, 99);
|
||||
assert_eq!(actual_index, 5);
|
||||
assert_eq!(deque, VecDeque::from(vec![0, 1, 2, 3, 4, 99]));
|
||||
}
|
||||
|
||||
// Test case 5: Empty deque
|
||||
{
|
||||
let mut deque = VecDeque::new();
|
||||
let mut locked = BTreeSet::new();
|
||||
|
||||
// Insert into empty deque
|
||||
let actual_index = insert_respecting_locks(&mut deque, &mut locked, 0, 99);
|
||||
assert_eq!(actual_index, 0);
|
||||
assert_eq!(deque, VecDeque::from(vec![99]));
|
||||
}
|
||||
|
||||
// Test case 6: All indices locked
|
||||
{
|
||||
let mut deque = VecDeque::from(vec![0, 1, 2, 3, 4]);
|
||||
let mut locked = BTreeSet::new();
|
||||
for i in 0..5 {
|
||||
locked.insert(i); // Lock all indices
|
||||
}
|
||||
|
||||
// Try to insert at index 2, should insert at the end
|
||||
let actual_index = insert_respecting_locks(&mut deque, &mut locked, 2, 99);
|
||||
assert_eq!(actual_index, 5);
|
||||
assert_eq!(deque, VecDeque::from(vec![0, 1, 2, 3, 4, 99]));
|
||||
}
|
||||
|
||||
// Test case 7: Consecutive locked indices
|
||||
{
|
||||
let mut deque = VecDeque::from(vec![0, 1, 2, 3, 4]);
|
||||
let mut locked = BTreeSet::new();
|
||||
locked.insert(2); // Lock index 2
|
||||
locked.insert(3); // Lock index 3
|
||||
|
||||
// Insert at index 1, should maintain consecutive locked indices
|
||||
insert_respecting_locks(&mut deque, &mut locked, 1, 99);
|
||||
assert_eq!(deque, VecDeque::from(vec![0, 99, 2, 3, 1, 4]));
|
||||
// Elements '2' and '3' remain at indices 2 and 3
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_remove_respecting_locks() {
|
||||
// Test case 1: Remove a non-locked index before a locked index
|
||||
{
|
||||
let mut deque = VecDeque::from(vec![0, 1, 2, 3, 4]);
|
||||
let mut locked = BTreeSet::new();
|
||||
locked.insert(2); // Lock index 2
|
||||
|
||||
let removed = remove_respecting_locks(&mut deque, &mut locked, 0);
|
||||
assert_eq!(removed, Some(0));
|
||||
assert_eq!(deque, VecDeque::from(vec![1, 3, 2, 4]));
|
||||
assert!(locked.contains(&2)); // Index 2 should still be locked
|
||||
}
|
||||
|
||||
// Test case 2: Remove a locked index
|
||||
{
|
||||
let mut deque = VecDeque::from(vec![0, 1, 2, 3, 4]);
|
||||
let mut locked = BTreeSet::new();
|
||||
locked.insert(2); // Lock index 2
|
||||
|
||||
let removed = remove_respecting_locks(&mut deque, &mut locked, 2);
|
||||
assert_eq!(removed, Some(2));
|
||||
assert_eq!(deque, VecDeque::from(vec![0, 1, 3, 4]));
|
||||
assert!(!locked.contains(&2)); // Index 2 should be unlocked
|
||||
}
|
||||
|
||||
// Test case 3: Remove an index after a locked index
|
||||
{
|
||||
let mut deque = VecDeque::from(vec![0, 1, 2, 3, 4]);
|
||||
let mut locked = BTreeSet::new();
|
||||
locked.insert(1); // Lock index 1
|
||||
|
||||
let removed = remove_respecting_locks(&mut deque, &mut locked, 3);
|
||||
assert_eq!(removed, Some(3));
|
||||
assert_eq!(deque, VecDeque::from(vec![0, 1, 2, 4]));
|
||||
assert!(locked.contains(&1)); // Index 1 should still be locked
|
||||
}
|
||||
|
||||
// Test case 4: Multiple locked indices
|
||||
{
|
||||
let mut deque = VecDeque::from(vec![0, 1, 2, 3, 4]);
|
||||
let mut locked = BTreeSet::new();
|
||||
locked.insert(1); // Lock index 1
|
||||
locked.insert(3); // Lock index 3
|
||||
|
||||
let removed = remove_respecting_locks(&mut deque, &mut locked, 0);
|
||||
assert_eq!(removed, Some(0));
|
||||
assert_eq!(deque, VecDeque::from(vec![2, 1, 4, 3]));
|
||||
assert!(locked.contains(&1) && locked.contains(&3)); // Both indices should still be locked
|
||||
}
|
||||
|
||||
// Test case 5: Remove the last element
|
||||
{
|
||||
let mut deque = VecDeque::from(vec![0, 1, 2, 3, 4]);
|
||||
let mut locked = BTreeSet::new();
|
||||
locked.insert(2); // Lock index 2
|
||||
|
||||
let removed = remove_respecting_locks(&mut deque, &mut locked, 4);
|
||||
assert_eq!(removed, Some(4));
|
||||
assert_eq!(deque, VecDeque::from(vec![0, 1, 2, 3]));
|
||||
assert!(locked.contains(&2)); // Index 2 should still be locked
|
||||
}
|
||||
|
||||
// Test case 6: Invalid index
|
||||
{
|
||||
let mut deque = VecDeque::from(vec![0, 1, 2, 3, 4]);
|
||||
let mut locked = BTreeSet::new();
|
||||
locked.insert(2); // Lock index 2
|
||||
|
||||
let removed = remove_respecting_locks(&mut deque, &mut locked, 10);
|
||||
assert_eq!(removed, None);
|
||||
assert_eq!(deque, VecDeque::from(vec![0, 1, 2, 3, 4])); // Deque unchanged
|
||||
assert!(locked.contains(&2)); // Lock unchanged
|
||||
}
|
||||
|
||||
// Test case 7: Remove enough elements to make a locked index invalid
|
||||
{
|
||||
let mut deque = VecDeque::from(vec![0, 1, 2]);
|
||||
let mut locked = BTreeSet::new();
|
||||
locked.insert(2); // Lock index 2
|
||||
|
||||
remove_respecting_locks(&mut deque, &mut locked, 0);
|
||||
assert_eq!(deque, VecDeque::from(vec![1, 2]));
|
||||
assert!(!locked.contains(&2)); // Index 2 should now be invalid
|
||||
}
|
||||
|
||||
// Test case 8: Removing an element before multiple locked indices
|
||||
{
|
||||
let mut deque = VecDeque::from(vec![0, 1, 2, 3, 4, 5]);
|
||||
let mut locked = BTreeSet::new();
|
||||
locked.insert(2); // Lock index 2
|
||||
locked.insert(4); // Lock index 4
|
||||
|
||||
let removed = remove_respecting_locks(&mut deque, &mut locked, 1);
|
||||
assert_eq!(removed, Some(1));
|
||||
assert_eq!(deque, VecDeque::from(vec![0, 3, 2, 5, 4]));
|
||||
assert!(locked.contains(&2) && locked.contains(&4)); // Both indices should still be locked
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -393,7 +393,9 @@ impl WindowManager {
|
||||
.get_mut(workspace_idx)
|
||||
.ok_or_eyre("no workspace at the given index")?;
|
||||
|
||||
workspace.locked_containers.insert(container_idx);
|
||||
if let Some(container) = workspace.containers_mut().get_mut(container_idx) {
|
||||
container.set_locked(true);
|
||||
}
|
||||
}
|
||||
SocketMessage::UnlockMonitorWorkspaceContainer(
|
||||
monitor_idx,
|
||||
@@ -410,7 +412,9 @@ impl WindowManager {
|
||||
.get_mut(workspace_idx)
|
||||
.ok_or_eyre("no workspace at the given index")?;
|
||||
|
||||
workspace.locked_containers.remove(&container_idx);
|
||||
if let Some(container) = workspace.containers_mut().get_mut(container_idx) {
|
||||
container.set_locked(false);
|
||||
}
|
||||
}
|
||||
SocketMessage::ToggleLock => self.toggle_lock()?,
|
||||
SocketMessage::ToggleFloat => self.toggle_float(false)?,
|
||||
|
||||
@@ -43,10 +43,6 @@ impl<T> Ring<T> {
|
||||
pub fn focused_mut(&mut self) -> Option<&mut T> {
|
||||
self.elements.get_mut(self.focused)
|
||||
}
|
||||
|
||||
pub fn swap(&mut self, i: usize, j: usize) {
|
||||
self.elements.swap(i, j);
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_ring_elements {
|
||||
|
||||
@@ -347,7 +347,6 @@ impl From<&WindowManager> for State {
|
||||
layer: workspace.layer,
|
||||
floating_layer_behaviour: workspace.floating_layer_behaviour,
|
||||
globals: workspace.globals,
|
||||
locked_containers: workspace.locked_containers.clone(),
|
||||
wallpaper: workspace.wallpaper.clone(),
|
||||
workspace_config: None,
|
||||
})
|
||||
@@ -3192,14 +3191,10 @@ impl WindowManager {
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub fn toggle_lock(&mut self) -> Result<()> {
|
||||
let workspace = self.focused_workspace_mut()?;
|
||||
let index = workspace.focused_container_idx();
|
||||
|
||||
if workspace.locked_containers().contains(&index) {
|
||||
workspace.locked_containers_mut().remove(&index);
|
||||
} else {
|
||||
workspace.locked_containers_mut().insert(index);
|
||||
if let Some(container) = workspace.focused_container_mut() {
|
||||
// Toggle the locked flag
|
||||
container.set_locked(!container.locked());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -5375,7 +5370,8 @@ mod tests {
|
||||
{
|
||||
// Ensure container 2 is not locked
|
||||
let workspace = wm.focused_workspace_mut().unwrap();
|
||||
assert!(!workspace.locked_containers().contains(&2));
|
||||
assert_eq!(workspace.focused_container_idx(), 2);
|
||||
assert!(!workspace.focused_container().unwrap().locked());
|
||||
}
|
||||
|
||||
// Toggle lock on focused container
|
||||
@@ -5384,7 +5380,7 @@ mod tests {
|
||||
{
|
||||
// Ensure container 2 is locked
|
||||
let workspace = wm.focused_workspace_mut().unwrap();
|
||||
assert!(workspace.locked_containers().contains(&2));
|
||||
assert!(workspace.focused_container().unwrap().locked());
|
||||
}
|
||||
|
||||
// Toggle lock on focused container
|
||||
@@ -5393,7 +5389,7 @@ mod tests {
|
||||
{
|
||||
// Ensure container 2 is not locked
|
||||
let workspace = wm.focused_workspace_mut().unwrap();
|
||||
assert!(!workspace.locked_containers().contains(&2));
|
||||
assert!(!workspace.focused_container().unwrap().locked());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use std::collections::BTreeSet;
|
||||
use std::collections::VecDeque;
|
||||
use std::ffi::OsStr;
|
||||
use std::fmt::Display;
|
||||
@@ -17,7 +16,7 @@ use crate::core::Layout;
|
||||
use crate::core::OperationDirection;
|
||||
use crate::core::Rect;
|
||||
use crate::default_layout::LayoutOptions;
|
||||
use crate::locked_deque::LockedDeque;
|
||||
use crate::lockable_sequence::LockableSequence;
|
||||
use crate::ring::Ring;
|
||||
use crate::should_act;
|
||||
use crate::stackbar_manager;
|
||||
@@ -103,8 +102,6 @@ pub struct Workspace {
|
||||
#[getset(get_copy = "pub", get_mut = "pub", set = "pub")]
|
||||
pub floating_layer_behaviour: Option<FloatingLayerBehaviour>,
|
||||
#[getset(get = "pub", get_mut = "pub", set = "pub")]
|
||||
pub locked_containers: BTreeSet<usize>,
|
||||
#[getset(get = "pub", get_mut = "pub", set = "pub")]
|
||||
pub wallpaper: Option<Wallpaper>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[getset(get = "pub", set = "pub")]
|
||||
@@ -158,7 +155,6 @@ impl Default for Workspace {
|
||||
floating_layer_behaviour: Default::default(),
|
||||
globals: Default::default(),
|
||||
workspace_config: None,
|
||||
locked_containers: Default::default(),
|
||||
wallpaper: None,
|
||||
}
|
||||
}
|
||||
@@ -898,10 +894,9 @@ impl Workspace {
|
||||
// this fn respects locked container indexes - we should use it for pretty much everything
|
||||
// except monocle and maximize toggles
|
||||
pub fn insert_container_at_idx(&mut self, idx: usize, container: Container) -> usize {
|
||||
let mut locked_containers = self.locked_containers().clone();
|
||||
let mut ld = LockedDeque::new(self.containers_mut(), &mut locked_containers);
|
||||
let insertion_idx = ld.insert(idx, container);
|
||||
self.locked_containers = locked_containers;
|
||||
let insertion_idx = self
|
||||
.containers_mut()
|
||||
.insert_respecting_locks(idx, container);
|
||||
|
||||
if insertion_idx > self.resize_dimensions().len() {
|
||||
self.resize_dimensions_mut().push(None);
|
||||
@@ -917,10 +912,7 @@ impl Workspace {
|
||||
// this fn respects locked container indexes - we should use it for pretty much everything
|
||||
// except monocle and maximize toggles
|
||||
pub fn remove_container_by_idx(&mut self, idx: usize) -> Option<Container> {
|
||||
let mut locked_containers = self.locked_containers().clone();
|
||||
let mut ld = LockedDeque::new(self.containers_mut(), &mut locked_containers);
|
||||
let container = ld.remove(idx);
|
||||
self.locked_containers = locked_containers;
|
||||
let container = self.containers_mut().remove_respecting_locks(idx);
|
||||
|
||||
if idx < self.resize_dimensions().len() {
|
||||
self.resize_dimensions_mut().remove(idx);
|
||||
@@ -1634,7 +1626,7 @@ impl Workspace {
|
||||
}
|
||||
|
||||
pub fn swap_containers(&mut self, i: usize, j: usize) {
|
||||
self.containers.swap(i, j);
|
||||
self.containers.elements_mut().swap_respecting_locks(i, j);
|
||||
self.focus_container(j);
|
||||
}
|
||||
|
||||
@@ -1733,7 +1725,6 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::container::Container;
|
||||
use crate::Window;
|
||||
use std::collections::BTreeSet;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[test]
|
||||
@@ -1741,20 +1732,18 @@ mod tests {
|
||||
let mut ws = Workspace::default();
|
||||
|
||||
let mut state = HashMap::new();
|
||||
let mut locked = BTreeSet::new();
|
||||
|
||||
// add 3 containers
|
||||
// add 4 containers
|
||||
for i in 0..4 {
|
||||
let container = Container::default();
|
||||
let mut container = Container::default();
|
||||
if i == 3 {
|
||||
container.set_locked(true); // set index 3 locked
|
||||
}
|
||||
state.insert(i, container.id().to_string());
|
||||
ws.add_container_to_back(container);
|
||||
}
|
||||
assert_eq!(ws.containers().len(), 4);
|
||||
|
||||
// set index 3 locked
|
||||
locked.insert(3);
|
||||
ws.locked_containers = locked;
|
||||
|
||||
// focus container at index 2
|
||||
ws.focus_container(2);
|
||||
|
||||
@@ -1786,20 +1775,17 @@ mod tests {
|
||||
fn test_locked_containers_remove_window() {
|
||||
let mut ws = Workspace::default();
|
||||
|
||||
let mut locked = BTreeSet::new();
|
||||
|
||||
// add 4 containers
|
||||
for i in 0..4 {
|
||||
let mut container = Container::default();
|
||||
container.windows_mut().push_back(Window::from(i));
|
||||
if i == 1 {
|
||||
container.set_locked(true);
|
||||
}
|
||||
ws.add_container_to_back(container);
|
||||
}
|
||||
assert_eq!(ws.containers().len(), 4);
|
||||
|
||||
// set index 1 locked
|
||||
locked.insert(1);
|
||||
ws.locked_containers = locked;
|
||||
|
||||
ws.remove_window(0).unwrap();
|
||||
assert_eq!(ws.containers()[0].focused_window().unwrap().hwnd, 2);
|
||||
// index 1 should still be the same
|
||||
@@ -1811,20 +1797,17 @@ mod tests {
|
||||
fn test_locked_containers_toggle_float() {
|
||||
let mut ws = Workspace::default();
|
||||
|
||||
let mut locked = BTreeSet::new();
|
||||
|
||||
// add 4 containers
|
||||
for i in 0..4 {
|
||||
let mut container = Container::default();
|
||||
container.windows_mut().push_back(Window::from(i));
|
||||
if i == 1 {
|
||||
container.set_locked(true);
|
||||
}
|
||||
ws.add_container_to_back(container);
|
||||
}
|
||||
assert_eq!(ws.containers().len(), 4);
|
||||
|
||||
// set index 1 locked
|
||||
locked.insert(1);
|
||||
ws.locked_containers = locked;
|
||||
|
||||
// set index 0 focused
|
||||
ws.focus_container(0);
|
||||
|
||||
@@ -1856,20 +1839,17 @@ mod tests {
|
||||
fn test_locked_containers_stack() {
|
||||
let mut ws = Workspace::default();
|
||||
|
||||
let mut locked = BTreeSet::new();
|
||||
|
||||
// add 6 containers
|
||||
for i in 0..6 {
|
||||
let mut container = Container::default();
|
||||
container.windows_mut().push_back(Window::from(i));
|
||||
if i == 4 {
|
||||
container.set_locked(true);
|
||||
}
|
||||
ws.add_container_to_back(container);
|
||||
}
|
||||
assert_eq!(ws.containers().len(), 6);
|
||||
|
||||
// set index 4 locked
|
||||
locked.insert(4);
|
||||
ws.locked_containers = locked;
|
||||
|
||||
// set index 3 focused
|
||||
ws.focus_container(3);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user