FontEditor: Allow application to launch without a font

Since LibFSAC requires a reified window before loading a font, it
makes sense to have a safe null state for the app.

This lets us stay alive after a failed file request on startup,
handle failure at any point during initialization, and claw back
memory from all our font RefPtrs.

A default startup font or none at all can now be set in FontEditor.ini
This commit is contained in:
thankyouverycool 2023-05-10 17:01:28 -04:00 committed by Andreas Kling
parent aaf60053f1
commit 7fa8fae786
Notes: sideshowbarker 2024-07-17 05:01:20 +09:00
5 changed files with 127 additions and 20 deletions

View file

@ -0,0 +1,2 @@
[Defaults]
Font=/res/fonts/KaticaRegular10.font

View file

@ -23,10 +23,13 @@
@FontEditor::GlyphEditorWidget {
name: "glyph_editor_widget"
visible: false
}
@GUI::Widget {
name: "width_control_container"
preferred_height: "shrink"
visible: false
layout: @GUI::VerticalBoxLayout {}
@GUI::SpinBox {

View file

@ -9,6 +9,7 @@
#include "GlyphEditorWidget.h"
#include "NewFontDialog.h"
#include <AK/Array.h>
#include <AK/ScopeGuard.h>
#include <AK/String.h>
#include <AK/StringBuilder.h>
#include <AK/StringUtils.h>
@ -230,9 +231,9 @@ ErrorOr<void> MainWidget::create_actions()
m_show_unicode_blocks_action = GUI::Action::create_checkable("&Unicode Blocks", { Mod_Ctrl, Key_U }, [this](auto& action) {
m_unicode_block_container->set_visible(action.is_checked());
if (action.is_checked())
m_search_textbox->set_focus(true);
m_search_textbox->set_focus(m_initialized);
else
m_glyph_map_widget->set_focus(true);
m_glyph_map_widget->set_focus(m_initialized);
Config::write_bool("FontEditor"sv, "Layout"sv, "ShowUnicodeBlocks"sv, action.is_checked());
});
m_show_unicode_blocks_action->set_checked(show_unicode_blocks);
@ -463,6 +464,7 @@ ErrorOr<void> MainWidget::create_widgets()
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_toolbar_container = find_descendant_of_type_named<GUI::ToolbarContainer>("toolbar_container");
m_width_control_container = find_descendant_of_type_named<GUI::Widget>("width_control_container");
m_glyph_map_widget = find_descendant_of_type_named<GUI::GlyphMapWidget>("glyph_map_widget");
m_glyph_editor_widget = find_descendant_of_type_named<GlyphEditorWidget>("glyph_editor_widget");
@ -637,19 +639,24 @@ ErrorOr<void> MainWidget::initialize(StringView path, RefPtr<Gfx::BitmapFont>&&
if (m_font == mutable_font)
return {};
TRY(m_glyph_map_widget->initialize(mutable_font));
ScopeGuard reset_on_error([&] {
if (!m_initialized)
reset();
});
m_initialized = false;
m_path = TRY(String::from_utf8(path));
m_font = move(mutable_font);
TRY(m_glyph_map_widget->initialize(m_font));
auto active_glyph = m_glyph_map_widget->active_glyph();
m_glyph_map_widget->set_focus(true);
m_glyph_map_widget->scroll_to_glyph(active_glyph);
auto selection = m_glyph_map_widget->selection().normalized();
m_undo_selection = TRY(try_make_ref_counted<UndoSelection>(selection.start(), selection.size(), active_glyph, *mutable_font, *m_glyph_map_widget));
m_undo_selection = TRY(try_make_ref_counted<UndoSelection>(selection.start(), selection.size(), active_glyph, *m_font, *m_glyph_map_widget));
m_undo_stack->clear();
m_path = TRY(String::from_utf8(path));
m_font = mutable_font;
if (m_preview_label)
m_preview_label->set_font(*m_font);
@ -692,6 +699,9 @@ ErrorOr<void> MainWidget::initialize(StringView path, RefPtr<Gfx::BitmapFont>&&
window()->set_modified(false);
update_title();
update_statusbar();
set_actions_enabled(true);
set_widgets_enabled(true);
m_initialized = true;
return {};
}
@ -881,6 +891,9 @@ void MainWidget::did_modify_font()
void MainWidget::update_statusbar()
{
if (!m_font)
return;
if (!m_statusbar->is_visible())
return;
@ -1073,4 +1086,88 @@ void MainWidget::show_error(Error error, StringView action, StringView basename)
(void)GUI::MessageBox::try_show_error(window(), maybe_message.release_value());
}
void MainWidget::reset()
{
VERIFY(window());
m_initialized = false;
m_font = nullptr;
m_path = {};
m_undo_selection = nullptr;
m_undo_stack->clear();
(void)m_glyph_map_widget->initialize(nullptr);
m_glyph_editor_widget->initialize(nullptr);
if (m_font_preview_window)
m_font_preview_window->close();
if (m_preview_label)
m_preview_label->set_font(nullptr);
m_name_textbox->set_text({}, GUI::AllowCallback::No);
m_family_textbox->set_text({}, GUI::AllowCallback::No);
m_slope_combobox->set_text({}, GUI::AllowCallback::No);
m_weight_combobox->set_text({}, GUI::AllowCallback::No);
m_presentation_spinbox->set_text({}, GUI::AllowCallback::No);
m_baseline_spinbox->set_text({}, GUI::AllowCallback::No);
m_mean_line_spinbox->set_text({}, GUI::AllowCallback::No);
m_spacing_spinbox->set_text({}, GUI::AllowCallback::No);
m_fixed_width_checkbox->set_checked(false, GUI::AllowCallback::No);
m_statusbar->set_text(0, {});
m_statusbar->set_text(1, {});
window()->set_modified(false);
window()->set_title("Font Editor");
set_actions_enabled(false);
set_widgets_enabled(false);
set_focus(true);
}
void MainWidget::set_actions_enabled(bool enabled)
{
m_save_action->set_enabled(enabled);
m_save_as_action->set_enabled(enabled);
m_cut_action->set_enabled(enabled);
m_copy_action->set_enabled(enabled);
m_paste_action->set_enabled(enabled && GUI::Clipboard::the().fetch_mime_type() == "glyph/x-fonteditor");
m_delete_action->set_enabled(enabled);
m_copy_text_action->set_enabled(enabled);
m_select_all_action->set_enabled(enabled);
m_go_to_glyph_action->set_enabled(enabled);
m_previous_glyph_action->set_enabled(enabled);
m_next_glyph_action->set_enabled(enabled);
m_move_glyph_action->set_enabled(enabled);
m_paint_glyph_action->set_enabled(enabled);
m_flip_horizontal_action->set_enabled(enabled);
m_flip_vertical_action->set_enabled(enabled);
m_rotate_clockwise_action->set_enabled(enabled);
m_rotate_counterclockwise_action->set_enabled(enabled);
m_open_preview_action->set_enabled(enabled);
m_highlight_modifications_action->set_enabled(enabled);
m_show_system_emoji_action->set_enabled(enabled);
m_scale_five_action->set_enabled(enabled);
m_scale_ten_action->set_enabled(enabled);
m_scale_fifteen_action->set_enabled(enabled);
}
void MainWidget::set_widgets_enabled(bool enabled)
{
m_font_metadata_groupbox->set_enabled(enabled);
m_unicode_block_container->set_enabled(enabled);
m_width_control_container->set_enabled(enabled);
m_width_control_container->set_visible(enabled);
m_glyph_map_widget->set_enabled(enabled);
m_glyph_editor_widget->set_enabled(enabled);
m_glyph_editor_widget->set_visible(enabled);
m_statusbar->segment(1).set_visible(enabled);
}
}

View file

@ -29,6 +29,9 @@ public:
virtual ~MainWidget() override = default;
void show_error(Error, StringView action, StringView basename = {});
void reset();
ErrorOr<void> initialize(StringView path, RefPtr<Gfx::BitmapFont>&&);
ErrorOr<void> initialize_menubar(GUI::Window&);
@ -61,6 +64,8 @@ private:
void update_title();
void set_scale_and_save(i32);
void set_actions_enabled(bool);
void set_widgets_enabled(bool);
ErrorOr<void> copy_selected_glyphs();
ErrorOr<void> cut_selected_glyphs();
@ -70,8 +75,6 @@ private:
void push_undo();
void reset_selection_and_push_undo();
void show_error(Error, StringView action, StringView basename = {});
RefPtr<GUI::GlyphMapWidget> m_glyph_map_widget;
RefPtr<GlyphEditorWidget> m_glyph_editor_widget;
@ -122,6 +125,7 @@ private:
RefPtr<GUI::Statusbar> m_statusbar;
RefPtr<GUI::ToolbarContainer> m_toolbar_container;
RefPtr<GUI::Widget> m_unicode_block_container;
RefPtr<GUI::Widget> m_width_control_container;
RefPtr<GUI::ComboBox> m_weight_combobox;
RefPtr<GUI::ComboBox> m_slope_combobox;
RefPtr<GUI::SpinBox> m_spacing_spinbox;
@ -150,6 +154,7 @@ private:
Vector<String> m_font_slope_list;
Vector<String> m_unicode_block_list;
Unicode::CodePointRange m_range { 0x0000, 0x10FFFF };
bool m_initialized { false };
};
}

View file

@ -14,7 +14,6 @@
#include <LibGUI/Application.h>
#include <LibGUI/Icon.h>
#include <LibGUI/Window.h>
#include <LibGfx/Font/BitmapFont.h>
#include <LibMain/Main.h>
ErrorOr<int> serenity_main(Main::Arguments arguments)
@ -47,6 +46,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
auto font_editor = TRY(window->set_main_widget<FontEditor::MainWidget>());
TRY(font_editor->initialize_menubar(*window));
font_editor->reset();
window->on_close_request = [&]() -> GUI::Window::CloseRequestDecision {
if (font_editor->request_close())
@ -56,16 +56,16 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
window->show();
auto path_to_load = path.is_empty() ? "/res/fonts/KaticaRegular10.font"sv : path;
auto file = TRY(FileSystemAccessClient::Client::the().request_file_read_only_approved(window, path_to_load));
if (!path.is_empty()) {
TRY(font_editor->open_file(file.filename(), file.release_stream()));
} else {
auto mapped_file = TRY(Core::MappedFile::map_from_file(file.release_stream(), path_to_load));
auto mutable_font = TRY(TRY(Gfx::BitmapFont::try_load_from_mapped_file(mapped_file))->unmasked_character_set());
TRY(font_editor->initialize({}, move(mutable_font)));
}
auto default_path = TRY(String::from_deprecated_string(Config::read_string("FontEditor"sv, "Defaults"sv, "Font"sv, {})));
auto path_to_load = path.is_empty() ? default_path : path;
auto open_or_error = [&]() -> ErrorOr<void> {
if (path_to_load.is_empty())
return {};
auto file = TRY(FileSystemAccessClient::Client::the().request_file_read_only_approved(window, path_to_load));
return TRY(font_editor->open_file(path, file.release_stream()));
}();
if (open_or_error.is_error())
font_editor->show_error(open_or_error.release_error(), "Opening"sv, path_to_load);
return app->exec();
}