Serenity Operating System
at master 217 lines 9.0 kB view raw
1/* 2 * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> 3 * Copyright (c) 2022, the SerenityOS developers. 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8#include <AK/QuickSort.h> 9#include <LibGUI/Button.h> 10#include <LibGUI/FontPicker.h> 11#include <LibGUI/FontPickerDialogGML.h> 12#include <LibGUI/ItemListModel.h> 13#include <LibGUI/Label.h> 14#include <LibGUI/ListView.h> 15#include <LibGUI/SpinBox.h> 16#include <LibGUI/Widget.h> 17#include <LibGfx/Font/FontDatabase.h> 18 19namespace GUI { 20 21FontPicker::FontPicker(Window* parent_window, Gfx::Font const* current_font, bool fixed_width_only) 22 : Dialog(parent_window) 23 , m_fixed_width_only(fixed_width_only) 24{ 25 set_title("Font picker"); 26 resize(430, 280); 27 set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/app-font-editor.png"sv).release_value_but_fixme_should_propagate_errors()); 28 29 auto widget = set_main_widget<GUI::Widget>().release_value_but_fixme_should_propagate_errors(); 30 widget->load_from_gml(font_picker_dialog_gml).release_value_but_fixme_should_propagate_errors(); 31 32 m_family_list_view = *widget->find_descendant_of_type_named<ListView>("family_list_view"); 33 m_family_list_view->set_model(ItemListModel<DeprecatedString>::create(m_families)); 34 m_family_list_view->horizontal_scrollbar().set_visible(false); 35 36 m_variant_list_view = *widget->find_descendant_of_type_named<ListView>("variant_list_view"); 37 m_variant_list_view->set_model(ItemListModel<DeprecatedString>::create(m_variants)); 38 m_variant_list_view->horizontal_scrollbar().set_visible(false); 39 40 m_size_spin_box = *widget->find_descendant_of_type_named<SpinBox>("size_spin_box"); 41 m_size_spin_box->set_range(1, 255); 42 43 m_size_list_view = *widget->find_descendant_of_type_named<ListView>("size_list_view"); 44 m_size_list_view->set_model(ItemListModel<int>::create(m_sizes)); 45 m_size_list_view->horizontal_scrollbar().set_visible(false); 46 47 m_sample_text_label = *widget->find_descendant_of_type_named<Label>("sample_text_label"); 48 49 m_families.clear(); 50 Gfx::FontDatabase::the().for_each_typeface([&](auto& typeface) { 51 if (m_fixed_width_only && !typeface.is_fixed_width()) 52 return; 53 if (!m_families.contains_slow(typeface.family())) 54 m_families.append(typeface.family()); 55 }); 56 quick_sort(m_families); 57 58 m_family_list_view->on_selection_change = [this] { 59 const auto& index = m_family_list_view->selection().first(); 60 m_family = index.data().to_deprecated_string(); 61 m_variants.clear(); 62 Gfx::FontDatabase::the().for_each_typeface([&](auto& typeface) { 63 if (m_fixed_width_only && !typeface.is_fixed_width()) 64 return; 65 if (typeface.family() == m_family.value() && !m_variants.contains_slow(typeface.variant())) 66 m_variants.append(typeface.variant()); 67 }); 68 quick_sort(m_variants); 69 Optional<size_t> index_of_old_variant_in_new_list; 70 if (m_variant.has_value()) 71 index_of_old_variant_in_new_list = m_variants.find_first_index(m_variant.value()); 72 73 m_variant_list_view->model()->invalidate(); 74 m_variant_list_view->set_cursor(m_variant_list_view->model()->index(index_of_old_variant_in_new_list.value_or(0)), GUI::AbstractView::SelectionUpdate::Set); 75 update_font(); 76 }; 77 78 m_variant_list_view->on_selection_change = [this] { 79 const auto& index = m_variant_list_view->selection().first(); 80 bool font_is_fixed_size = false; 81 m_variant = index.data().to_deprecated_string(); 82 m_sizes.clear(); 83 Gfx::FontDatabase::the().for_each_typeface([&](auto& typeface) { 84 if (m_fixed_width_only && !typeface.is_fixed_width()) 85 return; 86 if (typeface.family() == m_family.value() && typeface.variant() == m_variant.value()) { 87 font_is_fixed_size = typeface.is_fixed_size(); 88 if (font_is_fixed_size) { 89 m_size_spin_box->set_visible(false); 90 91 typeface.for_each_fixed_size_font([&](auto& font) { 92 m_sizes.append(font.presentation_size()); 93 }); 94 } else { 95 m_size_spin_box->set_visible(true); 96 97 m_sizes.append(8); 98 m_sizes.append(9); 99 m_sizes.append(10); 100 m_sizes.append(11); 101 m_sizes.append(12); 102 m_sizes.append(14); 103 m_sizes.append(16); 104 m_sizes.append(18); 105 m_sizes.append(20); 106 m_sizes.append(22); 107 m_sizes.append(24); 108 m_sizes.append(36); 109 } 110 } 111 }); 112 quick_sort(m_sizes); 113 m_size_list_view->model()->invalidate(); 114 m_size_list_view->set_selection_mode(GUI::AbstractView::SelectionMode::SingleSelection); 115 116 if (m_size.has_value()) { 117 Optional<size_t> index_of_old_size_in_new_list = m_sizes.find_first_index(m_size.value()); 118 if (index_of_old_size_in_new_list.has_value()) { 119 m_size_list_view->set_cursor(m_size_list_view->model()->index(index_of_old_size_in_new_list.value()), GUI::AbstractView::SelectionUpdate::Set); 120 } else { 121 if (font_is_fixed_size) { 122 m_size_list_view->set_cursor(m_size_list_view->model()->index(0), GUI::AbstractView::SelectionUpdate::Set); 123 } else { 124 m_size_list_view->set_selection_mode(GUI::AbstractView::SelectionMode::NoSelection); 125 m_size_spin_box->set_value(m_size.value()); 126 } 127 } 128 } else { 129 m_size_list_view->set_cursor(m_size_list_view->model()->index(0), GUI::AbstractView::SelectionUpdate::Set); 130 } 131 update_font(); 132 }; 133 134 m_size_list_view->on_selection_change = [this] { 135 const auto& index = m_size_list_view->selection().first(); 136 auto size = index.data().to_i32(); 137 Optional<size_t> index_of_new_size_in_list = m_sizes.find_first_index(size); 138 if (index_of_new_size_in_list.has_value()) { 139 m_size_list_view->set_selection_mode(GUI::AbstractView::SelectionMode::SingleSelection); 140 m_size = size; 141 m_size_spin_box->set_value(m_size.value()); 142 } 143 update_font(); 144 }; 145 146 m_size_spin_box->on_change = [this](int value) { 147 m_size = value; 148 149 Optional<size_t> index_of_new_size_in_list = m_sizes.find_first_index(m_size.value()); 150 151 if (index_of_new_size_in_list.has_value()) { 152 m_size_list_view->set_selection_mode(GUI::AbstractView::SelectionMode::SingleSelection); 153 m_size_list_view->set_cursor(m_size_list_view->model()->index(index_of_new_size_in_list.value()), GUI::AbstractView::SelectionUpdate::Set); 154 } else { 155 m_size_list_view->set_selection_mode(GUI::AbstractView::SelectionMode::NoSelection); 156 } 157 158 update_font(); 159 }; 160 161 auto& ok_button = *widget->find_descendant_of_type_named<GUI::Button>("ok_button"); 162 ok_button.on_click = [this](auto) { 163 done(ExecResult::OK); 164 }; 165 ok_button.set_default(true); 166 167 auto& cancel_button = *widget->find_descendant_of_type_named<GUI::Button>("cancel_button"); 168 cancel_button.on_click = [this](auto) { 169 done(ExecResult::Cancel); 170 }; 171 172 set_font(current_font); 173} 174 175void FontPicker::set_font(Gfx::Font const* font) 176{ 177 if (m_font == font) 178 return; 179 m_font = font; 180 m_sample_text_label->set_font(m_font); 181 182 if (!m_font) { 183 m_family = {}; 184 m_variant = {}; 185 m_size = {}; 186 m_variants.clear(); 187 m_sizes.clear(); 188 m_variant_list_view->model()->invalidate(); 189 m_size_list_view->model()->invalidate(); 190 return; 191 } 192 193 m_family = font->family(); 194 m_variant = font->variant(); 195 m_size = font->presentation_size(); 196 197 auto family_index = m_families.find_first_index(m_font->family()); 198 if (family_index.has_value()) 199 m_family_list_view->set_cursor(m_family_list_view->model()->index(family_index.value()), GUI::AbstractView::SelectionUpdate::Set); 200 201 auto variant_index = m_variants.find_first_index(m_font->variant()); 202 if (variant_index.has_value()) 203 m_variant_list_view->set_cursor(m_variant_list_view->model()->index(variant_index.value()), GUI::AbstractView::SelectionUpdate::Set); 204 205 auto size_index = m_sizes.find_first_index(m_font->presentation_size()); 206 if (size_index.has_value()) 207 m_size_list_view->set_cursor(m_size_list_view->model()->index(size_index.value()), GUI::AbstractView::SelectionUpdate::Set); 208} 209 210void FontPicker::update_font() 211{ 212 if (m_family.has_value() && m_size.has_value() && m_variant.has_value()) { 213 m_font = Gfx::FontDatabase::the().get(m_family.value(), m_variant.value(), m_size.value()); 214 m_sample_text_label->set_font(m_font); 215 } 216} 217}