Skip to content

Commit 18a5fa5

Browse files
committed
improv(stack): add tab model messages & refactor window add/remove operations
1 parent 63bc399 commit 18a5fa5

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
@@ -108,6 +108,77 @@ pub struct CosmicStackInternal {
108108
}
109109

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

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

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

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

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

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

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

264-
Some(window)
265-
});
266306
self.0
267307
.resize(Size::from((self.active().geometry().size.w, TAB_HEIGHT)));
268308
self.0.force_redraw();
@@ -444,6 +484,7 @@ impl CosmicStack {
444484
});
445485

446486
if !matches!(result, MoveResult::Default) {
487+
self.0.queue_message(Message::Refresh);
447488
self.0
448489
.resize(Size::from((self.active().geometry().size.w, TAB_HEIGHT)));
449490
}
@@ -793,11 +834,15 @@ impl CosmicStack {
793834
}
794835
}
795836

796-
#[derive(Debug, Clone, Copy)]
837+
#[derive(Debug, Clone)]
797838
pub enum Message {
798839
DragStart,
799840
Menu,
841+
TabAdd(tab::Model),
800842
TabMenu(usize),
843+
TabInsert(usize, tab::Model),
844+
TabRemove(usize),
845+
TabSwap(usize, usize),
801846
PotentialTabDragStart(usize),
802847
Activate(usize),
803848
Close(usize),
@@ -968,6 +1013,28 @@ impl Program for CosmicStackInternal {
9681013
}
9691014
}
9701015
}
1016+
Message::TabAdd(model) => {
1017+
let mut tab_models = Vec::with_capacity(self.tab_models.len() + 1);
1018+
tab_models.extend_from_slice(&self.tab_models);
1019+
tab_models.push(model);
1020+
self.tab_models = tab_models.into()
1021+
}
1022+
Message::TabInsert(idx, model) => {
1023+
let mut tab_models = Vec::with_capacity(self.tab_models.len() + 1);
1024+
tab_models.extend_from_slice(&self.tab_models);
1025+
tab_models.insert(idx, model);
1026+
self.tab_models = tab_models.into()
1027+
}
1028+
Message::TabRemove(idx) => {
1029+
let mut tab_models = Vec::from(self.tab_models.as_ref());
1030+
_ = tab_models.remove(idx);
1031+
self.tab_models = tab_models.into();
1032+
}
1033+
Message::TabSwap(from, to) => {
1034+
let mut tab_models = Vec::from(self.tab_models.as_ref());
1035+
tab_models.swap(from, to);
1036+
self.tab_models = tab_models.into();
1037+
}
9711038
Message::TabMenu(idx) => {
9721039
if let Some((seat, serial)) = last_seat.cloned() {
9731040
if let Some(surface) = self.windows.lock().unwrap()[idx]

0 commit comments

Comments
 (0)