1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 03:37:43 +00:00

FontEditor: Improve error propagation during construction

FontEditor now builds its actions, toolbars, models and menus
with fallible methods. Widget assignment from GML is now organized
in order of use.
This commit is contained in:
thankyouverycool 2022-07-04 22:22:06 -04:00 committed by Andreas Kling
parent 0a62fcfbdf
commit 9962a744eb
3 changed files with 213 additions and 164 deletions

View file

@ -111,38 +111,9 @@ static RefPtr<GUI::Window> create_font_preview_window(FontEditorWidget& editor)
return window; return window;
} }
FontEditorWidget::FontEditorWidget() ErrorOr<void> FontEditorWidget::create_actions()
{ {
load_from_gml(font_editor_window_gml); m_new_action = GUI::Action::create("&New Font...", { Mod_Ctrl, Key_N }, TRY(Gfx::Bitmap::try_load_from_file("/res/icons/16x16/filetype-font.png")), [&](auto&) {
auto& toolbar = *find_descendant_of_type_named<GUI::Toolbar>("toolbar");
auto& glyph_mode_toolbar = *find_descendant_of_type_named<GUI::Toolbar>("glyph_mode_toolbar");
auto& glyph_transform_toolbar = *find_descendant_of_type_named<GUI::Toolbar>("glyph_transform_toolbar");
auto& glyph_map_container = *find_descendant_of_type_named<GUI::Widget>("glyph_map_container");
m_statusbar = *find_descendant_of_type_named<GUI::Statusbar>("statusbar");
m_glyph_editor_container = *find_descendant_of_type_named<GUI::Widget>("glyph_editor_container");
m_left_column_container = *find_descendant_of_type_named<GUI::Widget>("left_column_container");
m_glyph_editor_width_spinbox = *find_descendant_of_type_named<GUI::SpinBox>("glyph_editor_width_spinbox");
m_glyph_editor_present_checkbox = *find_descendant_of_type_named<GUI::CheckBox>("glyph_editor_present_checkbox");
m_name_textbox = *find_descendant_of_type_named<GUI::TextBox>("name_textbox");
m_family_textbox = *find_descendant_of_type_named<GUI::TextBox>("family_textbox");
m_presentation_spinbox = *find_descendant_of_type_named<GUI::SpinBox>("presentation_spinbox");
m_weight_combobox = *find_descendant_of_type_named<GUI::ComboBox>("weight_combobox");
m_slope_combobox = *find_descendant_of_type_named<GUI::ComboBox>("slope_combobox");
m_spacing_spinbox = *find_descendant_of_type_named<GUI::SpinBox>("spacing_spinbox");
m_mean_line_spinbox = *find_descendant_of_type_named<GUI::SpinBox>("mean_line_spinbox");
m_baseline_spinbox = *find_descendant_of_type_named<GUI::SpinBox>("baseline_spinbox");
m_fixed_width_checkbox = *find_descendant_of_type_named<GUI::CheckBox>("fixed_width_checkbox");
m_font_metadata_groupbox = *find_descendant_of_type_named<GUI::GroupBox>("font_metadata_groupbox");
m_unicode_block_listview = *find_descendant_of_type_named<GUI::ListView>("unicode_block_listview");
m_search_textbox = *find_descendant_of_type_named<GUI::TextBox>("search_textbox");
m_unicode_block_container = *find_descendant_of_type_named<GUI::Widget>("unicode_block_container");
m_glyph_editor_widget = m_glyph_editor_container->add<GlyphEditorWidget>();
m_glyph_map_widget = glyph_map_container.add<GUI::GlyphMapWidget>();
m_new_action = GUI::Action::create("&New Font...", { Mod_Ctrl, Key_N }, Gfx::Bitmap::try_load_from_file("/res/icons/16x16/filetype-font.png").release_value_but_fixme_should_propagate_errors(), [&](auto&) {
if (!request_close()) if (!request_close())
return; return;
auto new_font_wizard = NewFontDialog::construct(window()); auto new_font_wizard = NewFontDialog::construct(window());
@ -157,26 +128,27 @@ FontEditorWidget::FontEditorWidget()
new_font->set_baseline(metadata.baseline); new_font->set_baseline(metadata.baseline);
new_font->set_mean_line(metadata.mean_line); new_font->set_mean_line(metadata.mean_line);
window()->set_modified(true); window()->set_modified(true);
initialize({}, move(new_font)); MUST(initialize({}, move(new_font)));
} }
}); });
m_new_action->set_status_tip("Create a new font"); m_new_action->set_status_tip("Create a new font");
m_open_action = GUI::CommonActions::make_open_action([&](auto&) { m_open_action = GUI::CommonActions::make_open_action([&](auto&) {
if (!request_close()) if (!request_close())
return; return;
Optional<String> open_path = GUI::FilePicker::get_open_filepath(window(), {}, "/res/fonts/"); Optional<String> open_path = GUI::FilePicker::get_open_filepath(window(), {}, "/res/fonts/");
if (!open_path.has_value()) if (!open_path.has_value())
return; return;
open_file(open_path.value()); open_file(open_path.value());
}); });
m_save_action = GUI::CommonActions::make_save_action([&](auto&) { m_save_action = GUI::CommonActions::make_save_action([&](auto&) {
if (m_path.is_empty()) if (m_path.is_empty())
m_save_as_action->activate(); m_save_as_action->activate();
else else
save_file(m_path); save_file(m_path);
}); });
m_save_as_action = GUI::CommonActions::make_save_as_action([&](auto&) { m_save_as_action = GUI::CommonActions::make_save_as_action([&](auto&) {
LexicalPath lexical_path(m_path.is_empty() ? "Untitled.font" : m_path); LexicalPath lexical_path(m_path.is_empty() ? "Untitled.font" : m_path);
Optional<String> save_path = GUI::FilePicker::get_save_filepath(window(), lexical_path.title(), lexical_path.extension()); Optional<String> save_path = GUI::FilePicker::get_save_filepath(window(), lexical_path.title(), lexical_path.extension());
@ -184,25 +156,37 @@ FontEditorWidget::FontEditorWidget()
return; return;
save_file(save_path.value()); save_file(save_path.value());
}); });
m_cut_action = GUI::CommonActions::make_cut_action([&](auto&) { m_cut_action = GUI::CommonActions::make_cut_action([&](auto&) {
cut_selected_glyphs(); cut_selected_glyphs();
}); });
m_copy_action = GUI::CommonActions::make_copy_action([&](auto&) { m_copy_action = GUI::CommonActions::make_copy_action([&](auto&) {
copy_selected_glyphs(); copy_selected_glyphs();
}); });
m_paste_action = GUI::CommonActions::make_paste_action([&](auto&) { m_paste_action = GUI::CommonActions::make_paste_action([&](auto&) {
paste_glyphs(); paste_glyphs();
}); });
m_paste_action->set_enabled(GUI::Clipboard::the().fetch_mime_type() == "glyph/x-fonteditor"); m_paste_action->set_enabled(GUI::Clipboard::the().fetch_mime_type() == "glyph/x-fonteditor");
GUI::Clipboard::the().on_change = [&](String const& data_type) {
m_paste_action->set_enabled(data_type == "glyph/x-fonteditor");
};
m_delete_action = GUI::CommonActions::make_delete_action([this](auto&) { m_delete_action = GUI::CommonActions::make_delete_action([this](auto&) {
delete_selected_glyphs(); delete_selected_glyphs();
}); });
m_undo_action = GUI::CommonActions::make_undo_action([&](auto&) { m_undo_action = GUI::CommonActions::make_undo_action([&](auto&) {
undo(); undo();
}); });
m_undo_action->set_enabled(false);
m_redo_action = GUI::CommonActions::make_redo_action([&](auto&) { m_redo_action = GUI::CommonActions::make_redo_action([&](auto&) {
redo(); redo();
}); });
m_redo_action->set_enabled(false);
m_select_all_action = GUI::CommonActions::make_select_all_action([this](auto&) { m_select_all_action = GUI::CommonActions::make_select_all_action([this](auto&) {
m_glyph_map_widget->set_selection(m_range.first, m_range.last - m_range.first + 1); m_glyph_map_widget->set_selection(m_range.first, m_range.last - m_range.first + 1);
@ -239,7 +223,7 @@ FontEditorWidget::FontEditorWidget()
m_show_unicode_blocks_action->set_checked(show_unicode_blocks); m_show_unicode_blocks_action->set_checked(show_unicode_blocks);
m_show_unicode_blocks_action->set_status_tip("Show or hide the Unicode block list"); m_show_unicode_blocks_action->set_status_tip("Show or hide the Unicode block list");
m_go_to_glyph_action = GUI::Action::create("&Go to Glyph...", { Mod_Ctrl, Key_G }, Gfx::Bitmap::try_load_from_file("/res/icons/16x16/go-to.png").release_value_but_fixme_should_propagate_errors(), [&](auto&) { m_go_to_glyph_action = GUI::Action::create("&Go to Glyph...", { Mod_Ctrl, Key_G }, TRY(Gfx::Bitmap::try_load_from_file("/res/icons/16x16/go-to.png")), [&](auto&) {
String input; String input;
if (GUI::InputBox::show(window(), input, "Hexadecimal:", "Go to glyph") == GUI::InputBox::ExecResult::OK && !input.is_empty()) { if (GUI::InputBox::show(window(), input, "Hexadecimal:", "Go to glyph") == GUI::InputBox::ExecResult::OK && !input.is_empty()) {
auto maybe_code_point = AK::StringUtils::convert_to_uint_from_hex(input); auto maybe_code_point = AK::StringUtils::convert_to_uint_from_hex(input);
@ -253,35 +237,19 @@ FontEditorWidget::FontEditorWidget()
} }
}); });
m_go_to_glyph_action->set_status_tip("Go to the specified code point"); m_go_to_glyph_action->set_status_tip("Go to the specified code point");
m_previous_glyph_action = GUI::Action::create("Pre&vious Glyph", { Mod_Alt, Key_Left }, Gfx::Bitmap::try_load_from_file("/res/icons/16x16/go-back.png").release_value_but_fixme_should_propagate_errors(), [&](auto&) {
m_previous_glyph_action = GUI::Action::create("Pre&vious Glyph", { Mod_Alt, Key_Left }, TRY(Gfx::Bitmap::try_load_from_file("/res/icons/16x16/go-back.png")), [&](auto&) {
m_glyph_map_widget->select_previous_existing_glyph(); m_glyph_map_widget->select_previous_existing_glyph();
}); });
m_previous_glyph_action->set_status_tip("Seek the previous visible glyph"); m_previous_glyph_action->set_status_tip("Seek the previous visible glyph");
m_next_glyph_action = GUI::Action::create("&Next Glyph", { Mod_Alt, Key_Right }, Gfx::Bitmap::try_load_from_file("/res/icons/16x16/go-forward.png").release_value_but_fixme_should_propagate_errors(), [&](auto&) {
m_next_glyph_action = GUI::Action::create("&Next Glyph", { Mod_Alt, Key_Right }, TRY(Gfx::Bitmap::try_load_from_file("/res/icons/16x16/go-forward.png")), [&](auto&) {
m_glyph_map_widget->select_next_existing_glyph(); m_glyph_map_widget->select_next_existing_glyph();
}); });
m_next_glyph_action->set_status_tip("Seek the next visible glyph"); m_next_glyph_action->set_status_tip("Seek the next visible glyph");
toolbar.add_action(*m_new_action);
toolbar.add_action(*m_open_action);
toolbar.add_action(*m_save_action);
toolbar.add_separator();
toolbar.add_action(*m_cut_action);
toolbar.add_action(*m_copy_action);
toolbar.add_action(*m_paste_action);
toolbar.add_action(*m_delete_action);
toolbar.add_separator();
toolbar.add_action(*m_undo_action);
toolbar.add_action(*m_redo_action);
toolbar.add_separator();
toolbar.add_action(*m_open_preview_action);
toolbar.add_separator();
toolbar.add_action(*m_previous_glyph_action);
toolbar.add_action(*m_next_glyph_action);
toolbar.add_action(*m_go_to_glyph_action);
i32 scale = Config::read_i32("FontEditor", "GlyphEditor", "Scale", 10); i32 scale = Config::read_i32("FontEditor", "GlyphEditor", "Scale", 10);
set_scale(scale);
m_scale_five_action = GUI::Action::create_checkable("500%", { Mod_Ctrl, Key_1 }, [this](auto&) { m_scale_five_action = GUI::Action::create_checkable("500%", { Mod_Ctrl, Key_1 }, [this](auto&) {
set_scale_and_save(5); set_scale_and_save(5);
}); });
@ -303,22 +271,19 @@ FontEditorWidget::FontEditorWidget()
m_glyph_editor_scale_actions.add_action(*m_scale_fifteen_action); m_glyph_editor_scale_actions.add_action(*m_scale_fifteen_action);
m_glyph_editor_scale_actions.set_exclusive(true); m_glyph_editor_scale_actions.set_exclusive(true);
m_paint_glyph_action = GUI::Action::create_checkable("Paint Glyph", { Mod_Ctrl, KeyCode::Key_J }, Gfx::Bitmap::try_load_from_file("/res/icons/pixelpaint/pen.png").release_value_but_fixme_should_propagate_errors(), [&](auto&) { m_paint_glyph_action = GUI::Action::create_checkable("Paint Glyph", { Mod_Ctrl, KeyCode::Key_J }, TRY(Gfx::Bitmap::try_load_from_file("/res/icons/pixelpaint/pen.png")), [&](auto&) {
m_glyph_editor_widget->set_mode(GlyphEditorWidget::Paint); m_glyph_editor_widget->set_mode(GlyphEditorWidget::Paint);
}); });
m_paint_glyph_action->set_checked(true); m_paint_glyph_action->set_checked(true);
m_move_glyph_action = GUI::Action::create_checkable("Move Glyph", { Mod_Ctrl, KeyCode::Key_K }, Gfx::Bitmap::try_load_from_file("/res/icons/16x16/selection-move.png").release_value_but_fixme_should_propagate_errors(), [&](auto&) { m_move_glyph_action = GUI::Action::create_checkable("Move Glyph", { Mod_Ctrl, KeyCode::Key_K }, TRY(Gfx::Bitmap::try_load_from_file("/res/icons/16x16/selection-move.png")), [&](auto&) {
m_glyph_editor_widget->set_mode(GlyphEditorWidget::Move); m_glyph_editor_widget->set_mode(GlyphEditorWidget::Move);
}); });
m_glyph_tool_actions.add_action(*m_move_glyph_action);
m_glyph_tool_actions.add_action(*m_paint_glyph_action); m_glyph_tool_actions.add_action(*m_paint_glyph_action);
m_glyph_tool_actions.add_action(*m_move_glyph_action);
m_glyph_tool_actions.set_exclusive(true); m_glyph_tool_actions.set_exclusive(true);
glyph_mode_toolbar.add_action(*m_paint_glyph_action);
glyph_mode_toolbar.add_action(*m_move_glyph_action);
m_rotate_counterclockwise_action = GUI::CommonActions::make_rotate_counterclockwise_action([&](auto&) { m_rotate_counterclockwise_action = GUI::CommonActions::make_rotate_counterclockwise_action([&](auto&) {
m_glyph_editor_widget->rotate_90(GlyphEditorWidget::Counterclockwise); m_glyph_editor_widget->rotate_90(GlyphEditorWidget::Counterclockwise);
}); });
@ -327,15 +292,15 @@ FontEditorWidget::FontEditorWidget()
m_glyph_editor_widget->rotate_90(GlyphEditorWidget::Clockwise); m_glyph_editor_widget->rotate_90(GlyphEditorWidget::Clockwise);
}); });
m_flip_horizontal_action = GUI::Action::create("Flip Horizontally", { Mod_Ctrl | Mod_Shift, Key_Q }, Gfx::Bitmap::try_load_from_file("/res/icons/16x16/edit-flip-horizontal.png").release_value_but_fixme_should_propagate_errors(), [&](auto&) { m_flip_horizontal_action = GUI::Action::create("Flip Horizontally", { Mod_Ctrl | Mod_Shift, Key_Q }, TRY(Gfx::Bitmap::try_load_from_file("/res/icons/16x16/edit-flip-horizontal.png")), [&](auto&) {
m_glyph_editor_widget->flip_horizontally(); m_glyph_editor_widget->flip_horizontally();
}); });
m_flip_vertical_action = GUI::Action::create("Flip Vertically", { Mod_Ctrl | Mod_Shift, Key_W }, Gfx::Bitmap::try_load_from_file("/res/icons/16x16/edit-flip-vertical.png").release_value_but_fixme_should_propagate_errors(), [&](auto&) { m_flip_vertical_action = GUI::Action::create("Flip Vertically", { Mod_Ctrl | Mod_Shift, Key_W }, TRY(Gfx::Bitmap::try_load_from_file("/res/icons/16x16/edit-flip-vertical.png")), [&](auto&) {
m_glyph_editor_widget->flip_vertically(); m_glyph_editor_widget->flip_vertically();
}); });
m_copy_text_action = GUI::Action::create("Copy as Te&xt", { Mod_Ctrl, Key_T }, Gfx::Bitmap::try_load_from_file("/res/icons/16x16/edit-copy.png").release_value_but_fixme_should_propagate_errors(), [&](auto&) { m_copy_text_action = GUI::Action::create("Copy as Te&xt", { Mod_Ctrl, Key_T }, TRY(Gfx::Bitmap::try_load_from_file("/res/icons/16x16/edit-copy.png")), [&](auto&) {
StringBuilder builder; StringBuilder builder;
auto selection = m_glyph_map_widget->selection().normalized(); auto selection = m_glyph_map_widget->selection().normalized();
for (auto code_point = selection.start(); code_point < selection.start() + selection.size(); ++code_point) { for (auto code_point = selection.start(); code_point < selection.start() + selection.size(); ++code_point) {
@ -347,14 +312,105 @@ FontEditorWidget::FontEditorWidget()
}); });
m_copy_text_action->set_status_tip("Copy to clipboard as text"); m_copy_text_action->set_status_tip("Copy to clipboard as text");
glyph_transform_toolbar.add_action(*m_flip_horizontal_action); return {};
glyph_transform_toolbar.add_action(*m_flip_vertical_action); }
glyph_transform_toolbar.add_action(*m_rotate_counterclockwise_action);
glyph_transform_toolbar.add_action(*m_rotate_clockwise_action);
GUI::Clipboard::the().on_change = [&](String const& data_type) { ErrorOr<void> FontEditorWidget::create_toolbars()
m_paste_action->set_enabled(data_type == "glyph/x-fonteditor"); {
auto& toolbar = *find_descendant_of_type_named<GUI::Toolbar>("toolbar");
(void)TRY(toolbar.try_add_action(*m_new_action));
(void)TRY(toolbar.try_add_action(*m_open_action));
(void)TRY(toolbar.try_add_action(*m_save_action));
TRY(toolbar.try_add_separator());
(void)TRY(toolbar.try_add_action(*m_cut_action));
(void)TRY(toolbar.try_add_action(*m_copy_action));
(void)TRY(toolbar.try_add_action(*m_paste_action));
(void)TRY(toolbar.try_add_action(*m_delete_action));
TRY(toolbar.try_add_separator());
(void)TRY(toolbar.try_add_action(*m_undo_action));
(void)TRY(toolbar.try_add_action(*m_redo_action));
TRY(toolbar.try_add_separator());
(void)TRY(toolbar.try_add_action(*m_open_preview_action));
TRY(toolbar.try_add_separator());
(void)TRY(toolbar.try_add_action(*m_previous_glyph_action));
(void)TRY(toolbar.try_add_action(*m_next_glyph_action));
(void)TRY(toolbar.try_add_action(*m_go_to_glyph_action));
auto& glyph_transform_toolbar = *find_descendant_of_type_named<GUI::Toolbar>("glyph_transform_toolbar");
(void)TRY(glyph_transform_toolbar.try_add_action(*m_flip_horizontal_action));
(void)TRY(glyph_transform_toolbar.try_add_action(*m_flip_vertical_action));
(void)TRY(glyph_transform_toolbar.try_add_action(*m_rotate_counterclockwise_action));
(void)TRY(glyph_transform_toolbar.try_add_action(*m_rotate_clockwise_action));
auto& glyph_mode_toolbar = *find_descendant_of_type_named<GUI::Toolbar>("glyph_mode_toolbar");
(void)TRY(glyph_mode_toolbar.try_add_action(*m_paint_glyph_action));
(void)TRY(glyph_mode_toolbar.try_add_action(*m_move_glyph_action));
return {};
}
ErrorOr<void> FontEditorWidget::create_models()
{
for (auto& it : Gfx::font_slope_names)
TRY(m_font_slope_list.try_append(it.name));
m_slope_combobox->set_model(GUI::ItemListModel<String>::create(m_font_slope_list));
for (auto& it : Gfx::font_weight_names)
TRY(m_font_weight_list.try_append(it.name));
m_weight_combobox->set_model(GUI::ItemListModel<String>::create(m_font_weight_list));
auto unicode_blocks = Unicode::block_display_names();
TRY(m_unicode_block_list.try_append("Show All"));
for (auto& block : unicode_blocks)
TRY(m_unicode_block_list.try_append(block.display_name));
m_unicode_block_model = GUI::ItemListModel<String>::create(m_unicode_block_list);
m_filter_model = TRY(GUI::FilteringProxyModel::create(*m_unicode_block_model));
m_filter_model->set_filter_term("");
m_unicode_block_listview = find_descendant_of_type_named<GUI::ListView>("unicode_block_listview");
m_unicode_block_listview->on_selection_change = [this, unicode_blocks] {
auto index = m_unicode_block_listview->selection().first();
auto mapped_index = m_filter_model->map(index);
if (mapped_index.row() > 0)
m_range = unicode_blocks[mapped_index.row() - 1].code_point_range;
else
m_range = { 0x0000, 0x10FFFF };
m_glyph_map_widget->set_active_range(m_range);
}; };
m_unicode_block_listview->set_model(m_filter_model);
m_unicode_block_listview->set_activates_on_selection(true);
m_unicode_block_listview->horizontal_scrollbar().set_visible(false);
m_unicode_block_listview->set_cursor(m_unicode_block_model->index(0, 0), GUI::AbstractView::SelectionUpdate::Set);
return {};
}
ErrorOr<void> FontEditorWidget::create_undo_stack()
{
m_undo_stack = TRY(try_make<GUI::UndoStack>());
m_undo_stack->on_state_change = [this] {
m_undo_action->set_enabled(m_undo_stack->can_undo());
m_redo_action->set_enabled(m_undo_stack->can_redo());
if (m_undo_stack->is_current_modified())
did_modify_font();
};
return {};
}
FontEditorWidget::FontEditorWidget()
{
load_from_gml(font_editor_window_gml);
m_font_metadata_groupbox = find_descendant_of_type_named<GUI::GroupBox>("font_metadata_groupbox");
m_unicode_block_container = find_descendant_of_type_named<GUI::Widget>("unicode_block_container");
m_glyph_editor_container = *find_descendant_of_type_named<GUI::Widget>("glyph_editor_container");
m_left_column_container = *find_descendant_of_type_named<GUI::Widget>("left_column_container");
auto& glyph_map_container = *find_descendant_of_type_named<GUI::Widget>("glyph_map_container");
m_glyph_editor_widget = m_glyph_editor_container->add<GlyphEditorWidget>();
m_glyph_map_widget = glyph_map_container.add<GUI::GlyphMapWidget>();
m_glyph_editor_widget->on_glyph_altered = [this](int glyph) { m_glyph_editor_widget->on_glyph_altered = [this](int glyph) {
m_glyph_map_widget->update_glyph(glyph); m_glyph_map_widget->update_glyph(glyph);
@ -366,6 +422,8 @@ FontEditorWidget::FontEditorWidget()
reset_selection_and_push_undo(); reset_selection_and_push_undo();
}; };
m_glyph_editor_width_spinbox = find_descendant_of_type_named<GUI::SpinBox>("glyph_editor_width_spinbox");
m_glyph_editor_present_checkbox = find_descendant_of_type_named<GUI::CheckBox>("glyph_editor_present_checkbox");
m_glyph_map_widget->on_active_glyph_changed = [this](int glyph) { m_glyph_map_widget->on_active_glyph_changed = [this](int glyph) {
if (m_undo_selection) { if (m_undo_selection) {
auto selection = m_glyph_map_widget->selection().normalized(); auto selection = m_glyph_map_widget->selection().normalized();
@ -397,16 +455,19 @@ FontEditorWidget::FontEditorWidget()
m_context_menu->popup(event.screen_position()); m_context_menu->popup(event.screen_position());
}; };
m_name_textbox = find_descendant_of_type_named<GUI::TextBox>("name_textbox");
m_name_textbox->on_change = [&] { m_name_textbox->on_change = [&] {
m_edited_font->set_name(m_name_textbox->text()); m_edited_font->set_name(m_name_textbox->text());
did_modify_font(); did_modify_font();
}; };
m_family_textbox = find_descendant_of_type_named<GUI::TextBox>("family_textbox");
m_family_textbox->on_change = [&] { m_family_textbox->on_change = [&] {
m_edited_font->set_family(m_family_textbox->text()); m_edited_font->set_family(m_family_textbox->text());
did_modify_font(); did_modify_font();
}; };
m_fixed_width_checkbox = find_descendant_of_type_named<GUI::CheckBox>("fixed_width_checkbox");
m_fixed_width_checkbox->on_checked = [this](bool checked) { m_fixed_width_checkbox->on_checked = [this](bool checked) {
m_edited_font->set_fixed_width(checked); m_edited_font->set_fixed_width(checked);
auto glyph_width = m_edited_font->raw_glyph_width(m_glyph_map_widget->active_glyph()); auto glyph_width = m_edited_font->raw_glyph_width(m_glyph_map_widget->active_glyph());
@ -439,34 +500,33 @@ FontEditorWidget::FontEditorWidget()
did_modify_font(); did_modify_font();
}; };
m_weight_combobox = find_descendant_of_type_named<GUI::ComboBox>("weight_combobox");
m_weight_combobox->on_change = [this](auto&, auto&) { m_weight_combobox->on_change = [this](auto&, auto&) {
m_edited_font->set_weight(Gfx::name_to_weight(m_weight_combobox->text())); m_edited_font->set_weight(Gfx::name_to_weight(m_weight_combobox->text()));
did_modify_font(); did_modify_font();
}; };
for (auto& it : Gfx::font_weight_names)
m_font_weight_list.append(it.name);
m_weight_combobox->set_model(*GUI::ItemListModel<String>::create(m_font_weight_list));
m_slope_combobox = find_descendant_of_type_named<GUI::ComboBox>("slope_combobox");
m_slope_combobox->on_change = [this](auto&, auto&) { m_slope_combobox->on_change = [this](auto&, auto&) {
m_edited_font->set_slope(Gfx::name_to_slope(m_slope_combobox->text())); m_edited_font->set_slope(Gfx::name_to_slope(m_slope_combobox->text()));
did_modify_font(); did_modify_font();
}; };
for (auto& it : Gfx::font_slope_names)
m_font_slope_list.append(it.name);
m_slope_combobox->set_model(*GUI::ItemListModel<String>::create(m_font_slope_list));
m_presentation_spinbox = find_descendant_of_type_named<GUI::SpinBox>("presentation_spinbox");
m_presentation_spinbox->on_change = [this](int value) { m_presentation_spinbox->on_change = [this](int value) {
m_edited_font->set_presentation_size(value); m_edited_font->set_presentation_size(value);
update_preview(); update_preview();
did_modify_font(); did_modify_font();
}; };
m_spacing_spinbox = find_descendant_of_type_named<GUI::SpinBox>("spacing_spinbox");
m_spacing_spinbox->on_change = [this](int value) { m_spacing_spinbox->on_change = [this](int value) {
m_edited_font->set_glyph_spacing(value); m_edited_font->set_glyph_spacing(value);
update_preview(); update_preview();
did_modify_font(); did_modify_font();
}; };
m_baseline_spinbox = find_descendant_of_type_named<GUI::SpinBox>("baseline_spinbox");
m_baseline_spinbox->on_change = [this](int value) { m_baseline_spinbox->on_change = [this](int value) {
m_edited_font->set_baseline(value); m_edited_font->set_baseline(value);
m_glyph_editor_widget->update(); m_glyph_editor_widget->update();
@ -474,6 +534,7 @@ FontEditorWidget::FontEditorWidget()
did_modify_font(); did_modify_font();
}; };
m_mean_line_spinbox = find_descendant_of_type_named<GUI::SpinBox>("mean_line_spinbox");
m_mean_line_spinbox->on_change = [this](int value) { m_mean_line_spinbox->on_change = [this](int value) {
m_edited_font->set_mean_line(value); m_edited_font->set_mean_line(value);
m_glyph_editor_widget->update(); m_glyph_editor_widget->update();
@ -481,29 +542,7 @@ FontEditorWidget::FontEditorWidget()
did_modify_font(); did_modify_font();
}; };
auto unicode_blocks = Unicode::block_display_names(); m_search_textbox = find_descendant_of_type_named<GUI::TextBox>("search_textbox");
m_unicode_block_list.append("Show All");
for (auto& block : unicode_blocks)
m_unicode_block_list.append(block.display_name);
m_unicode_block_model = GUI::ItemListModel<String>::create(m_unicode_block_list);
m_filter_model = MUST(GUI::FilteringProxyModel::create(*m_unicode_block_model));
m_filter_model->set_filter_term("");
m_unicode_block_listview->on_selection_change = [this, unicode_blocks] {
auto index = m_unicode_block_listview->selection().first();
auto mapped_index = m_filter_model->map(index);
if (mapped_index.row() > 0)
m_range = unicode_blocks[mapped_index.row() - 1].code_point_range;
else
m_range = { 0x0000, 0x10FFFF };
m_glyph_map_widget->set_active_range(m_range);
};
m_unicode_block_listview->set_model(*m_filter_model);
m_unicode_block_listview->set_activates_on_selection(true);
m_unicode_block_listview->horizontal_scrollbar().set_visible(false);
m_unicode_block_listview->set_cursor(m_unicode_block_model->index(0, 0), GUI::AbstractView::SelectionUpdate::Set);
m_search_textbox->on_return_pressed = [this] { m_search_textbox->on_return_pressed = [this] {
if (!m_unicode_block_listview->selection().is_empty()) if (!m_unicode_block_listview->selection().is_empty())
m_unicode_block_listview->activate_selected(); m_unicode_block_listview->activate_selected();
@ -523,6 +562,7 @@ FontEditorWidget::FontEditorWidget()
m_unicode_block_listview->set_cursor(m_filter_model->index(0, 0), GUI::AbstractView::SelectionUpdate::Set); m_unicode_block_listview->set_cursor(m_filter_model->index(0, 0), GUI::AbstractView::SelectionUpdate::Set);
}; };
m_statusbar = find_descendant_of_type_named<GUI::Statusbar>("statusbar");
GUI::Application::the()->on_action_enter = [this](GUI::Action& action) { GUI::Application::the()->on_action_enter = [this](GUI::Action& action) {
auto text = action.status_tip(); auto text = action.status_tip();
if (text.is_empty()) if (text.is_empty())
@ -533,14 +573,12 @@ FontEditorWidget::FontEditorWidget()
GUI::Application::the()->on_action_leave = [this](GUI::Action&) { GUI::Application::the()->on_action_leave = [this](GUI::Action&) {
m_statusbar->set_override_text({}); m_statusbar->set_override_text({});
}; };
set_scale(scale);
} }
void FontEditorWidget::initialize(String const& path, RefPtr<Gfx::BitmapFont>&& edited_font) ErrorOr<void> FontEditorWidget::initialize(String const& path, RefPtr<Gfx::BitmapFont>&& edited_font)
{ {
if (m_edited_font == edited_font) if (m_edited_font == edited_font)
return; return {};
m_path = path; m_path = path;
m_edited_font = edited_font; m_edited_font = edited_font;
@ -584,6 +622,11 @@ void FontEditorWidget::initialize(String const& path, RefPtr<Gfx::BitmapFont>&&
i++; i++;
} }
m_undo_selection = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) UndoSelection(m_glyph_map_widget->selection().start(), m_glyph_map_widget->selection().size(), m_glyph_map_widget->active_glyph(), *m_edited_font)));
m_undo_stack->clear();
update_statusbar();
deferred_invoke([this] { deferred_invoke([this] {
auto glyph = m_glyph_map_widget->active_glyph(); auto glyph = m_glyph_map_widget->active_glyph();
m_glyph_map_widget->set_focus(true); m_glyph_map_widget->set_focus(true);
@ -592,71 +635,62 @@ void FontEditorWidget::initialize(String const& path, RefPtr<Gfx::BitmapFont>&&
update_title(); update_title();
}); });
m_undo_stack = make<GUI::UndoStack>();
m_undo_selection = adopt_ref(*new UndoSelection(m_glyph_map_widget->selection().start(), m_glyph_map_widget->selection().size(), m_glyph_map_widget->active_glyph(), *m_edited_font));
m_undo_stack->on_state_change = [this] {
m_undo_action->set_enabled(m_undo_stack->can_undo());
m_redo_action->set_enabled(m_undo_stack->can_redo());
did_modify_font();
};
m_undo_action->set_enabled(false);
m_redo_action->set_enabled(false);
update_statusbar();
if (on_initialize) if (on_initialize)
on_initialize(); on_initialize();
return {};
} }
void FontEditorWidget::initialize_menubar(GUI::Window& window) ErrorOr<void> FontEditorWidget::initialize_menubar(GUI::Window& window)
{ {
auto& file_menu = window.add_menu("&File"); auto file_menu = TRY(window.try_add_menu("&File"));
file_menu.add_action(*m_new_action); TRY(file_menu->try_add_action(*m_new_action));
file_menu.add_action(*m_open_action); TRY(file_menu->try_add_action(*m_open_action));
file_menu.add_action(*m_save_action); TRY(file_menu->try_add_action(*m_save_action));
file_menu.add_action(*m_save_as_action); TRY(file_menu->try_add_action(*m_save_as_action));
file_menu.add_separator(); TRY(file_menu->try_add_separator());
file_menu.add_action(GUI::CommonActions::make_quit_action([this](auto&) { TRY(file_menu->try_add_action(GUI::CommonActions::make_quit_action([this](auto&) {
if (!request_close()) if (!request_close())
return; return;
GUI::Application::the()->quit(); GUI::Application::the()->quit();
})); })));
auto& edit_menu = window.add_menu("&Edit"); auto edit_menu = TRY(window.try_add_menu("&Edit"));
edit_menu.add_action(*m_undo_action); TRY(edit_menu->try_add_action(*m_undo_action));
edit_menu.add_action(*m_redo_action); TRY(edit_menu->try_add_action(*m_redo_action));
edit_menu.add_separator(); TRY(edit_menu->try_add_separator());
edit_menu.add_action(*m_cut_action); TRY(edit_menu->try_add_action(*m_cut_action));
edit_menu.add_action(*m_copy_action); TRY(edit_menu->try_add_action(*m_copy_action));
edit_menu.add_action(*m_paste_action); TRY(edit_menu->try_add_action(*m_paste_action));
edit_menu.add_action(*m_delete_action); TRY(edit_menu->try_add_action(*m_delete_action));
edit_menu.add_separator(); TRY(edit_menu->try_add_separator());
edit_menu.add_action(*m_select_all_action); TRY(edit_menu->try_add_action(*m_select_all_action));
edit_menu.add_separator(); TRY(edit_menu->try_add_separator());
edit_menu.add_action(*m_copy_text_action); TRY(edit_menu->try_add_action(*m_copy_text_action));
auto& go_menu = window.add_menu("&Go"); auto go_menu = TRY(window.try_add_menu("&Go"));
go_menu.add_action(*m_previous_glyph_action); TRY(go_menu->try_add_action(*m_previous_glyph_action));
go_menu.add_action(*m_next_glyph_action); TRY(go_menu->try_add_action(*m_next_glyph_action));
go_menu.add_action(*m_go_to_glyph_action); TRY(go_menu->try_add_action(*m_go_to_glyph_action));
auto& view_menu = window.add_menu("&View"); auto view_menu = TRY(window.try_add_menu("&View"));
view_menu.add_action(*m_open_preview_action); TRY(view_menu->try_add_action(*m_open_preview_action));
view_menu.add_separator(); TRY(view_menu->try_add_separator());
view_menu.add_action(*m_show_metadata_action); TRY(view_menu->try_add_action(*m_show_metadata_action));
view_menu.add_action(*m_show_unicode_blocks_action); TRY(view_menu->try_add_action(*m_show_unicode_blocks_action));
view_menu.add_separator(); TRY(view_menu->try_add_separator());
auto& scale_menu = view_menu.add_submenu("&Scale"); auto scale_menu = TRY(view_menu->try_add_submenu("&Scale"));
scale_menu.add_action(*m_scale_five_action); TRY(scale_menu->try_add_action(*m_scale_five_action));
scale_menu.add_action(*m_scale_ten_action); TRY(scale_menu->try_add_action(*m_scale_ten_action));
scale_menu.add_action(*m_scale_fifteen_action); TRY(scale_menu->try_add_action(*m_scale_fifteen_action));
auto& help_menu = window.add_menu("&Help"); auto help_menu = TRY(window.try_add_menu("&Help"));
help_menu.add_action(GUI::CommonActions::make_help_action([](auto&) { TRY(help_menu->try_add_action(GUI::CommonActions::make_help_action([](auto&) {
Desktop::Launcher::open(URL::create_with_file_protocol("/usr/share/man/man1/FontEditor.md"), "/bin/Help"); Desktop::Launcher::open(URL::create_with_file_protocol("/usr/share/man/man1/FontEditor.md"), "/bin/Help");
})); })));
help_menu.add_action(GUI::CommonActions::make_about_action("Font Editor", GUI::Icon::default_icon("app-font-editor"), &window)); TRY(help_menu->try_add_action(GUI::CommonActions::make_about_action("Font Editor", TRY(GUI::Icon::try_create_default_icon("app-font-editor")), &window)));
return {};
} }
bool FontEditorWidget::save_file(String const& path) bool FontEditorWidget::save_file(String const& path)
@ -700,7 +734,7 @@ bool FontEditorWidget::open_file(String const& path)
} }
auto new_font = bitmap_font->unmasked_character_set(); auto new_font = bitmap_font->unmasked_character_set();
window()->set_modified(false); window()->set_modified(false);
initialize(path, move(new_font)); MUST(initialize(path, move(new_font)));
return true; return true;
} }

View file

@ -17,12 +17,24 @@
class GlyphEditorWidget; class GlyphEditorWidget;
class FontEditorWidget final class FontEditorWidget final : public GUI::Widget {
: public GUI::Widget {
C_OBJECT(FontEditorWidget) C_OBJECT(FontEditorWidget)
public: public:
static ErrorOr<NonnullRefPtr<FontEditorWidget>> try_create()
{
NonnullRefPtr<FontEditorWidget> font_editor = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) FontEditorWidget()));
TRY(font_editor->create_actions());
TRY(font_editor->create_models());
TRY(font_editor->create_toolbars());
TRY(font_editor->create_undo_stack());
return font_editor;
}
virtual ~FontEditorWidget() override = default; virtual ~FontEditorWidget() override = default;
ErrorOr<void> initialize(String const& path, RefPtr<Gfx::BitmapFont>&&);
ErrorOr<void> initialize_menubar(GUI::Window&);
bool open_file(String const&); bool open_file(String const&);
bool save_file(String const&); bool save_file(String const&);
bool request_close(); bool request_close();
@ -30,8 +42,6 @@ public:
String const& path() { return m_path; } String const& path() { return m_path; }
Gfx::BitmapFont const& edited_font() { return *m_edited_font; } Gfx::BitmapFont const& edited_font() { return *m_edited_font; }
void initialize(String const& path, RefPtr<Gfx::BitmapFont>&&);
void initialize_menubar(GUI::Window&);
bool is_showing_font_metadata() { return m_font_metadata; } bool is_showing_font_metadata() { return m_font_metadata; }
void set_show_font_metadata(bool); void set_show_font_metadata(bool);
@ -44,6 +54,11 @@ public:
private: private:
FontEditorWidget(); FontEditorWidget();
ErrorOr<void> create_actions();
ErrorOr<void> create_models();
ErrorOr<void> create_toolbars();
ErrorOr<void> create_undo_stack();
virtual void drop_event(GUI::DropEvent&) override; virtual void drop_event(GUI::DropEvent&) override;
void undo(); void undo();

View file

@ -36,14 +36,14 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
args_parser.add_positional_argument(path, "The font file for editing.", "file", Core::ArgsParser::Required::No); args_parser.add_positional_argument(path, "The font file for editing.", "file", Core::ArgsParser::Required::No);
args_parser.parse(arguments); args_parser.parse(arguments);
auto app_icon = GUI::Icon::default_icon("app-font-editor"); auto app_icon = TRY(GUI::Icon::try_create_default_icon("app-font-editor"));
auto window = TRY(GUI::Window::try_create()); auto window = TRY(GUI::Window::try_create());
window->set_icon(app_icon.bitmap_for_size(16)); window->set_icon(app_icon.bitmap_for_size(16));
window->resize(640, 470); window->resize(640, 470);
auto font_editor = TRY(window->try_set_main_widget<FontEditorWidget>()); auto font_editor = TRY(window->try_set_main_widget<FontEditorWidget>());
font_editor->initialize_menubar(*window); TRY(font_editor->initialize_menubar(*window));
if (path) { if (path) {
auto success = font_editor->open_file(path); auto success = font_editor->open_file(path);
@ -51,7 +51,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
return 1; return 1;
} else { } else {
auto mutable_font = static_ptr_cast<Gfx::BitmapFont>(Gfx::FontDatabase::default_font().clone())->unmasked_character_set(); auto mutable_font = static_ptr_cast<Gfx::BitmapFont>(Gfx::FontDatabase::default_font().clone())->unmasked_character_set();
font_editor->initialize({}, move(mutable_font)); TRY(font_editor->initialize({}, move(mutable_font)));
} }
window->on_close_request = [&]() -> GUI::Window::CloseRequestDecision { window->on_close_request = [&]() -> GUI::Window::CloseRequestDecision {