@@ -109,6 +109,77 @@ pub struct CosmicStackInternal {
109109}
110110
111111impl 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 ) ]
793834pub 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