Serenity Operating System
at master 233 lines 9.8 kB view raw
1/* 2 * Copyright (c) 2021, the SerenityOS developers. 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include "NewFontDialog.h" 8#include <AK/StringBuilder.h> 9#include <Applications/FontEditor/NewFontDialogPage1GML.h> 10#include <Applications/FontEditor/NewFontDialogPage2GML.h> 11#include <LibGUI/Button.h> 12#include <LibGUI/CheckBox.h> 13#include <LibGUI/ComboBox.h> 14#include <LibGUI/ItemListModel.h> 15#include <LibGUI/Label.h> 16#include <LibGUI/MessageBox.h> 17#include <LibGUI/Painter.h> 18#include <LibGUI/SpinBox.h> 19#include <LibGUI/TextBox.h> 20#include <LibGUI/Widget.h> 21#include <LibGUI/Wizards/WizardDialog.h> 22#include <LibGfx/Font/BitmapFont.h> 23#include <LibGfx/Font/FontStyleMapping.h> 24#include <LibGfx/Palette.h> 25 26namespace FontEditor { 27 28class GlyphPreviewWidget final : public GUI::Frame { 29 C_OBJECT(GlyphPreviewWidget) 30public: 31 void set_preview_size(int width, int height) 32 { 33 m_width = width; 34 m_height = height; 35 for (int i = 10; i > 0; i--) { 36 if ((frame_thickness() * 2 + (m_width * i) - 1) <= 250 37 && (frame_thickness() * 2 + (m_height * i) - 1) <= 205) { 38 set_scale(i); 39 break; 40 } 41 } 42 set_fixed_width(frame_thickness() * 2 + (m_width * m_scale) - 1); 43 set_fixed_height(frame_thickness() * 2 + (m_height * m_scale) - 1); 44 } 45 46 void set_scale(int scale) { m_scale = scale; } 47 void set_baseline(int i) { m_baseline = i; } 48 void set_mean_line(int i) { m_mean_line = i; } 49 50private: 51 GlyphPreviewWidget() 52 { 53 set_preview_size(m_width, m_height); 54 } 55 virtual void paint_event(GUI::PaintEvent& event) override 56 { 57 GUI::Frame::paint_event(event); 58 GUI::Painter painter(*this); 59 painter.add_clip_rect(frame_inner_rect()); 60 painter.add_clip_rect(event.rect()); 61 painter.fill_rect(frame_inner_rect(), palette().base()); 62 painter.translate(frame_thickness(), frame_thickness()); 63 64 painter.translate(-1, -1); 65 for (int y = 1; y < m_height; ++y) { 66 int y_below = y - 1; 67 bool bold_line = y_below == m_baseline || y_below == m_mean_line; 68 painter.draw_line({ 0, y * m_scale }, { m_width * m_scale, y * m_scale }, palette().threed_shadow2(), bold_line ? 2 : 1); 69 } 70 71 for (int x = 1; x < m_width; ++x) 72 painter.draw_line({ x * m_scale, 0 }, { x * m_scale, m_height * m_scale }, palette().threed_shadow2()); 73 74 for (int y = 0; y < m_height; ++y) { 75 for (int x = 0; x < m_width; ++x) { 76 Gfx::IntRect rect { x * m_scale, y * m_scale, m_scale, m_scale }; 77 if (x >= m_width) { 78 painter.fill_rect(rect, palette().threed_shadow1()); 79 } else { 80 if (m_bits[x][y]) 81 painter.fill_rect(rect, palette().base_text()); 82 } 83 } 84 } 85 } 86 virtual void mousedown_event(GUI::MouseEvent& event) override 87 { 88 draw_at_mouse(event); 89 } 90 virtual void mousemove_event(GUI::MouseEvent& event) override 91 { 92 if (event.buttons() & (GUI::MouseButton::Primary | GUI::MouseButton::Secondary)) 93 draw_at_mouse(event); 94 } 95 void draw_at_mouse(GUI::MouseEvent const& event) 96 { 97 bool set = event.buttons() & GUI::MouseButton::Primary; 98 bool unset = event.buttons() & GUI::MouseButton::Secondary; 99 if (!(set ^ unset)) 100 return; 101 int x = (event.x() - 1) / m_scale; 102 int y = (event.y() - 1) / m_scale; 103 if (x < 0 || x >= m_width) 104 return; 105 if (y < 0 || y >= m_height) 106 return; 107 if (m_bits[x][y] == set) 108 return; 109 m_bits[x][y] = set; 110 update(); 111 } 112 113 int m_scale { 10 }; 114 int m_width { 20 }; 115 int m_height { 20 }; 116 int m_mean_line { 2 }; 117 int m_baseline { 16 }; 118 u8 m_bits[Gfx::GlyphBitmap::max_width()][Gfx::GlyphBitmap::max_height()] {}; 119}; 120 121} 122 123REGISTER_WIDGET(FontEditor, GlyphPreviewWidget); 124 125NewFontDialog::NewFontDialog(GUI::Window* parent_window) 126 : GUI::WizardDialog(parent_window) 127{ 128 set_title("New Font"); 129 130 m_font_properties_page = GUI::WizardPage::construct("Typeface properties", "Edit details about this font."); 131 m_font_properties_page->body_widget().load_from_gml(new_font_dialog_page_1_gml).release_value_but_fixme_should_propagate_errors(); 132 133 m_name_textbox = m_font_properties_page->body_widget().find_descendant_of_type_named<GUI::TextBox>("name_textbox"); 134 m_family_textbox = m_font_properties_page->body_widget().find_descendant_of_type_named<GUI::TextBox>("family_textbox"); 135 m_weight_combobox = m_font_properties_page->body_widget().find_descendant_of_type_named<GUI::ComboBox>("weight_combobox"); 136 m_slope_combobox = m_font_properties_page->body_widget().find_descendant_of_type_named<GUI::ComboBox>("slope_combobox"); 137 m_presentation_spinbox = m_font_properties_page->body_widget().find_descendant_of_type_named<GUI::SpinBox>("presentation_spinbox"); 138 139 for (auto& it : Gfx::font_weight_names) 140 m_font_weight_list.append(it.name); 141 m_weight_combobox->set_model(*GUI::ItemListModel<DeprecatedString>::create(m_font_weight_list)); 142 m_weight_combobox->set_selected_index(3); 143 144 for (auto& it : Gfx::font_slope_names) 145 m_font_slope_list.append(it.name); 146 m_slope_combobox->set_model(*GUI::ItemListModel<DeprecatedString>::create(m_font_slope_list)); 147 m_slope_combobox->set_selected_index(0); 148 149 m_presentation_spinbox->set_value(12); 150 151 m_font_properties_page->on_page_enter = [&]() { 152 m_name_textbox->set_focus(true); 153 }; 154 m_font_properties_page->on_next_page = [&]() { 155 return m_glyph_properties_page; 156 }; 157 158 m_glyph_properties_page = GUI::WizardPage::construct("Glyph properties", "Edit details about this font."); 159 m_glyph_properties_page->body_widget().load_from_gml(new_font_dialog_page_2_gml).release_value_but_fixme_should_propagate_errors(); 160 m_glyph_properties_page->set_is_final_page(true); 161 162 m_glyph_height_spinbox = m_glyph_properties_page->body_widget().find_descendant_of_type_named<GUI::SpinBox>("height_spinbox"); 163 m_glyph_width_spinbox = m_glyph_properties_page->body_widget().find_descendant_of_type_named<GUI::SpinBox>("width_spinbox"); 164 m_baseline_spinbox = m_glyph_properties_page->body_widget().find_descendant_of_type_named<GUI::SpinBox>("baseline_spinbox"); 165 m_mean_line_spinbox = m_glyph_properties_page->body_widget().find_descendant_of_type_named<GUI::SpinBox>("mean_line_spinbox"); 166 m_spacing_spinbox = m_glyph_properties_page->body_widget().find_descendant_of_type_named<GUI::SpinBox>("spacing_spinbox"); 167 m_fixed_width_checkbox = m_glyph_properties_page->body_widget().find_descendant_of_type_named<GUI::CheckBox>("fixed_width_checkbox"); 168 169 m_glyph_height_spinbox->set_value(20); 170 m_glyph_width_spinbox->set_value(20); 171 m_glyph_height_spinbox->set_max(Gfx::GlyphBitmap::max_height()); 172 m_glyph_width_spinbox->set_max(Gfx::GlyphBitmap::max_width()); 173 m_mean_line_spinbox->set_value(2); 174 m_baseline_spinbox->set_value(16); 175 m_mean_line_spinbox->set_max(max(m_glyph_height_spinbox->value() - 2, 0)); 176 m_baseline_spinbox->set_max(max(m_glyph_height_spinbox->value() - 2, 0)); 177 m_spacing_spinbox->set_value(1); 178 m_fixed_width_checkbox->set_checked(false); 179 180 auto& preview_editor = *m_glyph_properties_page->body_widget().find_descendant_of_type_named<FontEditor::GlyphPreviewWidget>("glyph_preview_widget"); 181 182 m_glyph_width_spinbox->on_change = [&](int value) { 183 preview_editor.set_preview_size(value, m_glyph_height_spinbox->value()); 184 }; 185 m_glyph_height_spinbox->on_change = [&](int value) { 186 preview_editor.set_preview_size(m_glyph_width_spinbox->value(), value); 187 m_mean_line_spinbox->set_max(max(value - 2, 0)); 188 m_baseline_spinbox->set_max(max(value - 2, 0)); 189 }; 190 m_baseline_spinbox->on_change = [&](int value) { 191 preview_editor.set_baseline(value); 192 preview_editor.update(); 193 }; 194 m_mean_line_spinbox->on_change = [&](int value) { 195 preview_editor.set_mean_line(value); 196 preview_editor.update(); 197 }; 198 199 push_page(*m_font_properties_page); 200} 201 202void NewFontDialog::save_metadata() 203{ 204 m_new_font_metadata.name = m_name_textbox->text(); 205 m_new_font_metadata.family = m_family_textbox->text(); 206 m_new_font_metadata.weight = Gfx::name_to_weight(m_weight_combobox->text()); 207 m_new_font_metadata.slope = Gfx::name_to_slope(m_slope_combobox->text()); 208 m_new_font_metadata.presentation_size = m_presentation_spinbox->value(); 209 210 m_new_font_metadata.baseline = m_baseline_spinbox->value(); 211 m_new_font_metadata.mean_line = m_mean_line_spinbox->value(); 212 m_new_font_metadata.glyph_height = m_glyph_height_spinbox->value(); 213 m_new_font_metadata.glyph_width = m_glyph_width_spinbox->value(); 214 m_new_font_metadata.glyph_spacing = m_spacing_spinbox->value(); 215 m_new_font_metadata.is_fixed_width = m_fixed_width_checkbox->is_checked(); 216} 217 218ErrorOr<NonnullRefPtr<Gfx::BitmapFont>> NewFontDialog::create_font() 219{ 220 save_metadata(); 221 222 auto font = TRY(Gfx::BitmapFont::try_create(m_new_font_metadata.glyph_height, m_new_font_metadata.glyph_width, m_new_font_metadata.is_fixed_width, 0x110000)); 223 font->set_name(m_new_font_metadata.name); 224 font->set_family(m_new_font_metadata.family); 225 font->set_presentation_size(m_new_font_metadata.presentation_size); 226 font->set_weight(m_new_font_metadata.weight); 227 font->set_slope(m_new_font_metadata.slope); 228 font->set_baseline(m_new_font_metadata.baseline); 229 font->set_mean_line(m_new_font_metadata.mean_line); 230 font->set_glyph_spacing(m_new_font_metadata.glyph_spacing); 231 232 return font; 233}