layout.cc (39663B)
1 #include "../winsys/util.hh" 2 #include "client.hh" 3 #include "cycle.t.hh" 4 #include "defaults.hh" 5 #include "layout.hh" 6 7 #include <algorithm> 8 #include <cmath> 9 #include <sstream> 10 #include <fstream> 11 12 using namespace winsys; 13 14 LayoutHandler::Layout::Layout(LayoutKind kind) 15 : kind(kind), 16 config(kind_to_config(kind)), 17 default_data(kind_to_default_data(kind)), 18 data({}, true) 19 { 20 data.insert_at_back(new LayoutData(default_data)); 21 data.insert_at_back(new LayoutData(default_data)); 22 data.insert_at_back(new LayoutData(default_data)); 23 } 24 25 LayoutHandler::Layout::~Layout() 26 { 27 for (LayoutData_ptr data : data) 28 delete data; 29 } 30 31 32 LayoutHandler::LayoutHandler() 33 : m_kind(LayoutKind::Float), 34 m_prev_kind(LayoutKind::Float), 35 m_layouts({ 36 #define NEW_LAYOUT(layout) { layout, new Layout(layout) } 37 NEW_LAYOUT(LayoutKind::Float), 38 NEW_LAYOUT(LayoutKind::FramelessFloat), 39 NEW_LAYOUT(LayoutKind::SingleFloat), 40 NEW_LAYOUT(LayoutKind::FramelessSingleFloat), 41 NEW_LAYOUT(LayoutKind::Center), 42 NEW_LAYOUT(LayoutKind::Monocle), 43 NEW_LAYOUT(LayoutKind::MainDeck), 44 NEW_LAYOUT(LayoutKind::StackDeck), 45 NEW_LAYOUT(LayoutKind::DoubleDeck), 46 NEW_LAYOUT(LayoutKind::Paper), 47 NEW_LAYOUT(LayoutKind::CompactPaper), 48 NEW_LAYOUT(LayoutKind::DoubleStack), 49 NEW_LAYOUT(LayoutKind::CompactDoubleStack), 50 NEW_LAYOUT(LayoutKind::HorizontalStack), 51 NEW_LAYOUT(LayoutKind::CompactHorizontalStack), 52 NEW_LAYOUT(LayoutKind::VerticalStack), 53 NEW_LAYOUT(LayoutKind::CompactVerticalStack), 54 #undef NEW_LAYOUT 55 }), 56 mp_layout(m_layouts.at(m_kind)), 57 mp_prev_layout(m_layouts.at(m_kind)) 58 {} 59 60 LayoutHandler::~LayoutHandler() 61 { 62 for (auto [_,layout] : m_layouts) 63 delete layout; 64 } 65 66 67 void 68 LayoutHandler::arrange( 69 Region screen_region, 70 placement_vector placements, 71 client_iter begin, 72 client_iter end 73 ) const 74 { 75 if (mp_layout->config.margin) { 76 Layout::LayoutData_ptr data = *mp_layout->data.active_element(); 77 78 screen_region.pos.x += data->margin.left; 79 screen_region.pos.y += data->margin.top; 80 81 screen_region.dim.w -= data->margin.left + data->margin.right; 82 screen_region.dim.h -= data->margin.top + data->margin.bottom; 83 } 84 85 switch (m_kind) { 86 case LayoutKind::Float: 87 { 88 arrange_float(screen_region, placements, begin, end); 89 break; 90 } 91 case LayoutKind::FramelessFloat: 92 { 93 arrange_frameless_float(screen_region, placements, begin, end); 94 break; 95 } 96 case LayoutKind::SingleFloat: 97 { 98 arrange_single_float(screen_region, placements, begin, end); 99 break; 100 } 101 case LayoutKind::FramelessSingleFloat: 102 { 103 arrange_frameless_single_float(screen_region, placements, begin, end); 104 break; 105 } 106 case LayoutKind::Center: 107 { 108 arrange_center(screen_region, placements, begin, end); 109 break; 110 } 111 case LayoutKind::Monocle: 112 { 113 arrange_monocle(screen_region, placements, begin, end); 114 break; 115 } 116 case LayoutKind::MainDeck: 117 { 118 arrange_main_deck(screen_region, placements, begin, end); 119 break; 120 } 121 case LayoutKind::StackDeck: 122 { 123 arrange_stack_deck(screen_region, placements, begin, end); 124 break; 125 } 126 case LayoutKind::DoubleDeck: 127 { 128 arrange_double_deck(screen_region, placements, begin, end); 129 break; 130 } 131 case LayoutKind::Paper: 132 { 133 arrange_paper(screen_region, placements, begin, end); 134 break; 135 } 136 case LayoutKind::CompactPaper: 137 { 138 arrange_compact_paper(screen_region, placements, begin, end); 139 break; 140 } 141 case LayoutKind::DoubleStack: 142 { 143 arrange_double_stack(screen_region, placements, begin, end); 144 break; 145 } 146 case LayoutKind::CompactDoubleStack: 147 { 148 arrange_compact_double_stack(screen_region, placements, begin, end); 149 break; 150 } 151 case LayoutKind::HorizontalStack: 152 { 153 arrange_horizontal_stack(screen_region, placements, begin, end); 154 break; 155 } 156 case LayoutKind::CompactHorizontalStack: 157 { 158 arrange_compact_horizontal_stack(screen_region, placements, begin, end); 159 break; 160 } 161 case LayoutKind::VerticalStack: 162 { 163 arrange_vertical_stack(screen_region, placements, begin, end); 164 break; 165 } 166 case LayoutKind::CompactVerticalStack: 167 { 168 arrange_compact_vertical_stack(screen_region, placements, begin, end); 169 break; 170 } 171 } 172 173 if (mp_layout->config.gap) { 174 Layout::LayoutData_ptr data = *mp_layout->data.active_element(); 175 176 std::for_each( 177 placements.begin(), 178 placements.end(), 179 [data](Placement& placement) { 180 if (placement.region && !Client::is_free(placement.client)) { 181 placement.region->pos.x += data->gap_size; 182 placement.region->pos.y += data->gap_size; 183 placement.region->dim.w -= 2 * data->gap_size; 184 placement.region->dim.h -= 2 * data->gap_size; 185 186 placement.region->apply_minimum_dim(Client::MIN_CLIENT_DIM); 187 } 188 } 189 ); 190 } 191 } 192 193 194 LayoutHandler::LayoutKind 195 LayoutHandler::kind() const 196 { 197 return m_kind; 198 } 199 200 void 201 LayoutHandler::set_kind(LayoutKind kind) 202 { 203 if (kind == m_kind) 204 return; 205 206 m_prev_kind = m_kind; 207 m_kind = kind; 208 209 mp_prev_layout = mp_layout; 210 mp_layout = *Util::const_retrieve(m_layouts, m_kind); 211 } 212 213 void 214 LayoutHandler::set_prev_kind() 215 { 216 if (m_prev_kind == m_kind) 217 return; 218 219 std::swap(m_kind, m_prev_kind); 220 std::swap(mp_layout, mp_prev_layout); 221 } 222 223 224 bool 225 LayoutHandler::layout_is_free() const 226 { 227 return mp_layout->config.method == Placement::PlacementMethod::Free; 228 } 229 230 bool 231 LayoutHandler::layout_has_margin() const 232 { 233 return mp_layout->config.margin; 234 } 235 236 bool 237 LayoutHandler::layout_has_gap() const 238 { 239 return mp_layout->config.gap; 240 } 241 242 bool 243 LayoutHandler::layout_is_persistent() const 244 { 245 return mp_layout->config.persistent; 246 } 247 248 bool 249 LayoutHandler::layout_is_single() const 250 { 251 return mp_layout->config.single; 252 } 253 254 bool 255 LayoutHandler::layout_wraps() const 256 { 257 return mp_layout->config.wraps; 258 } 259 260 261 std::size_t 262 LayoutHandler::gap_size() const 263 { 264 return (*mp_layout->data.active_element())->gap_size; 265 } 266 267 std::size_t 268 LayoutHandler::main_count() const 269 { 270 return (*mp_layout->data.active_element())->main_count; 271 } 272 273 float 274 LayoutHandler::main_factor() const 275 { 276 return (*mp_layout->data.active_element())->main_factor; 277 } 278 279 Extents 280 LayoutHandler::margin() const 281 { 282 return (*mp_layout->data.active_element())->margin; 283 } 284 285 286 void 287 LayoutHandler::copy_data_from_prev_layout() 288 { 289 *(*mp_layout->data.active_element()) 290 = *(*mp_prev_layout->data.active_element()); 291 } 292 293 void 294 LayoutHandler::set_prev_layout_data() 295 { 296 std::optional<Layout::LayoutData_ptr> prev_data 297 = mp_layout->data.prev_active_element(); 298 299 if (prev_data) 300 mp_layout->data.activate_element(*prev_data); 301 } 302 303 304 void 305 LayoutHandler::change_gap_size(Util::Change<int> change) 306 { 307 Layout::LayoutData_ptr data = *mp_layout->data.active_element(); 308 int value = static_cast<int>(data->gap_size) + change; 309 310 if (value <= 0) 311 data->gap_size = 0; 312 else if (static_cast<std::size_t>(value) >= Layout::LayoutData::MAX_GAP_SIZE) 313 data->gap_size = Layout::LayoutData::MAX_GAP_SIZE; 314 else 315 data->gap_size = value; 316 } 317 318 void 319 LayoutHandler::change_main_count(Util::Change<int> change) 320 { 321 Layout::LayoutData_ptr data = *mp_layout->data.active_element(); 322 int value = static_cast<int>(data->main_count) + change; 323 324 if (value <= 0) 325 data->main_count = 0; 326 else if (static_cast<std::size_t>(value) >= Layout::LayoutData::MAX_MAIN_COUNT) 327 data->main_count = Layout::LayoutData::MAX_MAIN_COUNT; 328 else 329 data->main_count = value; 330 } 331 332 void 333 LayoutHandler::change_main_factor(Util::Change<float> change) 334 { 335 Layout::LayoutData_ptr data = *mp_layout->data.active_element(); 336 float value = data->main_factor + change; 337 338 if (value <= 0.05f) 339 data->main_factor = 0.05f; 340 else if (value >= 0.95f) 341 data->main_factor = 0.95f; 342 else 343 data->main_factor = value; 344 } 345 346 void 347 LayoutHandler::change_margin(Util::Change<int> change) 348 { 349 change_margin(Edge::Left, change); 350 change_margin(Edge::Top, change); 351 change_margin(Edge::Right, change); 352 change_margin(Edge::Bottom, change); 353 } 354 355 void 356 LayoutHandler::change_margin(Edge edge, Util::Change<int> change) 357 { 358 Layout::LayoutData_ptr data = *mp_layout->data.active_element(); 359 int* margin; 360 const int* max_value; 361 362 switch (edge) { 363 case Edge::Left: 364 { 365 margin = &data->margin.left; 366 max_value = &Layout::LayoutData::MAX_MARGIN.left; 367 break; 368 } 369 case Edge::Top: 370 { 371 margin = &data->margin.top; 372 max_value = &Layout::LayoutData::MAX_MARGIN.top; 373 break; 374 } 375 case Edge::Right: 376 { 377 margin = &data->margin.right; 378 max_value = &Layout::LayoutData::MAX_MARGIN.right; 379 break; 380 } 381 case Edge::Bottom: 382 { 383 margin = &data->margin.bottom; 384 max_value = &Layout::LayoutData::MAX_MARGIN.bottom; 385 break; 386 } 387 } 388 389 int value = *margin + change; 390 391 if (value <= 0) 392 *margin = 0; 393 else if (value >= *max_value) 394 *margin = *max_value; 395 else 396 *margin = value; 397 } 398 399 void 400 LayoutHandler::reset_gap_size() 401 { 402 (*mp_layout->data.active_element())->gap_size 403 = mp_layout->default_data.gap_size; 404 } 405 406 void 407 LayoutHandler::reset_margin() 408 { 409 (*mp_layout->data.active_element())->margin 410 = mp_layout->default_data.margin; 411 } 412 413 void 414 LayoutHandler::reset_layout_data() 415 { 416 *(*mp_layout->data.active_element()) 417 = mp_layout->default_data; 418 } 419 420 void 421 LayoutHandler::cycle_layout_data(Direction direction) 422 { 423 mp_layout->data.cycle_active(direction); 424 } 425 426 427 void 428 LayoutHandler::save_layout(std::size_t number) const 429 { 430 std::stringstream datadir_ss; 431 std::string home_path = std::getenv("HOME"); 432 433 if (const char* env_xdgdata = std::getenv("XDG_DATA_HOME")) 434 datadir_ss << env_xdgdata << "/" << WM_NAME << "/"; 435 else 436 datadir_ss << home_path << "/.local/share/" << WM_NAME << "/" 437 << "layout_" << number; 438 439 std::string file_path = datadir_ss.str(); 440 441 std::vector<Layout::LayoutData> data; 442 data.reserve(mp_layout->data.size()); 443 for (Layout::LayoutData_ptr data_ptr : mp_layout->data.as_deque()) 444 data.push_back(*data_ptr); 445 446 typename std::vector<Layout::LayoutData>::size_type size 447 = data.size(); 448 449 std::ofstream out(file_path, std::ios::out | std::ios::binary); 450 out.write(reinterpret_cast<const char*>(&m_kind), sizeof(LayoutKind)); 451 out.write(reinterpret_cast<const char*>(&size), sizeof(size)); 452 out.write(reinterpret_cast<const char*>(&data[0]), 453 data.size() * sizeof(Layout::LayoutData)); 454 out.close(); 455 } 456 457 void 458 LayoutHandler::load_layout(std::size_t number) 459 { 460 std::stringstream datadir_ss; 461 std::string home_path = std::getenv("HOME"); 462 463 if (const char* env_xdgdata = std::getenv("XDG_DATA_HOME")) 464 datadir_ss << env_xdgdata << "/" << WM_NAME << "/"; 465 else 466 datadir_ss << home_path << "/.local/share/" << WM_NAME << "/" 467 << "layout_" << number; 468 469 std::string file_path = datadir_ss.str(); 470 471 LayoutKind kind; 472 std::vector<Layout::LayoutData> data; 473 typename std::vector<Layout::LayoutData>::size_type size = 0; 474 475 std::ifstream in(file_path, std::ios::in | std::ios::binary); 476 if (in.good()) { 477 in.read(reinterpret_cast<char*>(&kind), sizeof(LayoutKind)); 478 in.read(reinterpret_cast<char*>(&size), sizeof(size)); 479 480 data.resize(size, mp_layout->default_data); 481 in.read(reinterpret_cast<char*>(&data[0]), 482 data.size() * sizeof(Layout::LayoutData)); 483 484 set_kind(kind); 485 for (Layout::LayoutData_ptr data : mp_layout->data) 486 delete data; 487 488 mp_layout->data.clear(); 489 for (auto data_ : data) 490 mp_layout->data.insert_at_back(new Layout::LayoutData(data_)); 491 } 492 in.close(); 493 } 494 495 496 void 497 LayoutHandler::arrange_float( 498 Region, 499 placement_vector placements, 500 client_iter begin, 501 client_iter end 502 ) const 503 { 504 std::transform( 505 begin, 506 end, 507 std::back_inserter(placements), 508 [this](Client_ptr client) -> Placement { 509 return Placement { 510 mp_layout->config.method, 511 client, 512 mp_layout->config.decoration, 513 client->free_region 514 }; 515 } 516 ); 517 } 518 519 void 520 LayoutHandler::arrange_frameless_float( 521 Region screen_region, 522 placement_vector placements, 523 client_iter begin, 524 client_iter end 525 ) const 526 { 527 arrange_float(screen_region, placements, begin, end); 528 } 529 530 void 531 LayoutHandler::arrange_single_float( 532 Region, 533 placement_vector placements, 534 client_iter begin, 535 client_iter end 536 ) const 537 { 538 std::transform( 539 begin, 540 end, 541 std::back_inserter(placements), 542 [this](Client_ptr client) -> Placement { 543 return Placement { 544 mp_layout->config.method, 545 client, 546 mp_layout->config.decoration, 547 client->focused ? std::optional(client->free_region) : std::nullopt 548 }; 549 } 550 ); 551 } 552 553 void 554 LayoutHandler::arrange_frameless_single_float( 555 Region screen_region, 556 placement_vector placements, 557 client_iter begin, 558 client_iter end 559 ) const 560 { 561 arrange_single_float(screen_region, placements, begin, end); 562 } 563 564 void 565 LayoutHandler::arrange_center( 566 Region screen_region, 567 placement_vector placements, 568 client_iter begin, 569 client_iter end 570 ) const 571 { 572 const Layout::LayoutData_ptr data = *mp_layout->data.active_element(); 573 574 std::size_t h_comp = Layout::LayoutData::MAX_MAIN_COUNT; 575 float w_ratio = data->main_factor / 0.95; 576 float h_ratio = static_cast<float>((h_comp - data->main_count)) 577 / static_cast<float>(h_comp); 578 579 std::transform( 580 begin, 581 end, 582 std::back_inserter(placements), 583 [=,this](Client_ptr client) -> Placement { 584 Region region = screen_region; 585 586 int w = region.dim.w * w_ratio; 587 int h = region.dim.h * h_ratio; 588 589 if (w <= region.dim.w) 590 region.pos.x += (region.dim.w - w) / 2.f; 591 592 if (h <= region.dim.h) 593 region.pos.y += (region.dim.h - h) / 2.f; 594 595 region.dim = { w, h }; 596 597 return Placement { 598 mp_layout->config.method, 599 client, 600 mp_layout->config.decoration, 601 region 602 }; 603 } 604 ); 605 } 606 607 void 608 LayoutHandler::arrange_monocle( 609 Region screen_region, 610 placement_vector placements, 611 client_iter begin, 612 client_iter end 613 ) const 614 { 615 std::transform( 616 begin, 617 end, 618 std::back_inserter(placements), 619 [=,this](Client_ptr client) { 620 return Placement { 621 mp_layout->config.method, 622 client, 623 mp_layout->config.decoration, 624 screen_region 625 }; 626 } 627 ); 628 } 629 630 void 631 LayoutHandler::arrange_main_deck( 632 Region screen_region, 633 placement_vector placements, 634 client_iter begin, 635 client_iter end 636 ) const 637 { 638 const Layout::LayoutData_ptr data = *mp_layout->data.active_element(); 639 std::size_t n = end - begin; 640 641 if (n == 1) { 642 placements.emplace_back(Placement { 643 mp_layout->config.method, 644 *begin, 645 Decoration::NO_DECORATION, 646 screen_region 647 }); 648 649 return; 650 } 651 652 std::size_t n_main; 653 std::size_t n_stack; 654 655 if (n <= data->main_count) { 656 n_main = n; 657 n_stack = 0; 658 } else { 659 n_main = data->main_count; 660 n_stack = n - n_main; 661 } 662 663 int w_main 664 = data->main_count > 0 665 ? static_cast<float>(screen_region.dim.w) * data->main_factor 666 : 0; 667 668 int x_stack = screen_region.pos.x + w_main; 669 int w_stack = screen_region.dim.w - w_main; 670 int h_main = n_main > 0 ? screen_region.dim.h : 0; 671 int h_stack = n_stack > 0 ? screen_region.dim.h / n_stack : 0; 672 673 std::size_t i = 0; 674 675 std::transform( 676 begin, 677 end, 678 std::back_inserter(placements), 679 [=,this,&i](Client_ptr client) -> Placement { 680 if (i < data->main_count) { 681 ++i; 682 return Placement { 683 mp_layout->config.method, 684 client, 685 mp_layout->config.decoration, 686 Region { 687 Pos { 688 screen_region.pos.x, 689 screen_region.pos.y 690 }, 691 Dim { 692 n_stack == 0 ? screen_region.dim.w : w_main, 693 h_main 694 } 695 } 696 }; 697 } else { 698 return Placement { 699 mp_layout->config.method, 700 client, 701 mp_layout->config.decoration, 702 Region { 703 Pos { 704 x_stack, 705 screen_region.pos.y 706 + static_cast<int>((i++ - data->main_count) * h_stack) 707 }, 708 Dim { 709 w_stack, 710 h_stack 711 } 712 } 713 }; 714 } 715 } 716 ); 717 } 718 719 void 720 LayoutHandler::arrange_stack_deck( 721 Region screen_region, 722 placement_vector placements, 723 client_iter begin, 724 client_iter end 725 ) const 726 { 727 const Layout::LayoutData_ptr data = *mp_layout->data.active_element(); 728 std::size_t n = end - begin; 729 730 if (n == 1) { 731 placements.emplace_back(Placement { 732 mp_layout->config.method, 733 *begin, 734 Decoration::NO_DECORATION, 735 screen_region 736 }); 737 738 return; 739 } 740 741 std::size_t n_main; 742 std::size_t n_stack; 743 744 if (n <= data->main_count) { 745 n_main = n; 746 n_stack = 0; 747 } else { 748 n_main = data->main_count; 749 n_stack = n - n_main; 750 } 751 752 int w_main 753 = data->main_count > 0 754 ? static_cast<float>(screen_region.dim.w) * data->main_factor 755 : 0; 756 757 int x_stack = screen_region.pos.x + w_main; 758 int w_stack = screen_region.dim.w - w_main; 759 int h_main = n_main > 0 ? screen_region.dim.h / n_main : 0; 760 int h_stack = n_stack > 0 ? screen_region.dim.h : 0; 761 762 std::size_t i = 0; 763 764 std::transform( 765 begin, 766 end, 767 std::back_inserter(placements), 768 [=,this,&i](Client_ptr client) -> Placement { 769 if (i < data->main_count) { 770 return Placement { 771 mp_layout->config.method, 772 client, 773 mp_layout->config.decoration, 774 Region { 775 Pos { 776 screen_region.pos.x, 777 screen_region.pos.y 778 + static_cast<int>(i++) * h_main 779 }, 780 Dim { 781 n_stack == 0 ? screen_region.dim.w : w_main, 782 h_main 783 } 784 } 785 }; 786 } else { 787 ++i; 788 return Placement { 789 mp_layout->config.method, 790 client, 791 mp_layout->config.decoration, 792 Region { 793 Pos { 794 x_stack, 795 screen_region.pos.y 796 }, 797 Dim { 798 w_stack, 799 h_stack 800 } 801 } 802 }; 803 } 804 } 805 ); 806 } 807 808 void 809 LayoutHandler::arrange_double_deck( 810 Region screen_region, 811 placement_vector placements, 812 client_iter begin, 813 client_iter end 814 ) const 815 { 816 const Layout::LayoutData_ptr data = *mp_layout->data.active_element(); 817 std::size_t n = end - begin; 818 819 if (n == 1) { 820 placements.emplace_back(Placement { 821 mp_layout->config.method, 822 *begin, 823 Decoration::NO_DECORATION, 824 screen_region 825 }); 826 827 return; 828 } 829 830 std::size_t n_main; 831 std::size_t n_stack; 832 833 if (n <= data->main_count) { 834 n_main = n; 835 n_stack = 0; 836 } else { 837 n_main = data->main_count; 838 n_stack = n - n_main; 839 } 840 841 int w_main 842 = data->main_count > 0 843 ? static_cast<float>(screen_region.dim.w) * data->main_factor 844 : 0; 845 846 int x_stack = screen_region.pos.x + w_main; 847 int w_stack = screen_region.dim.w - w_main; 848 int h_main = n_main > 0 ? screen_region.dim.h : 0; 849 int h_stack = n_stack > 0 ? screen_region.dim.h : 0; 850 851 std::size_t i = 0; 852 853 std::transform( 854 begin, 855 end, 856 std::back_inserter(placements), 857 [=,this,&i](Client_ptr client) -> Placement { 858 if (i++ < data->main_count) { 859 return Placement { 860 mp_layout->config.method, 861 client, 862 mp_layout->config.decoration, 863 Region { 864 Pos { 865 screen_region.pos.x, 866 screen_region.pos.y 867 }, 868 Dim { 869 n_stack == 0 ? screen_region.dim.w : w_main, 870 h_main 871 } 872 } 873 }; 874 } else { 875 return Placement { 876 mp_layout->config.method, 877 client, 878 mp_layout->config.decoration, 879 Region { 880 Pos { 881 x_stack, 882 screen_region.pos.y 883 }, 884 Dim { 885 w_stack, 886 h_stack 887 } 888 } 889 }; 890 } 891 } 892 ); 893 } 894 895 void 896 LayoutHandler::arrange_paper( 897 Region screen_region, 898 placement_vector placements, 899 client_iter begin, 900 client_iter end 901 ) const 902 { 903 static const float MIN_W_RATIO = 0.5; 904 905 const Layout::LayoutData_ptr data = *mp_layout->data.active_element(); 906 std::size_t n = end - begin; 907 908 if (n == 1) { 909 placements.emplace_back(Placement { 910 mp_layout->config.method, 911 *begin, 912 Decoration::NO_DECORATION, 913 screen_region 914 }); 915 916 return; 917 } 918 919 int cw; 920 if (data->main_factor > MIN_W_RATIO) { 921 cw = screen_region.dim.w * data->main_factor; 922 } else { 923 cw = screen_region.dim.w * MIN_W_RATIO; 924 } 925 926 int w = static_cast<float>(screen_region.dim.w - cw) 927 / static_cast<float>(n - 1); 928 929 bool contains_active = false; 930 const auto last_active = std::max_element( 931 begin, 932 end, 933 [&contains_active](const Client_ptr lhs, const Client_ptr rhs) { 934 if (lhs->focused) { 935 contains_active = true; 936 return false; 937 } else if (rhs->focused) { 938 contains_active = true; 939 return true; 940 } 941 942 return lhs->last_focused < rhs->last_focused; 943 } 944 ); 945 946 bool after_active = false; 947 std::size_t i = 0; 948 949 std::transform( 950 begin, 951 end, 952 std::back_inserter(placements), 953 [=,this,&after_active,&i](Client_ptr client) -> Placement { 954 int x = screen_region.pos.x + static_cast<int>(i++ * w); 955 956 if ((!contains_active && *last_active == client) || client->focused) { 957 after_active = true; 958 959 return Placement { 960 mp_layout->config.method, 961 client, 962 mp_layout->config.decoration, 963 Region { 964 Pos { 965 x, 966 screen_region.pos.y 967 }, 968 Dim { 969 cw, 970 screen_region.dim.h 971 } 972 } 973 }; 974 } else { 975 if (after_active) 976 x += cw - w; 977 978 return Placement { 979 mp_layout->config.method, 980 client, 981 mp_layout->config.decoration, 982 Region { 983 Pos { 984 x, 985 screen_region.pos.y 986 }, 987 Dim { 988 w, 989 screen_region.dim.h 990 } 991 } 992 }; 993 } 994 } 995 ); 996 } 997 998 void 999 LayoutHandler::arrange_compact_paper( 1000 Region screen_region, 1001 placement_vector placements, 1002 client_iter begin, 1003 client_iter end 1004 ) const 1005 { 1006 arrange_paper(screen_region, placements, begin, end); 1007 } 1008 1009 void 1010 LayoutHandler::arrange_double_stack( 1011 Region screen_region, 1012 placement_vector placements, 1013 client_iter begin, 1014 client_iter end 1015 ) const 1016 { 1017 const Layout::LayoutData_ptr data = *mp_layout->data.active_element(); 1018 std::size_t n = end - begin; 1019 1020 if (n == 1) { 1021 placements.emplace_back(Placement { 1022 mp_layout->config.method, 1023 *begin, 1024 Decoration::NO_DECORATION, 1025 screen_region 1026 }); 1027 1028 return; 1029 } 1030 1031 std::size_t n_main; 1032 std::size_t n_stack; 1033 1034 if (n <= data->main_count) { 1035 n_main = n; 1036 n_stack = 0; 1037 } else { 1038 n_main = data->main_count; 1039 n_stack = n - n_main; 1040 } 1041 1042 int w_main 1043 = data->main_count > 0 1044 ? static_cast<float>(screen_region.dim.w) * data->main_factor 1045 : 0; 1046 1047 int x_stack = screen_region.pos.x + w_main; 1048 int w_stack = screen_region.dim.w - w_main; 1049 int h_main = n_main > 0 ? screen_region.dim.h / n_main : 0; 1050 int h_stack = n_stack > 0 ? screen_region.dim.h / n_stack : 0; 1051 1052 std::size_t i = 0; 1053 1054 std::transform( 1055 begin, 1056 end, 1057 std::back_inserter(placements), 1058 [=,this,&i](Client_ptr client) -> Placement { 1059 if (i < data->main_count) { 1060 return Placement { 1061 mp_layout->config.method, 1062 client, 1063 mp_layout->config.decoration, 1064 Region { 1065 Pos { 1066 screen_region.pos.x, 1067 screen_region.pos.y 1068 + static_cast<int>(i++) * h_main 1069 }, 1070 Dim { 1071 n_stack == 0 ? screen_region.dim.w : w_main, 1072 h_main 1073 } 1074 } 1075 }; 1076 } else { 1077 return Placement { 1078 mp_layout->config.method, 1079 client, 1080 mp_layout->config.decoration, 1081 Region { 1082 Pos { 1083 x_stack, 1084 screen_region.pos.y 1085 + static_cast<int>((i++ - data->main_count) * h_stack) 1086 }, 1087 Dim { 1088 w_stack, 1089 h_stack 1090 } 1091 } 1092 }; 1093 } 1094 } 1095 ); 1096 } 1097 1098 void 1099 LayoutHandler::arrange_compact_double_stack( 1100 Region screen_region, 1101 placement_vector placements, 1102 client_iter begin, 1103 client_iter end 1104 ) const 1105 { 1106 arrange_double_stack(screen_region, placements, begin, end); 1107 } 1108 1109 void 1110 LayoutHandler::arrange_horizontal_stack( 1111 Region screen_region, 1112 placement_vector placements, 1113 client_iter begin, 1114 client_iter end 1115 ) const 1116 { 1117 std::size_t n = end - begin; 1118 1119 if (n == 1) { 1120 placements.emplace_back(Placement { 1121 mp_layout->config.method, 1122 *begin, 1123 Decoration::NO_DECORATION, 1124 screen_region 1125 }); 1126 1127 return; 1128 } 1129 1130 int w = std::lround(static_cast<float>(screen_region.dim.w) / n); 1131 std::size_t i = 0; 1132 1133 std::transform( 1134 begin, 1135 end, 1136 std::back_inserter(placements), 1137 [=,this,&i](Client_ptr client) -> Placement { 1138 return Placement { 1139 mp_layout->config.method, 1140 client, 1141 mp_layout->config.decoration, 1142 Region { 1143 Pos { 1144 screen_region.pos.x + static_cast<int>(i++ * w), 1145 screen_region.pos.y 1146 }, 1147 Dim { 1148 w, 1149 screen_region.dim.h 1150 } 1151 } 1152 }; 1153 } 1154 ); 1155 } 1156 1157 void 1158 LayoutHandler::arrange_compact_horizontal_stack( 1159 Region screen_region, 1160 placement_vector placements, 1161 client_iter begin, 1162 client_iter end 1163 ) const 1164 { 1165 arrange_horizontal_stack(screen_region, placements, begin, end); 1166 } 1167 1168 void 1169 LayoutHandler::arrange_vertical_stack( 1170 Region screen_region, 1171 placement_vector placements, 1172 client_iter begin, 1173 client_iter end 1174 ) const 1175 { 1176 std::size_t n = end - begin; 1177 1178 if (n == 1) { 1179 placements.emplace_back(Placement { 1180 mp_layout->config.method, 1181 *begin, 1182 Decoration::NO_DECORATION, 1183 screen_region 1184 }); 1185 1186 return; 1187 } 1188 1189 int h = std::lround(static_cast<float>(screen_region.dim.h) / n); 1190 std::size_t i = 0; 1191 1192 std::transform( 1193 begin, 1194 end, 1195 std::back_inserter(placements), 1196 [=,this,&i](Client_ptr client) -> Placement { 1197 return Placement { 1198 mp_layout->config.method, 1199 client, 1200 mp_layout->config.decoration, 1201 Region { 1202 Pos { 1203 screen_region.pos.x, 1204 screen_region.pos.y + static_cast<int>(i++ * h) 1205 }, 1206 Dim { 1207 screen_region.dim.w, 1208 h 1209 } 1210 } 1211 }; 1212 } 1213 ); 1214 } 1215 1216 void 1217 LayoutHandler::arrange_compact_vertical_stack( 1218 Region screen_region, 1219 placement_vector placements, 1220 client_iter begin, 1221 client_iter end 1222 ) const 1223 { 1224 arrange_vertical_stack(screen_region, placements, begin, end); 1225 } 1226 1227 1228 LayoutHandler::Layout::LayoutConfig 1229 LayoutHandler::Layout::kind_to_config(LayoutKind kind) 1230 { 1231 switch (kind) { 1232 case LayoutKind::Float: 1233 { 1234 return LayoutConfig { 1235 Placement::PlacementMethod::Free, 1236 Decoration::FREE_DECORATION, 1237 false, 1238 false, 1239 false, 1240 false, 1241 true 1242 }; 1243 } 1244 case LayoutKind::FramelessFloat: 1245 { 1246 return LayoutConfig { 1247 Placement::PlacementMethod::Free, 1248 Decoration::NO_DECORATION, 1249 false, 1250 false, 1251 false, 1252 false, 1253 true 1254 }; 1255 } 1256 case LayoutKind::SingleFloat: 1257 { 1258 return LayoutConfig { 1259 Placement::PlacementMethod::Free, 1260 Decoration::FREE_DECORATION, 1261 false, 1262 false, 1263 true, 1264 true, 1265 true 1266 }; 1267 } 1268 case LayoutKind::FramelessSingleFloat: 1269 { 1270 return LayoutConfig { 1271 Placement::PlacementMethod::Free, 1272 Decoration::NO_DECORATION, 1273 false, 1274 false, 1275 true, 1276 true, 1277 true 1278 }; 1279 } 1280 case LayoutKind::Center: 1281 { 1282 return LayoutConfig { 1283 Placement::PlacementMethod::Tile, 1284 Decoration::NO_DECORATION, 1285 true, 1286 true, 1287 false, 1288 false, 1289 true 1290 }; 1291 } 1292 case LayoutKind::Monocle: 1293 { 1294 return LayoutConfig { 1295 Placement::PlacementMethod::Tile, 1296 Decoration::NO_DECORATION, 1297 true, 1298 true, 1299 false, 1300 false, 1301 true 1302 }; 1303 } 1304 case LayoutKind::MainDeck: 1305 { 1306 return LayoutConfig { 1307 Placement::PlacementMethod::Tile, 1308 Decoration { 1309 std::nullopt, 1310 Frame { 1311 Extents { 0, 0, 3, 0 }, 1312 ColorScheme::DEFAULT_COLOR_SCHEME 1313 } 1314 }, 1315 true, 1316 true, 1317 false, 1318 false, 1319 true 1320 }; 1321 } 1322 case LayoutKind::StackDeck: 1323 { 1324 return LayoutConfig { 1325 Placement::PlacementMethod::Tile, 1326 Decoration { 1327 std::nullopt, 1328 Frame { 1329 Extents { 0, 0, 3, 0 }, 1330 ColorScheme::DEFAULT_COLOR_SCHEME 1331 } 1332 }, 1333 true, 1334 true, 1335 false, 1336 false, 1337 true 1338 }; 1339 } 1340 case LayoutKind::DoubleDeck: 1341 { 1342 return LayoutConfig { 1343 Placement::PlacementMethod::Tile, 1344 Decoration { 1345 std::nullopt, 1346 Frame { 1347 Extents { 0, 0, 3, 0 }, 1348 ColorScheme::DEFAULT_COLOR_SCHEME 1349 } 1350 }, 1351 true, 1352 true, 1353 false, 1354 false, 1355 true 1356 }; 1357 } 1358 case LayoutKind::Paper: 1359 { 1360 return LayoutConfig { 1361 Placement::PlacementMethod::Tile, 1362 Decoration { 1363 std::nullopt, 1364 Frame { 1365 Extents { 1, 1, 0, 0 }, 1366 ColorScheme::DEFAULT_COLOR_SCHEME 1367 } 1368 }, 1369 true, 1370 true, 1371 true, 1372 false, 1373 false 1374 }; 1375 } 1376 case LayoutKind::CompactPaper: 1377 { 1378 return LayoutConfig { 1379 Placement::PlacementMethod::Tile, 1380 Decoration { 1381 std::nullopt, 1382 Frame { 1383 Extents { 1, 1, 0, 0 }, 1384 ColorScheme::DEFAULT_COLOR_SCHEME 1385 } 1386 }, 1387 true, 1388 false, 1389 true, 1390 false, 1391 false 1392 }; 1393 } 1394 case LayoutKind::DoubleStack: 1395 { 1396 return LayoutConfig { 1397 Placement::PlacementMethod::Tile, 1398 Decoration { 1399 std::nullopt, 1400 Frame { 1401 Extents { 0, 0, 3, 0 }, 1402 ColorScheme::DEFAULT_COLOR_SCHEME 1403 } 1404 }, 1405 true, 1406 true, 1407 false, 1408 false, 1409 true 1410 }; 1411 } 1412 case LayoutKind::CompactDoubleStack: 1413 { 1414 return LayoutConfig { 1415 Placement::PlacementMethod::Tile, 1416 Decoration { 1417 std::nullopt, 1418 Frame { 1419 Extents { 0, 0, 3, 0 }, 1420 ColorScheme::DEFAULT_COLOR_SCHEME 1421 } 1422 }, 1423 true, 1424 false, 1425 false, 1426 false, 1427 true 1428 }; 1429 } 1430 case LayoutKind::HorizontalStack: 1431 { 1432 return LayoutConfig { 1433 Placement::PlacementMethod::Tile, 1434 Decoration { 1435 std::nullopt, 1436 Frame { 1437 Extents { 0, 0, 3, 0 }, 1438 ColorScheme::DEFAULT_COLOR_SCHEME 1439 } 1440 }, 1441 true, 1442 true, 1443 false, 1444 false, 1445 true 1446 }; 1447 } 1448 case LayoutKind::CompactHorizontalStack: 1449 { 1450 return LayoutConfig { 1451 Placement::PlacementMethod::Tile, 1452 Decoration { 1453 std::nullopt, 1454 Frame { 1455 Extents { 0, 0, 3, 0 }, 1456 ColorScheme::DEFAULT_COLOR_SCHEME 1457 } 1458 }, 1459 true, 1460 false, 1461 false, 1462 false, 1463 true 1464 }; 1465 } 1466 case LayoutKind::VerticalStack: 1467 { 1468 return LayoutConfig { 1469 Placement::PlacementMethod::Tile, 1470 Decoration { 1471 std::nullopt, 1472 Frame { 1473 Extents { 3, 0, 0, 0 }, 1474 ColorScheme::DEFAULT_COLOR_SCHEME 1475 } 1476 }, 1477 true, 1478 true, 1479 false, 1480 false, 1481 true 1482 }; 1483 } 1484 case LayoutKind::CompactVerticalStack: 1485 { 1486 return LayoutConfig { 1487 Placement::PlacementMethod::Tile, 1488 Decoration { 1489 std::nullopt, 1490 Frame { 1491 Extents { 3, 0, 0, 0 }, 1492 ColorScheme::DEFAULT_COLOR_SCHEME 1493 } 1494 }, 1495 true, 1496 false, 1497 false, 1498 false, 1499 true 1500 }; 1501 } 1502 default: Util::die("no associated configuration defined"); 1503 } 1504 1505 return kind_to_config(LayoutKind::Float); 1506 } 1507 1508 LayoutHandler::Layout::LayoutData 1509 LayoutHandler::Layout::kind_to_default_data(LayoutKind kind) 1510 { 1511 switch (kind) { 1512 case LayoutKind::Center: 1513 { 1514 return Layout::LayoutData { 1515 Extents { 0, 0, 0, 0 }, 1516 0, 1517 5, 1518 .40f 1519 }; 1520 } 1521 case LayoutKind::Float: // fallthrough 1522 case LayoutKind::FramelessFloat: // fallthrough 1523 case LayoutKind::SingleFloat: // fallthrough 1524 case LayoutKind::FramelessSingleFloat: // fallthrough 1525 case LayoutKind::Monocle: // fallthrough 1526 case LayoutKind::MainDeck: // fallthrough 1527 case LayoutKind::StackDeck: // fallthrough 1528 case LayoutKind::DoubleDeck: // fallthrough 1529 case LayoutKind::Paper: // fallthrough 1530 case LayoutKind::CompactPaper: // fallthrough 1531 case LayoutKind::DoubleStack: // fallthrough 1532 case LayoutKind::CompactDoubleStack: // fallthrough 1533 case LayoutKind::HorizontalStack: // fallthrough 1534 case LayoutKind::CompactHorizontalStack: // fallthrough 1535 case LayoutKind::VerticalStack: // fallthrough 1536 case LayoutKind::CompactVerticalStack: 1537 { 1538 return Layout::LayoutData { 1539 Extents { 0, 0, 0, 0 }, 1540 0, 1541 1, 1542 .50f 1543 }; 1544 } 1545 default: Util::die("no associated default data defined"); 1546 } 1547 1548 return kind_to_default_data(LayoutKind::Float); 1549 }