Skip to content

Commit 73c7c95

Browse files
committed
improv(stack): add tab model messages & refactor window add/remove operations
1 parent 2e73d09 commit 73c7c95

File tree

1 file changed

+129
-62
lines changed

1 file changed

+129
-62
lines changed

src/shell/element/stack.rs

Lines changed: 129 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,77 @@ pub struct CosmicStackInternal {
109109
}
110110

111111
impl CosmicStackInternal {
112+
pub fn set_previous_index(&self, moved_into: Option<&Seat<State>>) -> Option<usize> {
113+
let last_mod_serial = moved_into.and_then(|seat| seat.last_modifier_change());
114+
let mut prev_idx = self.previous_index.lock().unwrap();
115+
if !prev_idx.is_some_and(|(serial, _)| Some(serial) == last_mod_serial) {
116+
*prev_idx = last_mod_serial.map(|s| (s, self.active.load(Ordering::SeqCst)));
117+
}
118+
prev_idx.map(|(_, idx)| idx)
119+
}
120+
121+
#[must_use]
122+
pub fn add_window(&self, idx: Option<usize>, window: CosmicSurface) -> Message {
123+
window.send_configure();
124+
self.scroll_to_focus.store(true, Ordering::SeqCst);
125+
let model = tab::Model::from(&window);
126+
let mut windows = self.windows.lock().unwrap();
127+
if let Some(idx) = idx {
128+
windows.insert(idx, window);
129+
let prev_active = self.active.swap(idx, Ordering::SeqCst);
130+
if prev_active == idx {
131+
self.reenter.store(true, Ordering::SeqCst);
132+
self.previous_keyboard.store(prev_active, Ordering::SeqCst);
133+
}
134+
135+
Message::TabInsert(idx, model)
136+
} else {
137+
windows.push(window);
138+
self.active.store(windows.len() - 1, Ordering::SeqCst);
139+
Message::TabAdd(model)
140+
}
141+
}
142+
143+
#[must_use]
144+
pub fn remove_window(&self, idx: usize) -> Option<CosmicSurface> {
145+
let mut windows = self.windows.lock().unwrap();
146+
147+
if windows.len() == 1 {
148+
self.override_alive.store(false, Ordering::SeqCst);
149+
let window = &windows[0];
150+
window.try_force_undecorated(false);
151+
window.set_tiled(false);
152+
return None;
153+
}
154+
155+
if windows.len() <= idx {
156+
return None;
157+
}
158+
159+
if idx == self.active.load(Ordering::SeqCst) {
160+
self.reenter.store(true, Ordering::SeqCst);
161+
}
162+
163+
let window = windows.remove(idx);
164+
window.try_force_undecorated(false);
165+
window.set_tiled(false);
166+
167+
_ = self
168+
.active
169+
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |active| {
170+
if active == idx {
171+
self.scroll_to_focus.store(true, Ordering::SeqCst);
172+
Some(windows.len() - 1)
173+
} else if idx < active {
174+
Some(active - 1)
175+
} else {
176+
None
177+
}
178+
});
179+
180+
Some(window)
181+
}
182+
112183
pub fn swap_focus(&self, focus: Option<Focus>) -> Option<Focus> {
113184
let value = focus.map_or(0, |x| x as u8);
114185
unsafe { Focus::from_u8(self.pointer_entered.swap(value, Ordering::SeqCst)) }
@@ -180,90 +251,59 @@ impl CosmicStack {
180251
let window = window.into();
181252
window.try_force_undecorated(true);
182253
window.set_tiled(true);
183-
self.0.with_program(|p| {
184-
let last_mod_serial = moved_into.and_then(|seat| seat.last_modifier_change());
185-
let mut prev_idx = p.previous_index.lock().unwrap();
186-
if !prev_idx.is_some_and(|(serial, _)| Some(serial) == last_mod_serial) {
187-
*prev_idx = last_mod_serial.map(|s| (s, p.active.load(Ordering::SeqCst)));
188-
}
254+
let message = self.0.with_program(|p| {
255+
p.set_previous_index(moved_into);
189256

190257
if let Some(mut geo) = p.geometry.lock().unwrap().clone() {
191258
geo.loc.y += TAB_HEIGHT;
192259
geo.size.h -= TAB_HEIGHT;
193260
window.set_geometry(geo, TAB_HEIGHT as u32);
194261
}
195-
window.send_configure();
196-
if let Some(idx) = idx {
197-
p.windows.lock().unwrap().insert(idx, window);
198-
let old_idx = p.active.swap(idx, Ordering::SeqCst);
199-
if old_idx == idx {
200-
p.reenter.store(true, Ordering::SeqCst);
201-
p.previous_keyboard.store(old_idx, Ordering::SeqCst);
202-
}
203-
} else {
204-
let mut windows = p.windows.lock().unwrap();
205-
windows.push(window);
206-
p.active.store(windows.len() - 1, Ordering::SeqCst);
207-
}
208-
p.scroll_to_focus.store(true, Ordering::SeqCst);
262+
263+
p.add_window(idx, window)
209264
});
265+
266+
self.0.queue_message(message);
210267
self.0
211268
.resize(Size::from((self.active().geometry().size.w, TAB_HEIGHT)));
212-
self.0.force_redraw()
213269
}
214270

215271
pub fn remove_window(&self, window: &CosmicSurface) {
216-
self.0.with_program(|p| {
217-
let mut windows = p.windows.lock().unwrap();
218-
if windows.len() == 1 {
219-
p.override_alive.store(false, Ordering::SeqCst);
220-
let window = windows.get(0).unwrap();
221-
window.try_force_undecorated(false);
222-
window.set_tiled(false);
223-
return;
224-
}
272+
let message = self.0.with_program(|p| {
273+
let windows = p.windows.lock().unwrap();
274+
let idx = windows.iter().position(|w| w == window)?;
275+
drop(windows);
276+
p.remove_window(idx)
277+
.is_some()
278+
.then(|| Message::TabRemove(idx))
279+
});
225280

226-
let Some(idx) = windows.iter().position(|w| w == window) else {
227-
return;
228-
};
229-
if idx == p.active.load(Ordering::SeqCst) {
230-
p.reenter.store(true, Ordering::SeqCst);
231-
}
232-
let window = windows.remove(idx);
233-
window.try_force_undecorated(false);
234-
window.set_tiled(false);
281+
if let Some(message) = message {
282+
self.0.queue_message(message);
283+
}
235284

236-
p.active.fetch_min(windows.len() - 1, Ordering::SeqCst);
237-
});
238285
self.0
239286
.resize(Size::from((self.active().geometry().size.w, TAB_HEIGHT)));
240287
self.0.force_redraw()
241288
}
242289

243290
pub fn remove_idx(&self, idx: usize) -> Option<CosmicSurface> {
244-
let window = self.0.with_program(|p| {
245-
let mut windows = p.windows.lock().unwrap();
246-
if windows.len() == 1 {
247-
p.override_alive.store(false, Ordering::SeqCst);
248-
let window = windows.get(0).unwrap();
249-
window.try_force_undecorated(false);
250-
window.set_tiled(false);
251-
return Some(window.clone());
252-
}
253-
if windows.len() <= idx {
254-
return None;
255-
}
256-
if idx == p.active.load(Ordering::SeqCst) {
257-
p.reenter.store(true, Ordering::SeqCst);
291+
let (message, window) = self.0.with_program(|p| match p.remove_window(idx) {
292+
Some(window) => (Some(Message::TabRemove(idx)), Some(window)),
293+
None => {
294+
let windows = p.windows.lock().unwrap();
295+
if windows.len() == 1 {
296+
(None, Some(windows[0].clone()))
297+
} else {
298+
(None, None)
299+
}
258300
}
259-
let window = windows.remove(idx);
260-
window.try_force_undecorated(false);
261-
window.set_tiled(false);
301+
});
262302

263-
p.active.fetch_min(windows.len() - 1, Ordering::SeqCst);
303+
if let Some(message) = message {
304+
self.0.queue_message(message);
305+
}
264306

265-
Some(window)
266-
});
267307
self.0
268308
.resize(Size::from((self.active().geometry().size.w, TAB_HEIGHT)));
269309
self.0.force_redraw();
@@ -445,6 +485,7 @@ impl CosmicStack {
445485
});
446486

447487
if !matches!(result, MoveResult::Default) {
488+
self.0.queue_message(Message::Refresh);
448489
self.0
449490
.resize(Size::from((self.active().geometry().size.w, TAB_HEIGHT)));
450491
}
@@ -789,11 +830,15 @@ impl CosmicStack {
789830
}
790831
}
791832

792-
#[derive(Debug, Clone, Copy)]
833+
#[derive(Debug, Clone)]
793834
pub enum Message {
794835
DragStart,
795836
Menu,
837+
TabAdd(tab::Model),
796838
TabMenu(usize),
839+
TabInsert(usize, tab::Model),
840+
TabRemove(usize),
841+
TabSwap(usize, usize),
797842
PotentialTabDragStart(usize),
798843
Activate(usize),
799844
Close(usize),
@@ -964,6 +1009,28 @@ impl Program for CosmicStackInternal {
9641009
}
9651010
}
9661011
}
1012+
Message::TabAdd(model) => {
1013+
let mut tab_models = Vec::with_capacity(self.tab_models.len() + 1);
1014+
tab_models.extend_from_slice(&self.tab_models);
1015+
tab_models.push(model);
1016+
self.tab_models = tab_models.into()
1017+
}
1018+
Message::TabInsert(idx, model) => {
1019+
let mut tab_models = Vec::with_capacity(self.tab_models.len() + 1);
1020+
tab_models.extend_from_slice(&self.tab_models);
1021+
tab_models.insert(idx, model);
1022+
self.tab_models = tab_models.into()
1023+
}
1024+
Message::TabRemove(idx) => {
1025+
let mut tab_models = Vec::from(self.tab_models.as_ref());
1026+
_ = tab_models.remove(idx);
1027+
self.tab_models = tab_models.into();
1028+
}
1029+
Message::TabSwap(from, to) => {
1030+
let mut tab_models = Vec::from(self.tab_models.as_ref());
1031+
tab_models.swap(from, to);
1032+
self.tab_models = tab_models.into();
1033+
}
9671034
Message::TabMenu(idx) => {
9681035
if let Some((seat, serial)) = last_seat.cloned() {
9691036
if let Some(surface) = self.windows.lock().unwrap()[idx]

0 commit comments

Comments
 (0)