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:
JustForFun88
2025-06-15 13:58:33 +05:00
committed by LGUG2Z
parent f6ccec9505
commit 6416c0b6eb
10 changed files with 462 additions and 381 deletions

View File

@@ -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());
}
}

View File

@@ -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

View File

@@ -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());
}
}

View File

@@ -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);

View 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);
}
}

View File

@@ -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
}
}
}

View File

@@ -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)?,

View File

@@ -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 {

View File

@@ -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());
}
}

View File

@@ -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);