Serenity Operating System
at portability 177 lines 7.3 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <LibGUI/BoxLayout.h> 28#include <LibGUI/Widget.h> 29#include <LibGfx/Orientation.h> 30#include <stdio.h> 31 32//#define GBOXLAYOUT_DEBUG 33 34namespace GUI { 35 36BoxLayout::BoxLayout(Orientation orientation) 37 : m_orientation(orientation) 38{ 39} 40 41void BoxLayout::run(Widget& widget) 42{ 43 bool should_log = false; 44#ifdef GBOXLAYOUT_DEBUG 45 should_log = true; 46#endif 47 if (should_log) 48 dbgprintf("BoxLayout: running layout on %s{%p}, entry count: %zu\n", widget.class_name(), &widget, m_entries.size()); 49 50 if (m_entries.is_empty()) 51 return; 52 53 Gfx::Size available_size = widget.size(); 54 int number_of_entries_with_fixed_size = 0; 55 56 int number_of_visible_entries = 0; 57 58 if (should_log) 59 dbgprintf("BoxLayout: Starting with available size: %s\n", available_size.to_string().characters()); 60 61 Optional<size_t> last_entry_with_automatic_size; 62 63 for (size_t i = 0; i < m_entries.size(); ++i) { 64 auto& entry = m_entries[i]; 65 if (entry.type == Entry::Type::Spacer) { 66 ++number_of_visible_entries; 67 } 68 if (!entry.widget) 69 continue; 70 71 if (!entry.widget->is_visible()) 72 continue; 73 ++number_of_visible_entries; 74 if (entry.widget && entry.widget->size_policy(orientation()) == SizePolicy::Fixed) { 75 if (should_log) { 76 dbgprintf("BoxLayout: Subtracting for fixed %s{%p}, size: %s\n", entry.widget->class_name(), entry.widget.ptr(), entry.widget->preferred_size().to_string().characters()); 77 dbgprintf("BoxLayout: Available size before: %s\n", available_size.to_string().characters()); 78 } 79 available_size -= entry.widget->preferred_size(); 80 if (should_log) 81 dbgprintf("BoxLayout: Available size after: %s\n", available_size.to_string().characters()); 82 ++number_of_entries_with_fixed_size; 83 } else { 84 last_entry_with_automatic_size = i; 85 } 86 available_size -= { spacing(), spacing() }; 87 } 88 89 available_size += { spacing(), spacing() }; 90 91 available_size -= { margins().left() + margins().right(), margins().top() + margins().bottom() }; 92 93 if (should_log) 94 dbgprintf("BoxLayout: Number of visible: %d/%zu\n", number_of_visible_entries, m_entries.size()); 95 96 int number_of_entries_with_automatic_size = number_of_visible_entries - number_of_entries_with_fixed_size; 97 98 if (should_log) 99 dbgprintf("BoxLayout: available_size=%s, fixed=%d, fill=%d\n", available_size.to_string().characters(), number_of_entries_with_fixed_size, number_of_entries_with_automatic_size); 100 101 Gfx::Size automatic_size; 102 Gfx::Size automatic_size_for_last_entry; 103 104 if (number_of_entries_with_automatic_size) { 105 if (m_orientation == Orientation::Horizontal) { 106 automatic_size.set_width(available_size.width() / number_of_entries_with_automatic_size); 107 automatic_size.set_height(widget.height()); 108 109 automatic_size_for_last_entry.set_width(available_size.width() - (number_of_entries_with_automatic_size - 1) * automatic_size.width()); 110 automatic_size_for_last_entry.set_height(widget.height()); 111 } else { 112 automatic_size.set_width(widget.width()); 113 automatic_size.set_height(available_size.height() / number_of_entries_with_automatic_size); 114 115 automatic_size_for_last_entry.set_width(widget.width()); 116 automatic_size_for_last_entry.set_height(available_size.height() - (number_of_entries_with_automatic_size - 1) * automatic_size.height()); 117 } 118 } 119 120 if (should_log) 121 dbgprintf("BoxLayout: automatic_size=%s\n", automatic_size.to_string().characters()); 122 123 int current_x = margins().left(); 124 int current_y = margins().top(); 125 126 for (size_t i = 0; i < m_entries.size(); ++i) { 127 auto& entry = m_entries[i]; 128 if (entry.type == Entry::Type::Spacer) { 129 current_x += automatic_size.width(); 130 current_y += automatic_size.height(); 131 } 132 133 if (!entry.widget) 134 continue; 135 if (!entry.widget->is_visible()) 136 continue; 137 Gfx::Rect rect(current_x, current_y, 0, 0); 138 if (entry.layout) { 139 // FIXME: Implement recursive layout. 140 ASSERT_NOT_REACHED(); 141 } 142 ASSERT(entry.widget); 143 144 if (last_entry_with_automatic_size.has_value() && i == last_entry_with_automatic_size.value()) { 145 rect.set_size(automatic_size_for_last_entry); 146 } else { 147 rect.set_size(automatic_size); 148 } 149 150 if (entry.widget->size_policy(Orientation::Vertical) == SizePolicy::Fixed) 151 rect.set_height(entry.widget->preferred_size().height()); 152 153 if (entry.widget->size_policy(Orientation::Horizontal) == SizePolicy::Fixed) 154 rect.set_width(entry.widget->preferred_size().width()); 155 156 if (orientation() == Orientation::Horizontal) { 157 if (entry.widget->size_policy(Orientation::Vertical) == SizePolicy::Fill) 158 rect.set_height(widget.height() - margins().top() - margins().bottom()); 159 rect.center_vertically_within(widget.rect()); 160 } else { 161 if (entry.widget->size_policy(Orientation::Horizontal) == SizePolicy::Fill) 162 rect.set_width(widget.width() - margins().left() - margins().right()); 163 rect.center_horizontally_within(widget.rect()); 164 } 165 166 if (should_log) 167 dbgprintf("BoxLayout: apply, %s{%p} <- %s\n", entry.widget->class_name(), entry.widget.ptr(), rect.to_string().characters()); 168 entry.widget->set_relative_rect(rect); 169 170 if (orientation() == Orientation::Horizontal) 171 current_x += rect.width() + spacing(); 172 else 173 current_y += rect.height() + spacing(); 174 } 175} 176 177}