zone.rs (19903B)
1 use crate::change::Disposition; 2 use crate::cycle::Cycle; 3 use crate::cycle::InsertPos; 4 use crate::cycle::Selector; 5 use crate::decoration::Border; 6 use crate::decoration::Decoration; 7 use crate::error::StateChangeError; 8 use crate::identify::Ident; 9 use crate::identify::Identify; 10 use crate::layout::Apply; 11 use crate::layout::Layout; 12 use crate::layout::LayoutConfig; 13 use crate::layout::LayoutData; 14 use crate::layout::LayoutKind; 15 use crate::placement::Placement; 16 use crate::placement::PlacementMethod; 17 use crate::placement::PlacementRegion; 18 use crate::placement::PlacementTarget; 19 20 use winsys::geometry::Region; 21 use winsys::window::Window; 22 23 use std::cell::Cell; 24 use std::collections::HashMap; 25 use std::collections::HashSet; 26 use std::sync::atomic; 27 use std::vec::Vec; 28 29 pub type ZoneId = u32; 30 31 static INSTANCE_COUNT: atomic::AtomicU32 = atomic::AtomicU32::new(1); 32 33 #[derive(Debug, PartialEq)] 34 pub enum ZoneContent { 35 Client(Window), 36 Tab(Cycle<ZoneId>), 37 Layout(Layout, Cycle<ZoneId>), 38 } 39 40 #[derive(Debug)] 41 pub struct Zone { 42 id: ZoneId, 43 parent: Cell<Option<ZoneId>>, 44 method: Cell<PlacementMethod>, 45 content: ZoneContent, 46 region: Cell<Region>, 47 decoration: Cell<Decoration>, 48 is_visible: Cell<bool>, 49 } 50 51 impl Zone { 52 fn next_id() -> ZoneId { 53 INSTANCE_COUNT.fetch_add(1, atomic::Ordering::Relaxed) 54 } 55 56 fn new( 57 parent: Option<ZoneId>, 58 content: ZoneContent, 59 region: Region, 60 ) -> (ZoneId, Self) { 61 let id = Self::next_id(); 62 63 (id, Self { 64 id, 65 parent: Cell::new(parent), 66 method: Cell::new(PlacementMethod::Free), 67 content, 68 region: Cell::new(region), 69 decoration: Cell::new(Decoration::NO_DECORATION), 70 is_visible: Cell::new(true), 71 }) 72 } 73 74 pub fn set_content( 75 &mut self, 76 content: ZoneContent, 77 ) { 78 self.content = content; 79 } 80 81 fn set_kind( 82 &mut self, 83 kind: LayoutKind, 84 ) -> Result<LayoutKind, StateChangeError> { 85 match self.content { 86 ZoneContent::Layout(ref mut layout, _) => layout.set_kind(kind), 87 _ => Err(StateChangeError::InvalidCaller), 88 } 89 } 90 91 pub fn prev_kind(&self) -> Result<LayoutKind, StateChangeError> { 92 match &self.content { 93 ZoneContent::Layout(layout, _) => Ok(layout.prev_kind()), 94 _ => Err(StateChangeError::InvalidCaller), 95 } 96 } 97 98 pub fn kind(&self) -> Result<LayoutKind, StateChangeError> { 99 match &self.content { 100 ZoneContent::Layout(layout, _) => Ok(layout.kind()), 101 _ => Err(StateChangeError::InvalidCaller), 102 } 103 } 104 105 pub fn set_region( 106 &self, 107 region: Region, 108 ) { 109 self.region.set(region); 110 } 111 112 pub fn set_method( 113 &self, 114 method: PlacementMethod, 115 ) { 116 self.method.set(method); 117 } 118 119 pub fn default_data(&self) -> Option<LayoutData> { 120 match &self.content { 121 ZoneContent::Layout(layout, _) => Some(layout.default_data()), 122 _ => None, 123 } 124 } 125 126 pub fn data(&self) -> Option<&LayoutData> { 127 match self.content { 128 ZoneContent::Layout(ref layout, _) => Some(layout.data()), 129 _ => None, 130 } 131 } 132 133 pub fn data_mut(&mut self) -> Option<&mut LayoutData> { 134 match self.content { 135 ZoneContent::Layout(ref mut layout, _) => Some(layout.data_mut()), 136 _ => None, 137 } 138 } 139 140 pub fn prev_data(&self) -> Option<&LayoutData> { 141 match self.content { 142 ZoneContent::Layout(ref layout, _) => Some(layout.prev_data()), 143 _ => None, 144 } 145 } 146 147 pub fn config(&self) -> Option<LayoutConfig> { 148 match self.content { 149 ZoneContent::Layout(ref layout, _) => Some(layout.config()), 150 _ => None, 151 } 152 } 153 154 pub fn method(&self) -> PlacementMethod { 155 self.method.get() 156 } 157 } 158 159 enum ZoneChange { 160 Visible(bool), 161 Region(Region), 162 Decoration(Decoration), 163 Method(PlacementMethod), 164 } 165 166 pub struct ZoneManager { 167 zone_map: HashMap<ZoneId, Zone>, 168 persistent_data_copy: bool, 169 } 170 171 impl ZoneManager { 172 pub fn new() -> Self { 173 Self { 174 zone_map: HashMap::new(), 175 persistent_data_copy: true, 176 } 177 } 178 179 pub fn new_zone( 180 &mut self, 181 parent: Option<ZoneId>, 182 content: ZoneContent, 183 ) -> ZoneId { 184 let (id, zone) = Zone::new(parent, content, Region::new(0, 0, 0, 0)); 185 let parent = parent.and_then(|p| self.zone_map.get_mut(&p)); 186 187 if let Some(parent) = parent { 188 match &mut parent.content { 189 ZoneContent::Tab(zones) | ZoneContent::Layout(_, zones) => { 190 zones.insert_at(&InsertPos::AfterActive, id) 191 }, 192 _ => unreachable!("attempted to insert into non-cycle"), 193 } 194 } 195 196 self.zone_map.insert(id, zone); 197 id 198 } 199 200 pub fn remove_zone( 201 &mut self, 202 id: ZoneId, 203 ) { 204 let cycle = self.nearest_cycle(id); 205 let cycle = self.zone_map.get_mut(&cycle).unwrap(); 206 207 match &mut cycle.content { 208 ZoneContent::Tab(zones) | ZoneContent::Layout(_, zones) => { 209 zones.remove_for(&Selector::AtIdent(id)); 210 }, 211 _ => {}, 212 } 213 } 214 215 pub fn activate_zone( 216 &self, 217 id: ZoneId, 218 ) { 219 if let Some(cycle_id) = self.next_cycle(id) { 220 let cycle = self.zone(cycle_id); 221 222 match cycle.content { 223 ZoneContent::Tab(ref zones) | ZoneContent::Layout(_, ref zones) => { 224 zones.activate_for(&Selector::AtIdent(id)); 225 self.activate_zone(cycle_id); 226 }, 227 _ => {}, 228 } 229 } 230 } 231 232 pub fn set_kind( 233 &mut self, 234 id: ZoneId, 235 kind: LayoutKind, 236 ) -> Result<LayoutKind, StateChangeError> { 237 let persistent_data_copy = self.persistent_data_copy; 238 let cycle = self.nearest_cycle(id); 239 let cycle = self.zone_mut(cycle); 240 241 let prev_kind = cycle.set_kind(kind)?; 242 243 if persistent_data_copy { 244 let prev_data = *cycle.prev_data().unwrap(); 245 let data = cycle.data_mut().unwrap(); 246 *data = prev_data; 247 } 248 249 Ok(prev_kind) 250 } 251 252 pub fn set_prev_kind( 253 &mut self, 254 id: ZoneId, 255 ) -> Result<LayoutKind, StateChangeError> { 256 let persistent_data_copy = self.persistent_data_copy; 257 let cycle = self.nearest_cycle(id); 258 let cycle = self.zone_mut(cycle); 259 260 let kind = cycle.prev_kind()?; 261 let prev_kind = cycle.set_kind(kind)?; 262 263 if persistent_data_copy { 264 let prev_data = *cycle.prev_data().unwrap(); 265 let data = cycle.data_mut().unwrap(); 266 *data = prev_data; 267 } 268 269 Ok(prev_kind) 270 } 271 272 pub fn active_default_data( 273 &mut self, 274 id: ZoneId, 275 ) -> Option<LayoutData> { 276 let cycle = self.nearest_cycle(id); 277 let cycle = self.zone(cycle); 278 279 cycle.default_data() 280 } 281 282 pub fn active_prev_data( 283 &mut self, 284 id: ZoneId, 285 ) -> Option<&LayoutData> { 286 let cycle = self.nearest_cycle(id); 287 let cycle = self.zone_mut(cycle); 288 289 cycle.prev_data() 290 } 291 292 pub fn active_data_mut( 293 &mut self, 294 id: ZoneId, 295 ) -> Option<&mut LayoutData> { 296 let cycle = self.nearest_cycle(id); 297 let cycle = self.zone_mut(cycle); 298 299 cycle.data_mut() 300 } 301 302 pub fn active_layoutconfig( 303 &self, 304 id: ZoneId, 305 ) -> Option<LayoutConfig> { 306 let cycle = self.nearest_cycle(id); 307 let cycle = self.zone(cycle); 308 309 cycle.config() 310 } 311 312 pub fn zone_checked( 313 &self, 314 id: ZoneId, 315 ) -> Option<&Zone> { 316 self.zone_map.get(&id) 317 } 318 319 pub fn zone( 320 &self, 321 id: ZoneId, 322 ) -> &Zone { 323 self.zone_map.get(&id).unwrap() 324 } 325 326 pub fn zone_checked_mut( 327 &mut self, 328 id: ZoneId, 329 ) -> Option<&mut Zone> { 330 self.zone_map.get_mut(&id) 331 } 332 333 pub fn zone_mut( 334 &mut self, 335 id: ZoneId, 336 ) -> &mut Zone { 337 self.zone_map.get_mut(&id).unwrap() 338 } 339 340 pub fn parent_id( 341 &self, 342 id: ZoneId, 343 ) -> Option<ZoneId> { 344 self.zone_map.get(&id).and_then(|zone| zone.parent.get()) 345 } 346 347 pub fn cycle_config( 348 &self, 349 id: ZoneId, 350 ) -> Option<(ZoneId, Option<LayoutConfig>)> { 351 let cycle = self.next_cycle(id)?; 352 let zone = self.zone(cycle); 353 354 Some((cycle, zone.config())) 355 } 356 357 pub fn is_cycle( 358 &self, 359 id: ZoneId, 360 ) -> bool { 361 let zone = self.zone_map.get(&id).unwrap(); 362 363 match zone.content { 364 ZoneContent::Tab(_) | ZoneContent::Layout(..) => true, 365 _ => false, 366 } 367 } 368 369 pub fn nearest_cycle( 370 &self, 371 id: ZoneId, 372 ) -> ZoneId { 373 let mut next = id; 374 375 loop { 376 let zone = self.zone_map.get(&next).unwrap(); 377 378 match zone.content { 379 ZoneContent::Tab(_) | ZoneContent::Layout(..) => { 380 return next; 381 }, 382 _ => {}, 383 } 384 385 if let Some(parent) = zone.parent.get() { 386 next = parent; 387 } else { 388 unreachable!("no nearest cycle found"); 389 } 390 } 391 } 392 393 pub fn next_cycle( 394 &self, 395 mut id: ZoneId, 396 ) -> Option<ZoneId> { 397 while let Some(next_id) = self.parent_id(id) { 398 let zone = self.zone_map.get(&next_id).unwrap(); 399 400 match zone.content { 401 ZoneContent::Tab(_) | ZoneContent::Layout(..) => { 402 return Some(next_id); 403 }, 404 _ => id = next_id, 405 } 406 } 407 408 None 409 } 410 411 pub fn is_within_persisent( 412 &self, 413 mut id: ZoneId, 414 ) -> bool { 415 while let Some(next_id) = self.parent_id(id) { 416 let zone = self.zone_map.get(&next_id).unwrap(); 417 418 match zone.content { 419 ZoneContent::Tab(_) => { 420 return true; 421 }, 422 ZoneContent::Layout(ref layout, _) => { 423 if layout.config().persistent { 424 return true; 425 } 426 }, 427 _ => {}, 428 } 429 430 id = next_id; 431 } 432 433 false 434 } 435 436 fn gather_subzones( 437 &self, 438 zone: ZoneId, 439 recurse: bool, 440 ) -> Vec<ZoneId> { 441 if let Some(zone) = self.zone_map.get(&zone) { 442 match &zone.content { 443 ZoneContent::Client(_) => {}, 444 ZoneContent::Tab(zones) | ZoneContent::Layout(_, zones) => { 445 let mut zones = zones.as_vec(); 446 447 if recurse { 448 let mut subzones = Vec::new(); 449 450 zones.iter().for_each(|&zone| { 451 subzones.extend(self.gather_subzones(zone, recurse)); 452 }); 453 454 zones.extend(subzones); 455 } 456 457 return zones; 458 }, 459 } 460 } 461 462 Vec::with_capacity(0) 463 } 464 465 /// Arrange a zone and all of its subzones within the region 466 /// of the supplied zone 467 pub fn arrange( 468 &self, 469 zone: ZoneId, 470 to_ignore: &HashSet<ZoneId>, 471 ) -> Vec<Placement> { 472 let cycle = self.nearest_cycle(zone); 473 let zone = self.zone_map.get(&cycle).unwrap(); 474 let region = zone.region.get(); 475 let decoration = zone.decoration.get(); 476 477 let method = match &zone.content { 478 ZoneContent::Tab(_) => PlacementMethod::Tile, 479 ZoneContent::Layout(layout, _) => layout.config().method, 480 _ => unreachable!("attempting to derive method from non-cycle"), 481 }; 482 483 self.arrange_subzones(cycle, region, decoration, method, to_ignore) 484 } 485 486 fn arrange_subzones( 487 &self, 488 id: ZoneId, 489 region: Region, 490 decoration: Decoration, 491 method: PlacementMethod, 492 to_ignore: &HashSet<ZoneId>, 493 ) -> Vec<Placement> { 494 let zone = self.zone_map.get(&id).unwrap(); 495 let content = &zone.content; 496 497 let mut zone_changes: Vec<(ZoneId, ZoneChange)> = Vec::new(); 498 499 let placements = match &content { 500 ZoneContent::Client(window) => { 501 return vec![Placement { 502 method, 503 kind: PlacementTarget::Client(*window), 504 zone: id, 505 region: if method == PlacementMethod::Free { 506 PlacementRegion::FreeRegion 507 } else { 508 PlacementRegion::NewRegion(region) 509 }, 510 decoration, 511 }]; 512 }, 513 ZoneContent::Tab(zones) => { 514 let mut placements = vec![Placement { 515 method, 516 kind: PlacementTarget::Tab(zones.len()), 517 zone: id, 518 region: if method == PlacementMethod::Free { 519 PlacementRegion::FreeRegion 520 } else { 521 zone_changes.push((id, ZoneChange::Region(region))); 522 PlacementRegion::NewRegion(region) 523 }, 524 decoration, 525 }]; 526 527 let mut region = region; 528 Layout::adjust_for_border(&mut region, 1, &Zone::MIN_ZONE_DIM); 529 530 let active_element = zones.active_element().copied(); 531 let zones: Vec<ZoneId> = zones 532 .iter() 533 .filter(|&id| !to_ignore.contains(id)) 534 .copied() 535 .collect(); 536 537 zones.into_iter().for_each(|id| { 538 let is_active_element = Some(id) == active_element; 539 let subzones = self.gather_subzones(id, !is_active_element); 540 let method = PlacementMethod::Tile; 541 542 subzones.into_iter().for_each(|id| { 543 zone_changes.push((id, ZoneChange::Visible(is_active_element))); 544 zone_changes.push((id, ZoneChange::Region(region))); 545 zone_changes.push((id, ZoneChange::Method(method))); 546 }); 547 548 placements.extend(self.arrange_subzones( 549 id, 550 region, 551 Decoration { 552 frame: None, 553 border: Some(Border { 554 width: 1, 555 colors: Default::default(), 556 }), 557 }, 558 method, 559 to_ignore, 560 )); 561 }); 562 563 placements 564 }, 565 ZoneContent::Layout(layout, zones) => { 566 let active_element = zones.active_element(); 567 let mut subplacements = Vec::new(); 568 let mut placements = vec![Placement { 569 method, 570 kind: PlacementTarget::Layout, 571 zone: id, 572 region: if method == PlacementMethod::Free { 573 PlacementRegion::FreeRegion 574 } else { 575 PlacementRegion::NewRegion(region) 576 }, 577 decoration, 578 }]; 579 580 let zones: Vec<ZoneId> = zones 581 .iter() 582 .filter(|&id| layout.config().single || !to_ignore.contains(id)) 583 .copied() 584 .collect(); 585 586 let (method, application) = layout.apply( 587 region, 588 zones.iter().map(|id| Some(id) == active_element).collect(), 589 ); 590 591 zones.into_iter().zip(application.into_iter()).for_each( 592 |(id, (disposition, is_visible))| { 593 let (region, decoration) = match disposition { 594 Disposition::Unchanged(decoration) => { 595 let zone = self.zone_map.get(&id).unwrap(); 596 (zone.region.get(), decoration) 597 }, 598 Disposition::Changed(region, decoration) => (region, decoration), 599 }; 600 601 if !is_visible { 602 let subzones = self.gather_subzones(id, true); 603 604 placements.push(Placement { 605 method, 606 kind: PlacementTarget::from_zone_content(&self.zone(id).content), 607 zone: id, 608 region: PlacementRegion::NoRegion, 609 decoration, 610 }); 611 612 zone_changes.extend( 613 subzones 614 .into_iter() 615 .map(|id| { 616 placements.push(Placement { 617 method, 618 kind: PlacementTarget::from_zone_content( 619 &self.zone(id).content, 620 ), 621 zone: id, 622 region: PlacementRegion::NoRegion, 623 decoration, 624 }); 625 626 (id, ZoneChange::Visible(false)) 627 }) 628 .collect::<Vec<(ZoneId, ZoneChange)>>(), 629 ); 630 } else { 631 subplacements.push((id, region, decoration)); 632 } 633 }, 634 ); 635 636 subplacements 637 .into_iter() 638 .for_each(|(id, region, decoration)| { 639 placements.extend( 640 self.arrange_subzones(id, region, decoration, method, to_ignore), 641 ); 642 }); 643 644 placements 645 }, 646 }; 647 648 { 649 let zone = self.zone_map.get(&id).unwrap(); 650 zone.region.set(region); 651 zone.decoration.set(decoration); 652 zone.method.set(method); 653 } 654 655 zone_changes.into_iter().for_each(|(id, change)| { 656 let zone = self.zone_map.get(&id).unwrap(); 657 658 match change { 659 ZoneChange::Visible(is_visible) => { 660 zone.is_visible.set(is_visible); 661 }, 662 ZoneChange::Region(region) => { 663 zone.region.set(region); 664 }, 665 ZoneChange::Decoration(decoration) => { 666 zone.decoration.set(decoration); 667 }, 668 ZoneChange::Method(method) => { 669 zone.method.set(method); 670 }, 671 }; 672 }); 673 674 placements 675 } 676 } 677 678 impl PartialEq<Self> for Zone { 679 fn eq( 680 &self, 681 other: &Self, 682 ) -> bool { 683 self.id == other.id 684 } 685 } 686 687 impl Identify for Zone { 688 fn id(&self) -> Ident { 689 self.id 690 } 691 }