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