wzrd

An ICCCM & EWMH compliant X11 reparenting, dynamic window manager, written in Rust
git clone git://git.deurzen.net/wzrd
Log | Files | Refs | LICENSE

layout.rs (30295B)


      1 use crate::change::Disposition;
      2 use crate::decoration::Decoration;
      3 use crate::decoration::Frame;
      4 use crate::error::StateChangeError;
      5 use crate::identify::Ident;
      6 use crate::identify::Identify;
      7 use crate::placement::PlacementMethod;
      8 use crate::zone::Zone;
      9 
     10 use winsys::geometry::Dim;
     11 use winsys::geometry::Extents;
     12 use winsys::geometry::Padding;
     13 use winsys::geometry::Pos;
     14 use winsys::geometry::Region;
     15 
     16 use strum::EnumCount;
     17 use strum::IntoEnumIterator;
     18 use strum_macros::EnumIter;
     19 use strum_macros::ToString;
     20 
     21 use std::collections::HashMap;
     22 use std::string::ToString;
     23 use std::vec::Vec;
     24 
     25 type LayoutFn = fn(&Region, &LayoutData, Vec<bool>) -> Vec<(Disposition, bool)>;
     26 
     27 #[non_exhaustive]
     28 #[derive(Debug, PartialEq, Clone, Copy)]
     29 pub struct LayoutConfig {
     30     pub method: PlacementMethod,
     31     pub decoration: Decoration,
     32     pub root_only: bool,
     33     pub margin: bool,
     34     pub gap: bool,
     35     pub persistent: bool,
     36     pub single: bool,
     37     pub wraps: bool,
     38 }
     39 
     40 impl Default for LayoutConfig {
     41     fn default() -> Self {
     42         Self {
     43             method: PlacementMethod::Free,
     44             decoration: Default::default(),
     45             root_only: true,
     46             margin: false,
     47             gap: false,
     48             persistent: false,
     49             single: false,
     50             wraps: true,
     51         }
     52     }
     53 }
     54 
     55 #[non_exhaustive]
     56 #[derive(Debug, PartialEq, Clone, Copy)]
     57 pub struct LayoutData {
     58     /// Generic layout data
     59     pub margin: Padding,
     60     pub gap_size: u32,
     61 
     62     /// Tiled layout data
     63     pub main_count: u32,
     64     pub main_factor: f32,
     65 }
     66 
     67 impl Default for LayoutData {
     68     fn default() -> Self {
     69         Self {
     70             margin: Default::default(),
     71             gap_size: 0u32,
     72 
     73             main_count: 1u32,
     74             main_factor: 0.50f32,
     75         }
     76     }
     77 }
     78 
     79 #[non_exhaustive]
     80 #[repr(u8)]
     81 #[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, EnumIter, EnumCount, ToString)]
     82 pub enum LayoutKind {
     83     /// Free layouts
     84     Float = b'f',
     85     BLFloat = b'F',
     86     SingleFloat = b'z',
     87     BLSingleFloat = b'Z',
     88 
     89     /// Tiled layouts
     90     // Overlapping
     91     Center = b';',
     92     Monocle = b'%',
     93     // Non-overlapping
     94     Paper = b'p',
     95     SPaper = b'P',
     96     Stack = b's',
     97     SStack = b'S',
     98     BStack = b'b',
     99     SBStack = b'B',
    100     Horz = b'h',
    101     SHorz = b'H',
    102     Vert = b'v',
    103     SVert = b'V',
    104 }
    105 
    106 impl LayoutKind {
    107     pub fn symbol(&self) -> char {
    108         (*self as u8) as char
    109     }
    110 
    111     pub fn name(&self) -> String {
    112         self.to_string()
    113     }
    114 
    115     pub fn config(&self) -> LayoutConfig {
    116         match *self {
    117             LayoutKind::Float => LayoutConfig {
    118                 method: PlacementMethod::Free,
    119                 decoration: Decoration::FREE_DECORATION,
    120                 root_only: true,
    121                 margin: false,
    122                 gap: false,
    123                 persistent: false,
    124                 single: false,
    125                 wraps: true,
    126             },
    127             LayoutKind::BLFloat => LayoutConfig {
    128                 method: PlacementMethod::Free,
    129                 decoration: Decoration::NO_DECORATION,
    130                 root_only: true,
    131                 margin: false,
    132                 gap: false,
    133                 persistent: false,
    134                 single: false,
    135                 wraps: true,
    136             },
    137             LayoutKind::SingleFloat => LayoutConfig {
    138                 method: PlacementMethod::Free,
    139                 decoration: Decoration::FREE_DECORATION,
    140                 root_only: true,
    141                 margin: false,
    142                 gap: false,
    143                 persistent: true,
    144                 single: true,
    145                 wraps: true,
    146             },
    147             LayoutKind::BLSingleFloat => LayoutConfig {
    148                 method: PlacementMethod::Free,
    149                 decoration: Decoration::NO_DECORATION,
    150                 root_only: true,
    151                 margin: false,
    152                 gap: false,
    153                 persistent: true,
    154                 single: true,
    155                 wraps: true,
    156             },
    157             LayoutKind::Center => LayoutConfig {
    158                 method: PlacementMethod::Tile,
    159                 decoration: Decoration::NO_DECORATION,
    160                 root_only: false,
    161                 margin: true,
    162                 gap: true,
    163                 persistent: false,
    164                 single: false,
    165                 wraps: true,
    166             },
    167             LayoutKind::Monocle => LayoutConfig {
    168                 method: PlacementMethod::Tile,
    169                 decoration: Decoration::NO_DECORATION,
    170                 root_only: false,
    171                 margin: true,
    172                 gap: true,
    173                 persistent: false,
    174                 single: false,
    175                 wraps: true,
    176             },
    177             LayoutKind::Paper => LayoutConfig {
    178                 method: PlacementMethod::Tile,
    179                 decoration: Decoration {
    180                     frame: Some(Frame {
    181                         extents: Extents {
    182                             left: 1,
    183                             right: 1,
    184                             top: 0,
    185                             bottom: 0,
    186                         },
    187                         colors: Default::default(),
    188                     }),
    189                     border: None,
    190                 },
    191                 root_only: false,
    192                 margin: true,
    193                 gap: true,
    194                 persistent: true,
    195                 single: false,
    196                 wraps: false,
    197             },
    198             LayoutKind::SPaper => LayoutConfig {
    199                 method: PlacementMethod::Tile,
    200                 decoration: Decoration {
    201                     frame: Some(Frame {
    202                         extents: Extents {
    203                             left: 1,
    204                             right: 1,
    205                             top: 0,
    206                             bottom: 0,
    207                         },
    208                         colors: Default::default(),
    209                     }),
    210                     border: None,
    211                 },
    212                 root_only: false,
    213                 margin: true,
    214                 gap: false,
    215                 persistent: true,
    216                 single: false,
    217                 wraps: false,
    218             },
    219             LayoutKind::Stack => LayoutConfig {
    220                 method: PlacementMethod::Tile,
    221                 decoration: Decoration {
    222                     frame: Some(Frame {
    223                         extents: Extents {
    224                             left: 0,
    225                             right: 0,
    226                             top: 3,
    227                             bottom: 0,
    228                         },
    229                         colors: Default::default(),
    230                     }),
    231                     border: None,
    232                 },
    233                 root_only: false,
    234                 margin: true,
    235                 gap: true,
    236                 persistent: false,
    237                 single: false,
    238                 wraps: true,
    239             },
    240             LayoutKind::SStack => LayoutConfig {
    241                 method: PlacementMethod::Tile,
    242                 decoration: Decoration {
    243                     frame: Some(Frame {
    244                         extents: Extents {
    245                             left: 0,
    246                             right: 0,
    247                             top: 3,
    248                             bottom: 0,
    249                         },
    250                         colors: Default::default(),
    251                     }),
    252                     border: None,
    253                 },
    254                 root_only: false,
    255                 margin: true,
    256                 gap: false,
    257                 persistent: false,
    258                 single: false,
    259                 wraps: true,
    260             },
    261             LayoutKind::BStack => LayoutConfig {
    262                 method: PlacementMethod::Tile,
    263                 decoration: Decoration {
    264                     frame: Some(Frame {
    265                         extents: Extents {
    266                             left: 0,
    267                             right: 0,
    268                             top: 3,
    269                             bottom: 0,
    270                         },
    271                         colors: Default::default(),
    272                     }),
    273                     border: None,
    274                 },
    275                 root_only: false,
    276                 margin: true,
    277                 gap: true,
    278                 persistent: false,
    279                 single: false,
    280                 wraps: true,
    281             },
    282             LayoutKind::SBStack => LayoutConfig {
    283                 method: PlacementMethod::Tile,
    284                 decoration: Decoration {
    285                     frame: Some(Frame {
    286                         extents: Extents {
    287                             left: 0,
    288                             right: 0,
    289                             top: 3,
    290                             bottom: 0,
    291                         },
    292                         colors: Default::default(),
    293                     }),
    294                     border: None,
    295                 },
    296                 root_only: false,
    297                 margin: true,
    298                 gap: false,
    299                 persistent: false,
    300                 single: false,
    301                 wraps: true,
    302             },
    303             LayoutKind::Horz => LayoutConfig {
    304                 method: PlacementMethod::Tile,
    305                 decoration: Decoration {
    306                     frame: Some(Frame {
    307                         extents: Extents {
    308                             left: 0,
    309                             right: 0,
    310                             top: 3,
    311                             bottom: 0,
    312                         },
    313                         colors: Default::default(),
    314                     }),
    315                     border: None,
    316                 },
    317                 root_only: false,
    318                 margin: true,
    319                 gap: true,
    320                 persistent: false,
    321                 single: false,
    322                 wraps: true,
    323             },
    324             LayoutKind::SHorz => LayoutConfig {
    325                 method: PlacementMethod::Tile,
    326                 decoration: Decoration {
    327                     frame: Some(Frame {
    328                         extents: Extents {
    329                             left: 0,
    330                             right: 0,
    331                             top: 3,
    332                             bottom: 0,
    333                         },
    334                         colors: Default::default(),
    335                     }),
    336                     border: None,
    337                 },
    338                 root_only: false,
    339                 margin: true,
    340                 gap: false,
    341                 persistent: false,
    342                 single: false,
    343                 wraps: true,
    344             },
    345             LayoutKind::Vert => LayoutConfig {
    346                 method: PlacementMethod::Tile,
    347                 decoration: Decoration {
    348                     frame: Some(Frame {
    349                         extents: Extents {
    350                             left: 0,
    351                             right: 0,
    352                             top: 3,
    353                             bottom: 0,
    354                         },
    355                         colors: Default::default(),
    356                     }),
    357                     border: None,
    358                 },
    359                 root_only: false,
    360                 margin: true,
    361                 gap: true,
    362                 persistent: false,
    363                 single: false,
    364                 wraps: true,
    365             },
    366             LayoutKind::SVert => LayoutConfig {
    367                 method: PlacementMethod::Tile,
    368                 decoration: Decoration {
    369                     frame: Some(Frame {
    370                         extents: Extents {
    371                             left: 0,
    372                             right: 0,
    373                             top: 3,
    374                             bottom: 0,
    375                         },
    376                         colors: Default::default(),
    377                     }),
    378                     border: None,
    379                 },
    380                 root_only: false,
    381                 margin: true,
    382                 gap: false,
    383                 persistent: false,
    384                 single: false,
    385                 wraps: true,
    386             },
    387 
    388             #[allow(unreachable_patterns)]
    389             _ => unimplemented!("{:?} does not have an associated configuration", self),
    390         }
    391     }
    392 
    393     fn default_data(&self) -> LayoutData {
    394         match *self {
    395             LayoutKind::Float => Default::default(),
    396             LayoutKind::BLFloat => Default::default(),
    397             LayoutKind::SingleFloat => Default::default(),
    398             LayoutKind::BLSingleFloat => Default::default(),
    399             LayoutKind::Center => LayoutData {
    400                 main_count: 5u32,
    401                 main_factor: 0.40f32,
    402                 ..Default::default()
    403             },
    404             LayoutKind::Monocle => Default::default(),
    405             LayoutKind::Paper => Default::default(),
    406             LayoutKind::SPaper => Default::default(),
    407             LayoutKind::Stack => LayoutData {
    408                 main_count: 1u32,
    409                 main_factor: 0.50f32,
    410                 ..Default::default()
    411             },
    412             LayoutKind::SStack => LayoutData {
    413                 main_count: 1u32,
    414                 main_factor: 0.50f32,
    415                 ..Default::default()
    416             },
    417             LayoutKind::BStack => LayoutData {
    418                 main_count: 1u32,
    419                 main_factor: 0.50f32,
    420                 ..Default::default()
    421             },
    422             LayoutKind::SBStack => LayoutData {
    423                 main_count: 1u32,
    424                 main_factor: 0.50f32,
    425                 ..Default::default()
    426             },
    427             LayoutKind::Horz => Default::default(),
    428             LayoutKind::SHorz => Default::default(),
    429             LayoutKind::Vert => Default::default(),
    430             LayoutKind::SVert => Default::default(),
    431 
    432             #[allow(unreachable_patterns)]
    433             _ => unimplemented!("{:?} does not have associated default data", self),
    434         }
    435     }
    436 
    437     #[inline]
    438     fn stack_split(
    439         n: usize,
    440         n_main: u32,
    441     ) -> (i32, i32) {
    442         let n_main = n_main as i32;
    443         let n = n as i32;
    444 
    445         if n <= n_main {
    446             (n, 0i32)
    447         } else {
    448             (n_main, n - n_main)
    449         }
    450     }
    451 
    452     fn func(&self) -> LayoutFn {
    453         match *self {
    454             LayoutKind::Float => |_, _, active_map| {
    455                 let config = &LayoutKind::Float.config();
    456                 vec![(Disposition::Unchanged(config.decoration), true); active_map.len()]
    457             },
    458             LayoutKind::BLFloat => |_, _, active_map| {
    459                 let config = &LayoutKind::BLFloat.config();
    460                 vec![(Disposition::Unchanged(config.decoration), true); active_map.len()]
    461             },
    462             LayoutKind::SingleFloat => |_, _, active_map| {
    463                 let config = &LayoutKind::SingleFloat.config();
    464                 active_map
    465                     .into_iter()
    466                     .map(|b| (Disposition::Unchanged(config.decoration), b))
    467                     .collect()
    468             },
    469             LayoutKind::BLSingleFloat => |_, _, active_map| {
    470                 let config = &LayoutKind::BLSingleFloat.config();
    471                 active_map
    472                     .into_iter()
    473                     .map(|b| (Disposition::Unchanged(config.decoration), b))
    474                     .collect()
    475             },
    476             LayoutKind::Center => |region, data, active_map| {
    477                 let config = &LayoutKind::Center.config();
    478                 let (pos, dim) = region.values();
    479 
    480                 let h_comp = Layout::MAX_MAIN_COUNT + 1;
    481                 let w_ratio: f32 = data.main_factor / 0.95;
    482                 let h_ratio: f32 = (h_comp - data.main_count) as f32 / h_comp as f32;
    483 
    484                 active_map
    485                     .into_iter()
    486                     .map(|_| {
    487                         (
    488                             Disposition::Changed(
    489                                 Region {
    490                                     pos,
    491                                     dim,
    492                                 }
    493                                 .from_absolute_inner_center(Dim {
    494                                     w: (dim.w as f32 * w_ratio) as i32,
    495                                     h: (dim.h as f32 * h_ratio) as i32,
    496                                 }),
    497                                 config.decoration,
    498                             ),
    499                             true,
    500                         )
    501                     })
    502                     .collect()
    503             },
    504             LayoutKind::Monocle => |region, _, active_map| {
    505                 let config = &LayoutKind::Monocle.config();
    506                 let (pos, dim) = region.values();
    507 
    508                 active_map
    509                     .into_iter()
    510                     .map(|_| {
    511                         (
    512                             Disposition::Changed(
    513                                 Region {
    514                                     pos,
    515                                     dim,
    516                                 },
    517                                 config.decoration,
    518                             ),
    519                             true,
    520                         )
    521                     })
    522                     .collect()
    523             },
    524             LayoutKind::Paper => |region, data, active_map| {
    525                 const MIN_W_RATIO: f32 = 0.5;
    526 
    527                 let config = &LayoutKind::Paper.config();
    528                 let (pos, dim) = region.values();
    529                 let n = active_map.len();
    530 
    531                 if n == 1 {
    532                     return vec![(
    533                         Disposition::Changed(*region, Decoration::NO_DECORATION),
    534                         true,
    535                     )];
    536                 }
    537 
    538                 let cw = (dim.w as f32
    539                     * if data.main_factor > MIN_W_RATIO {
    540                         data.main_factor
    541                     } else {
    542                         MIN_W_RATIO
    543                     }) as i32;
    544 
    545                 let w = ((dim.w - cw) as usize / (n - 1)) as i32;
    546                 let mut after_active = false;
    547 
    548                 active_map
    549                     .into_iter()
    550                     .enumerate()
    551                     .map(|(i, active)| {
    552                         let i = i as i32;
    553 
    554                         (
    555                             Disposition::Changed(
    556                                 if active {
    557                                     after_active = true;
    558                                     Region::new(pos.x + i * w, pos.y, cw, dim.h)
    559                                 } else {
    560                                     let mut x = pos.x + i * w;
    561 
    562                                     if after_active {
    563                                         x += cw - w;
    564                                     }
    565 
    566                                     Region::new(x, pos.y, w, dim.h)
    567                                 },
    568                                 config.decoration,
    569                             ),
    570                             true,
    571                         )
    572                     })
    573                     .collect()
    574             },
    575             LayoutKind::SPaper => |region, data, active_map| {
    576                 let mut region = region.clone();
    577                 Layout::adjust_for_gap_size(&mut region, data.gap_size, &Zone::MIN_ZONE_DIM);
    578 
    579                 (Self::Paper.func())(&region, data, active_map)
    580             },
    581             LayoutKind::Stack => |region, data, active_map| {
    582                 let (pos, dim) = region.values();
    583                 let n = active_map.len();
    584 
    585                 if n == 1 {
    586                     return vec![(
    587                         Disposition::Changed(*region, Decoration::NO_DECORATION),
    588                         true,
    589                     )];
    590                 }
    591 
    592                 let (n_main, n_stack) = Self::stack_split(n, data.main_count);
    593                 let h_stack = if n_stack > 0 { dim.h / n_stack } else { 0 };
    594                 let h_main = if n_main > 0 { dim.h / n_main } else { 0 };
    595 
    596                 let div = if data.main_count > 0 {
    597                     (dim.w as f32 * data.main_factor) as i32
    598                 } else {
    599                     0
    600                 };
    601 
    602                 let config = &LayoutKind::Stack.config();
    603                 let main_count = data.main_count as i32;
    604 
    605                 active_map
    606                     .into_iter()
    607                     .enumerate()
    608                     .map(|(i, _)| {
    609                         let i = i as i32;
    610 
    611                         (
    612                             Disposition::Changed(
    613                                 if i < main_count {
    614                                     Region::new(
    615                                         pos.x,
    616                                         pos.y + (i * h_main),
    617                                         if n_stack == 0 { dim.w } else { div },
    618                                         h_main,
    619                                     )
    620                                 } else {
    621                                     Region::new(
    622                                         pos.x + div,
    623                                         pos.y + (i - main_count) * h_stack,
    624                                         dim.w - div,
    625                                         h_stack,
    626                                     )
    627                                 },
    628                                 config.decoration,
    629                             ),
    630                             true,
    631                         )
    632                     })
    633                     .collect()
    634             },
    635             LayoutKind::SStack => |region, data, active_map| {
    636                 let mut region = region.clone();
    637                 Layout::adjust_for_gap_size(&mut region, data.gap_size, &Zone::MIN_ZONE_DIM);
    638 
    639                 (Self::Stack.func())(&region, data, active_map)
    640             },
    641             LayoutKind::BStack => |region, data, active_map| {
    642                 let (pos, dim) = region.values();
    643                 let n = active_map.len();
    644 
    645                 if n == 1 {
    646                     return vec![(
    647                         Disposition::Changed(*region, Decoration::NO_DECORATION),
    648                         true,
    649                     )];
    650                 }
    651 
    652                 let (n_main, n_stack) = Self::stack_split(n, data.main_count);
    653 
    654                 let div = if data.main_count > 0 {
    655                     (dim.w as f32 * data.main_factor) as i32
    656                 } else {
    657                     0
    658                 };
    659 
    660                 let h_main = if n_main > 0 {
    661                     (if n_stack > 0 { div } else { dim.h }) / n_main
    662                 } else {
    663                     0
    664                 };
    665 
    666                 let w_stack = if n_stack > 0 { dim.w / n_stack } else { 0 };
    667 
    668                 let config = &LayoutKind::Stack.config();
    669                 let main_count = data.main_count as i32;
    670 
    671                 active_map
    672                     .into_iter()
    673                     .enumerate()
    674                     .map(|(i, _)| {
    675                         let i = i as i32;
    676 
    677                         (
    678                             Disposition::Changed(
    679                                 if i < main_count {
    680                                     Region::new(pos.x, pos.y + (i * h_main), dim.w, h_main)
    681                                 } else {
    682                                     Region::new(
    683                                         pos.x + ((i - main_count) * w_stack),
    684                                         pos.y + div,
    685                                         w_stack,
    686                                         dim.h - div,
    687                                     )
    688                                 },
    689                                 config.decoration,
    690                             ),
    691                             true,
    692                         )
    693                     })
    694                     .collect()
    695             },
    696             LayoutKind::SBStack => |region, data, active_map| {
    697                 let mut region = region.clone();
    698                 Layout::adjust_for_gap_size(&mut region, data.gap_size, &Zone::MIN_ZONE_DIM);
    699 
    700                 (Self::BStack.func())(&region, data, active_map)
    701             },
    702             LayoutKind::Horz => |_region, _data, _active_map| todo!(),
    703             LayoutKind::SHorz => |_region, _data, _active_map| todo!(),
    704             LayoutKind::Vert => |_region, _data, _active_map| todo!(),
    705             LayoutKind::SVert => |_region, _data, _active_map| todo!(),
    706 
    707             #[allow(unreachable_patterns)]
    708             _ => unimplemented!("{:?} does not have an associated function", self),
    709         }
    710     }
    711 }
    712 
    713 pub struct Layout {
    714     kind: LayoutKind,
    715     prev_kind: LayoutKind,
    716     data: HashMap<LayoutKind, LayoutData>,
    717 }
    718 
    719 impl Layout {
    720     #[inline]
    721     pub fn new() -> Self {
    722         let kind = LayoutKind::Stack;
    723         let mut data = HashMap::with_capacity(LayoutKind::COUNT);
    724 
    725         for kind in LayoutKind::iter() {
    726             data.insert(kind, kind.default_data());
    727         }
    728 
    729         Self {
    730             kind,
    731             prev_kind: kind,
    732             data,
    733         }
    734     }
    735 
    736     #[inline]
    737     pub fn with_kind(kind: LayoutKind) -> Self {
    738         let mut data = HashMap::with_capacity(LayoutKind::COUNT);
    739 
    740         for kind in LayoutKind::iter() {
    741             data.insert(kind, kind.default_data());
    742         }
    743 
    744         Self {
    745             kind,
    746             prev_kind: kind,
    747             data,
    748         }
    749     }
    750 
    751     #[inline]
    752     pub fn kind(&self) -> LayoutKind {
    753         self.kind
    754     }
    755 
    756     #[inline]
    757     pub fn prev_kind(&self) -> LayoutKind {
    758         self.prev_kind
    759     }
    760 
    761     #[inline]
    762     pub fn config(&self) -> LayoutConfig {
    763         self.kind.config()
    764     }
    765 
    766     #[inline]
    767     pub fn prev_data(&self) -> &LayoutData {
    768         self.data.get(&self.prev_kind).unwrap()
    769     }
    770 
    771     #[inline]
    772     pub fn data(&self) -> &LayoutData {
    773         self.data.get(&self.kind).unwrap()
    774     }
    775 
    776     #[inline]
    777     pub fn data_mut(&mut self) -> &mut LayoutData {
    778         self.data.get_mut(&self.kind).unwrap()
    779     }
    780 
    781     #[inline]
    782     pub fn default_data(&self) -> LayoutData {
    783         self.kind.default_data()
    784     }
    785 
    786     #[inline]
    787     pub fn set_kind(
    788         &mut self,
    789         kind: LayoutKind,
    790     ) -> Result<LayoutKind, StateChangeError> {
    791         if kind == self.kind {
    792             return Err(StateChangeError::EarlyStop);
    793         }
    794 
    795         self.prev_kind = self.kind;
    796         self.kind = kind;
    797 
    798         Ok(self.prev_kind)
    799     }
    800 
    801     #[inline]
    802     pub fn adjust_for_margin(
    803         region: Region,
    804         extents: &Extents,
    805     ) -> Region {
    806         Region {
    807             pos: Pos {
    808                 x: region.pos.x + extents.left,
    809                 y: region.pos.y + extents.top,
    810             },
    811             dim: Dim {
    812                 w: region.dim.w - extents.left - extents.right,
    813                 h: region.dim.h - extents.top - extents.bottom,
    814             },
    815         }
    816     }
    817 
    818     #[inline]
    819     pub fn adjust_for_gap_size(
    820         region: &mut Region,
    821         gap_size: u32,
    822         min_dim: &Dim,
    823     ) {
    824         let gap_size = gap_size as i32;
    825         let dim_gap = 2 * gap_size;
    826 
    827         let new_w = region.dim.w - dim_gap;
    828         if new_w < min_dim.w {
    829             region.pos.x += ((region.dim.w - min_dim.w) as f32 / 2f32) as i32;
    830             region.dim.w = min_dim.w;
    831         } else {
    832             region.dim.w = new_w;
    833             region.pos.x += gap_size;
    834         }
    835 
    836         let new_h = region.dim.h - dim_gap;
    837         if new_h < min_dim.h {
    838             region.pos.y += ((region.dim.h - min_dim.h) as f32 / 2f32) as i32;
    839             region.dim.h = min_dim.h;
    840         } else {
    841             region.dim.h = new_h;
    842             region.pos.y += gap_size;
    843         }
    844     }
    845 
    846     #[inline]
    847     pub fn adjust_for_border(
    848         region: &mut Region,
    849         border_width: u32,
    850         min_dim: &Dim,
    851     ) {
    852         let border_padding = 2 * border_width as i32;
    853 
    854         let new_w = region.dim.w - border_padding;
    855         region.dim.w = std::cmp::max(min_dim.w, new_w);
    856 
    857         let new_h = region.dim.h - border_padding;
    858         region.dim.h = std::cmp::max(min_dim.h, new_h);
    859     }
    860 }
    861 
    862 impl Default for Layout {
    863     fn default() -> Self {
    864         Self {
    865             kind: LayoutKind::Stack,
    866             prev_kind: LayoutKind::Stack,
    867             data: HashMap::new(),
    868         }
    869     }
    870 }
    871 
    872 pub trait Apply {
    873     fn apply(
    874         &self,
    875         region: Region,
    876         active_map: Vec<bool>,
    877     ) -> (PlacementMethod, Vec<(Disposition, bool)>);
    878 }
    879 
    880 impl Apply for Layout {
    881     #[inline]
    882     fn apply(
    883         &self,
    884         region: Region,
    885         active_map: Vec<bool>,
    886     ) -> (PlacementMethod, Vec<(Disposition, bool)>) {
    887         let config = self.kind.config();
    888         let data = self.data();
    889 
    890         let region = if config.margin {
    891             Self::adjust_for_margin(region, &data.margin)
    892         } else {
    893             region
    894         };
    895 
    896         (
    897             config.method,
    898             (self.kind.func())(&region, &data, active_map)
    899                 .into_iter()
    900                 .map(|(mut disposition, is_visible)| {
    901                     match disposition {
    902                         Disposition::Unchanged(_) => {},
    903                         Disposition::Changed(ref mut region, decoration) => {
    904                             if let Some(border) = decoration.border {
    905                                 Self::adjust_for_gap_size(
    906                                     region,
    907                                     border.width,
    908                                     &Zone::MIN_ZONE_DIM,
    909                                 );
    910                             }
    911 
    912                             if config.gap {
    913                                 Self::adjust_for_gap_size(
    914                                     region,
    915                                     data.gap_size,
    916                                     &Zone::MIN_ZONE_DIM,
    917                                 );
    918                             }
    919                         },
    920                     }
    921 
    922                     (disposition, is_visible)
    923                 })
    924                 .collect(),
    925         )
    926     }
    927 }
    928 
    929 impl PartialEq<Self> for Layout {
    930     fn eq(
    931         &self,
    932         other: &Self,
    933     ) -> bool {
    934         self.kind == other.kind && self.data.get(&self.kind) == other.data.get(&other.kind)
    935     }
    936 }
    937 
    938 impl Identify for Layout {
    939     #[inline(always)]
    940     fn id(&self) -> Ident {
    941         self.kind as Ident
    942     }
    943 }
    944 
    945 impl std::fmt::Debug for Layout {
    946     fn fmt(
    947         &self,
    948         f: &mut std::fmt::Formatter<'_>,
    949     ) -> std::fmt::Result {
    950         f.debug_struct("Layout")
    951             .field("kind", &self.kind)
    952             .field("prev_kind", &self.prev_kind)
    953             .field("data", &self.data.get(&self.kind))
    954             .finish()
    955     }
    956 }