FileManager: Add "Font" tab to File Properties window

We now keep the MappedFile around when inspecting a font, so that we can
use that font to display a preview.
This commit is contained in:
Sam Atkins 2023-06-27 18:57:13 +01:00 committed by Sam Atkins
parent 98eaa4b044
commit 8ec691057f
4 changed files with 228 additions and 0 deletions

View file

@ -8,6 +8,7 @@ serenity_component(
compile_gml(FileManagerWindow.gml FileManagerWindowGML.h file_manager_window_gml)
compile_gml(FileOperationProgress.gml FileOperationProgressGML.h file_operation_progress_gml)
compile_gml(PropertiesWindowAudioTab.gml PropertiesWindowAudioTabGML.h properties_window_audio_tab_gml)
compile_gml(PropertiesWindowFontTab.gml PropertiesWindowFontTabGML.h properties_window_font_tab_gml)
compile_gml(PropertiesWindowGeneralTab.gml PropertiesWindowGeneralTabGML.h properties_window_general_tab_gml)
compile_gml(PropertiesWindowImageTab.gml PropertiesWindowImageTabGML.h properties_window_image_tab_gml)
@ -24,6 +25,7 @@ set(GENERATED_SOURCES
FileManagerWindowGML.h
FileOperationProgressGML.h
PropertiesWindowAudioTabGML.h
PropertiesWindowFontTabGML.h
PropertiesWindowGeneralTabGML.h
PropertiesWindowImageTabGML.h
)

View file

@ -11,6 +11,7 @@
#include <AK/NumberFormat.h>
#include <Applications/FileManager/DirectoryView.h>
#include <Applications/FileManager/PropertiesWindowAudioTabGML.h>
#include <Applications/FileManager/PropertiesWindowFontTabGML.h>
#include <Applications/FileManager/PropertiesWindowGeneralTabGML.h>
#include <Applications/FileManager/PropertiesWindowImageTabGML.h>
#include <LibAudio/Loader.h>
@ -27,6 +28,12 @@
#include <LibGUI/MessageBox.h>
#include <LibGUI/SeparatorWidget.h>
#include <LibGUI/TabWidget.h>
#include <LibGfx/Font/BitmapFont.h>
#include <LibGfx/Font/FontDatabase.h>
#include <LibGfx/Font/FontStyleMapping.h>
#include <LibGfx/Font/OpenType/Font.h>
#include <LibGfx/Font/Typeface.h>
#include <LibGfx/Font/WOFF/Font.h>
#include <LibGfx/ICC/Profile.h>
#include <LibGfx/ICC/Tags.h>
#include <grp.h>
@ -218,6 +225,9 @@ ErrorOr<void> PropertiesWindow::create_file_type_specific_tabs(GUI::TabWidget& t
if (mime_type.starts_with("audio/"sv))
return create_audio_tab(tab_widget, move(mapped_file));
if (mime_type.starts_with("font/"sv) || m_path.ends_with(".font"sv))
return create_font_tab(tab_widget, move(mapped_file), mime_type);
if (mime_type.starts_with("image/"sv))
return create_image_tab(tab_widget, move(mapped_file), mime_type);
@ -262,6 +272,98 @@ ErrorOr<void> PropertiesWindow::create_audio_tab(GUI::TabWidget& tab_widget, Non
return {};
}
struct FontInfo {
enum class Format {
BitmapFont,
OpenType,
TrueType,
WOFF,
WOFF2,
};
Format format;
NonnullRefPtr<Gfx::Typeface> typeface;
};
static ErrorOr<FontInfo> load_font(StringView path, StringView mime_type, NonnullRefPtr<Core::MappedFile> mapped_file)
{
if (path.ends_with(".font"sv)) {
auto font = TRY(Gfx::BitmapFont::try_load_from_mapped_file(mapped_file));
auto typeface = TRY(try_make_ref_counted<Gfx::Typeface>(font->family(), font->variant()));
typeface->add_bitmap_font(move(font));
return FontInfo { FontInfo::Format::BitmapFont, move(typeface) };
}
if (mime_type == "font/otf" || mime_type == "font/ttf") {
auto font = TRY(OpenType::Font::try_load_from_externally_owned_memory(mapped_file->bytes()));
auto typeface = TRY(try_make_ref_counted<Gfx::Typeface>(font->family(), font->variant()));
typeface->set_vector_font(move(font));
return FontInfo {
mime_type == "font/otf" ? FontInfo::Format::OpenType : FontInfo::Format::TrueType,
move(typeface)
};
}
if (mime_type == "font/woff" || mime_type == "font/woff2") {
auto font = TRY(WOFF::Font::try_load_from_externally_owned_memory(mapped_file->bytes()));
auto typeface = TRY(try_make_ref_counted<Gfx::Typeface>(font->family(), font->variant()));
typeface->set_vector_font(move(font));
return FontInfo {
mime_type == "font/woff" ? FontInfo::Format::WOFF : FontInfo::Format::WOFF2,
move(typeface)
};
}
return Error::from_string_view("Unrecognized font format."sv);
}
ErrorOr<void> PropertiesWindow::create_font_tab(GUI::TabWidget& tab_widget, NonnullRefPtr<Core::MappedFile> mapped_file, StringView mime_type)
{
auto font_info_or_error = load_font(m_path, mime_type, mapped_file);
if (font_info_or_error.is_error()) {
warnln("Failed to open '{}': {}", m_path, font_info_or_error.release_error());
return {};
}
auto font_info = font_info_or_error.release_value();
auto& typeface = font_info.typeface;
auto tab = TRY(tab_widget.try_add_tab<GUI::Widget>("Font"_short_string));
TRY(tab->load_from_gml(properties_window_font_tab_gml));
String format_name;
switch (font_info.format) {
case FontInfo::Format::BitmapFont:
format_name = TRY("Bitmap Font"_string);
break;
case FontInfo::Format::OpenType:
format_name = TRY("OpenType"_string);
break;
case FontInfo::Format::TrueType:
format_name = TRY("TrueType"_string);
break;
case FontInfo::Format::WOFF:
format_name = TRY("WOFF"_string);
break;
case FontInfo::Format::WOFF2:
format_name = TRY("WOFF2"_string);
break;
}
tab->find_descendant_of_type_named<GUI::Label>("font_format")->set_text(format_name);
tab->find_descendant_of_type_named<GUI::Label>("font_family")->set_text(TRY(String::from_deprecated_string(typeface->family())));
tab->find_descendant_of_type_named<GUI::Label>("font_fixed_width")->set_text(typeface->is_fixed_width() ? "Yes"_short_string : "No"_short_string);
tab->find_descendant_of_type_named<GUI::Label>("font_width")->set_text(TRY(String::from_utf8(Gfx::width_to_name(static_cast<Gfx::FontWidth>(typeface->width())))));
auto nearest_weight_class_name = [](unsigned weight) {
if (weight > 925)
return Gfx::weight_to_name(Gfx::FontWeight::ExtraBlack);
unsigned weight_class = clamp(round_to<unsigned>(weight / 100.0) * 100, Gfx::FontWeight::Thin, Gfx::FontWeight::Black);
return Gfx::weight_to_name(weight_class);
};
auto weight = typeface->weight();
tab->find_descendant_of_type_named<GUI::Label>("font_weight")->set_text(TRY(String::formatted("{} ({})", weight, nearest_weight_class_name(weight))));
tab->find_descendant_of_type_named<GUI::Label>("font_slope")->set_text(TRY(String::from_utf8(Gfx::slope_to_name(typeface->slope()))));
return {};
}
ErrorOr<void> PropertiesWindow::create_image_tab(GUI::TabWidget& tab_widget, NonnullRefPtr<Core::MappedFile> mapped_file, StringView mime_type)
{
auto image_decoder = Gfx::ImageDecoder::try_create_for_raw_bytes(mapped_file->bytes(), mime_type);

View file

@ -31,6 +31,7 @@ private:
ErrorOr<void> create_general_tab(GUI::TabWidget&, bool disable_rename);
ErrorOr<void> create_file_type_specific_tabs(GUI::TabWidget&);
ErrorOr<void> create_audio_tab(GUI::TabWidget&, NonnullRefPtr<Core::MappedFile>);
ErrorOr<void> create_font_tab(GUI::TabWidget&, NonnullRefPtr<Core::MappedFile>, StringView mime_type);
ErrorOr<void> create_image_tab(GUI::TabWidget&, NonnullRefPtr<Core::MappedFile>, StringView mime_type);
struct PermissionMasks {

View file

@ -0,0 +1,123 @@
@GUI::Widget {
layout: @GUI::VerticalBoxLayout {
margins: [8]
spacing: 12
}
@GUI::GroupBox {
title: "Font"
preferred_height: "shrink"
layout: @GUI::VerticalBoxLayout {
margins: [12, 8, 0]
spacing: 2
}
@GUI::Widget {
layout: @GUI::HorizontalBoxLayout {
spacing: 12
}
@GUI::Label {
text: "Format:"
text_alignment: "TopLeft"
fixed_width: 80
}
@GUI::Label {
name: "font_format"
text: "TrueType"
text_alignment: "TopLeft"
}
}
@GUI::Widget {
layout: @GUI::HorizontalBoxLayout {
spacing: 12
}
@GUI::Label {
text: "Family:"
text_alignment: "TopLeft"
fixed_width: 80
}
@GUI::Label {
name: "font_family"
text: "SerenitySans"
text_alignment: "TopLeft"
}
}
@GUI::Widget {
layout: @GUI::HorizontalBoxLayout {
spacing: 12
}
@GUI::Label {
text: "Fixed width:"
text_alignment: "TopLeft"
fixed_width: 80
}
@GUI::Label {
name: "font_fixed_width"
text: "No"
text_alignment: "TopLeft"
}
}
@GUI::Widget {
layout: @GUI::HorizontalBoxLayout {
spacing: 12
}
@GUI::Label {
text: "Width:"
text_alignment: "TopLeft"
fixed_width: 80
}
@GUI::Label {
name: "font_width"
text: "Normal"
text_alignment: "TopLeft"
}
}
@GUI::Widget {
layout: @GUI::HorizontalBoxLayout {
spacing: 12
}
@GUI::Label {
text: "Weight:"
text_alignment: "TopLeft"
fixed_width: 80
}
@GUI::Label {
name: "font_weight"
text: "500 (Medium)"
text_alignment: "TopLeft"
}
}
@GUI::Widget {
layout: @GUI::HorizontalBoxLayout {
spacing: 12
}
@GUI::Label {
text: "Slope:"
text_alignment: "TopLeft"
fixed_width: 80
}
@GUI::Label {
name: "font_slope"
text: "Regular"
text_alignment: "TopLeft"
}
}
}
}