diff --git a/bin/assets/i18n/de.xml b/bin/assets/i18n/de.xml
index 21f09c73a..24ac28578 100644
--- a/bin/assets/i18n/de.xml
+++ b/bin/assets/i18n/de.xml
@@ -323,6 +323,8 @@ ecode versucht, externes Terminal zu öffnen.
Ordner/Projekteinstellungen
Ordner/Projekteinstellungen
Blockfaltungsaktualisierungsfrequenz
+ Extern
+ Extern: %s
Schriftgröße erhöhen
Schriftgröße zurücksetzen
Schriftgröße verringern
diff --git a/bin/assets/i18n/en.xml b/bin/assets/i18n/en.xml
index 58d69c73f..cbe232461 100644
--- a/bin/assets/i18n/en.xml
+++ b/bin/assets/i18n/en.xml
@@ -307,6 +307,8 @@ ecode will try to open an external terminal.
Folder/Project Settings
Folder/Project Settings
Folds Refresh Frequency
+ External
+ External: %s
Font Size Grow
Font Size Reset
Font Size Shrink
diff --git a/bin/assets/i18n/fr.xml b/bin/assets/i18n/fr.xml
index dfb659e9f..e241f506c 100644
--- a/bin/assets/i18n/fr.xml
+++ b/bin/assets/i18n/fr.xml
@@ -306,6 +306,8 @@ ecode tentera d'ouvrir un terminal externe.
Paramètres du dossier/projet
Paramètres du dossier/projet
Fréquence de rafraîchissement des replis
+ Externe
+ Externe : %s
Augmenter la taille de la police
Réinitialiser la taille de la police
Réduire la taille de la police
diff --git a/bin/assets/i18n/zh.xml b/bin/assets/i18n/zh.xml
index 003b4a15f..6bef49895 100644
--- a/bin/assets/i18n/zh.xml
+++ b/bin/assets/i18n/zh.xml
@@ -231,6 +231,8 @@
查找:
文件夹/项目设置
文件夹设置
+ 外部
+ 外部:%s
字号+
字号重置
字号-
diff --git a/include/eepp/ui/tools/uifontpickerdialog.hpp b/include/eepp/ui/tools/uifontpickerdialog.hpp
index a7f24a8e8..1acbbf087 100644
--- a/include/eepp/ui/tools/uifontpickerdialog.hpp
+++ b/include/eepp/ui/tools/uifontpickerdialog.hpp
@@ -220,6 +220,9 @@ class EE_API UIFontPickerDialog : public UIWindow {
void browseFont();
bool addExternalFont( const std::string& path, Uint32 faceIndex = 0 );
+ bool isExternalFont( const FontDesc& font ) const;
+ std::string externalTag( const FontDesc& font, bool includeFileName );
+ bool familyEntryMatchesFont( const FontFamilyEntry& family, const FontDesc& font ) const;
void clearBrowseDialog();
diff --git a/src/eepp/ui/tools/uifontpickerdialog.cpp b/src/eepp/ui/tools/uifontpickerdialog.cpp
index e0ec8fe7b..1a7032f79 100644
--- a/src/eepp/ui/tools/uifontpickerdialog.cpp
+++ b/src/eepp/ui/tools/uifontpickerdialog.cpp
@@ -472,6 +472,8 @@ void UIFontPickerDialog::loadFonts() {
void UIFontPickerDialog::setFonts( std::vector fonts ) {
FontDesc selectedFont = mSelection.font;
+ for ( const auto& font : fonts )
+ mExternalFontKeys.erase( font.getFileKey() );
mergeLoadedFonts( fonts );
for ( const auto& font : mFonts ) {
auto found = std::find_if( fonts.begin(), fonts.end(),
@@ -575,21 +577,32 @@ void UIFontPickerDialog::updateFamilies() {
FontDesc previousFont = mSelection.font;
const std::string query = String::toLower( mSearchInput->getText().toUtf8() );
+ UnorderedSet systemFamilies;
+ for ( const auto& font : mFonts ) {
+ if ( !isExternalFont( font ) )
+ systemFamilies.insert( font.family );
+ }
UnorderedSet familyKeys;
mFamilies.clear();
for ( const auto& font : mFonts ) {
- const bool external =
- mExternalFontKeys.find( font.getFileKey() ) != mExternalFontKeys.end();
+ const bool external = isExternalFont( font );
if ( wantsMonospaceOnly() && !font.monospace && !external )
continue;
- const std::string label = font.family + ( external ? " [External]" : "" );
+ const bool separateExternal = external && ( mFlags & ShowStyle ) == 0;
+ const bool externalOnly = systemFamilies.find( font.family ) == systemFamilies.end();
+ std::string label( font.family );
+ if ( separateExternal ) {
+ label += " " + externalTag( font, true );
+ } else if ( externalOnly ) {
+ label += " " + externalTag( font, false );
+ }
if ( !query.empty() && String::toLower( label ).find( query ) == std::string::npos )
continue;
const std::string familyKey =
- font.family + ( external ? "\n" + font.getFileKey() : std::string{} );
+ font.family + ( separateExternal ? "\n" + font.getFileKey() : std::string{} );
if ( familyKeys.insert( familyKey ).second )
mFamilies.push_back(
- { label, font.family, external ? font.getFileKey() : std::string{} } );
+ { label, font.family, separateExternal ? font.getFileKey() : std::string{} } );
}
mFamilyModel = std::make_shared( &mFamilies );
@@ -609,22 +622,23 @@ void UIFontPickerDialog::updateFamilies() {
void UIFontPickerDialog::updateStyles() {
mStyles.clear();
+ bool selectionMatchesFamily = false;
if ( !mFamilyList->getSelection().isEmpty() ) {
const Int64 row = mFamilyList->getSelection().first().row();
if ( row >= 0 && row < static_cast( mFamilies.size() ) ) {
const FontFamilyEntry& family = mFamilies[row];
+ selectionMatchesFamily = familyEntryMatchesFont( family, mSelection.font );
for ( const auto& font : mFonts ) {
- const bool external =
- mExternalFontKeys.find( font.getFileKey() ) != mExternalFontKeys.end();
- const bool matchesSource = family.externalFontKey.empty()
- ? !external
- : family.externalFontKey == font.getFileKey();
- if ( font.family == family.family && matchesSource &&
+ const bool external = isExternalFont( font );
+ if ( familyEntryMatchesFont( family, font ) &&
( !wantsMonospaceOnly() || font.monospace || external ) ) {
std::string label( styleLabel( font ) );
auto tagIt = mFontTags.find( font.getFileKey() );
- if ( tagIt != mFontTags.end() )
+ if ( external ) {
+ label += " " + externalTag( font, true );
+ } else if ( tagIt != mFontTags.end() ) {
label += " [" + tagIt->second + "]";
+ }
mStyles.push_back( { label, font } );
}
}
@@ -637,8 +651,7 @@ void UIFontPickerDialog::updateStyles() {
mUpdating = false;
if ( !mStyles.empty() ) {
- const std::string selectedFamily( mStyles.front().desc.family );
- if ( selectedFamily == mSelection.font.family )
+ if ( selectionMatchesFamily && ( mFlags & ShowStyle ) != 0 )
selectStyle( mSelection.font );
else
selectRegularStyle();
@@ -747,12 +760,12 @@ void UIFontPickerDialog::selectInitialRows() {
void UIFontPickerDialog::selectFamily( const FontDesc& font ) {
if ( font.family.empty() || !mFamilyModel )
return;
- const bool external = mExternalFontKeys.find( font.getFileKey() ) != mExternalFontKeys.end();
+ const bool separateExternal = isExternalFont( font ) && ( mFlags & ShowStyle ) == 0;
for ( size_t i = 0; i < mFamilies.size(); i++ ) {
const FontFamilyEntry& family = mFamilies[i];
if ( family.family == font.family &&
- ( external ? family.externalFontKey == font.getFileKey()
- : family.externalFontKey.empty() ) ) {
+ ( separateExternal ? family.externalFontKey == font.getFileKey()
+ : family.externalFontKey.empty() ) ) {
mFamilyList->setSelection( mFamilyModel->index( i ) );
return;
}
@@ -899,6 +912,30 @@ bool UIFontPickerDialog::addExternalFont( const std::string& path, Uint32 faceIn
return true;
}
+bool UIFontPickerDialog::isExternalFont( const FontDesc& font ) const {
+ return mExternalFontKeys.find( font.getFileKey() ) != mExternalFontKeys.end();
+}
+
+std::string UIFontPickerDialog::externalTag( const FontDesc& font, bool includeFileName ) {
+ const std::string fileName( FileSystem::fileNameFromPath( font.path ) );
+ if ( includeFileName && !fileName.empty() ) {
+ return "[" +
+ String::format( i18n( "font_picker_external_file", "External: %s" ).toUtf8(),
+ fileName ) +
+ "]";
+ }
+ return "[" + i18n( "font_picker_external", "External" ).toUtf8() + "]";
+}
+
+bool UIFontPickerDialog::familyEntryMatchesFont( const FontFamilyEntry& family,
+ const FontDesc& font ) const {
+ if ( family.family != font.family )
+ return false;
+ if ( family.externalFontKey.empty() )
+ return ( mFlags & ShowStyle ) != 0 || !isExternalFont( font );
+ return family.externalFontKey == font.getFileKey();
+}
+
void UIFontPickerDialog::clearBrowseDialog() {
if ( !mBrowseDialog )
return;
@@ -930,7 +967,6 @@ void UIFontPickerDialog::setSelectedFont( const FontDesc& desc ) {
mMonospaceOnly->setChecked( true );
selectFamily( selection );
updateStyles();
- selectStyle( selection );
updateSelectionFromLists( false );
if ( previousSelection != mSelection )
emitSelectionChanged();
diff --git a/src/tests/unit_tests/uifontpickerdialog_tests.cpp b/src/tests/unit_tests/uifontpickerdialog_tests.cpp
index ecce785fb..17f953615 100644
--- a/src/tests/unit_tests/uifontpickerdialog_tests.cpp
+++ b/src/tests/unit_tests/uifontpickerdialog_tests.cpp
@@ -65,6 +65,13 @@ class TestFontPickerDialog : public UIFontPickerDialog {
Uint32 getPreviewTextSize() const { return mPreviewText->getFontSize(); }
Uint32 getPreviewInputSize() const { return mPreviewInput->getFontSize(); }
String getDetails() const { return mDetailsText->getText(); }
+ void setFontsForTest( std::vector fonts ) { setFonts( std::move( fonts ) ); }
+ void markSelectedFontExternalForTest() {
+ FontDesc selectedFont = mSelection.font;
+ mExternalFontKeys.insert( selectedFont.getFileKey() );
+ updateFamilies();
+ setSelectedFont( selectedFont );
+ }
protected:
TestFontPickerDialog( Uint32 flags ) : UIFontPickerDialog( flags ) {}
@@ -97,7 +104,7 @@ UTEST( UIFontPickerDialog, PreselectsExternalFontPath ) {
EXPECT_FALSE( selectionDialog->getSelection().font.family.empty() );
}
-UTEST( UIFontPickerDialog, ExternalFontKeepsSeparateFamilyEntryOnNameCollision ) {
+UTEST( UIFontPickerDialog, ExternalFontBecomesDistinctStyleOnFamilyNameCollision ) {
const std::string managedFontPath =
Sys::getProcessPath() + "assets/fonts/NotoSansKR-Regular.ttf";
ASSERT_TRUE( FileSystem::fileExists( managedFontPath ) );
@@ -126,23 +133,44 @@ UTEST( UIFontPickerDialog, ExternalFontKeepsSeparateFamilyEntryOnNameCollision )
EXPECT_STDSTREQ( externalPath, dialog->getSelection().font.path );
EXPECT_FALSE( dialog->getFamilyList()->getSelection().isEmpty() );
- EXPECT_STDSTREQ( family + " [External]",
- dialog->getFamilyList()->getSelection().first().data().toString() );
+ EXPECT_STDSTREQ( family, dialog->getFamilyList()->getSelection().first().data().toString() );
+ EXPECT_TRUE( dialog->getStyleList()->getSelection().first().data().toString().find(
+ "[External:" ) != std::string::npos );
- bool foundSystemFamily = false;
- bool foundExternalFamily = false;
+ size_t matchingFamilies = 0;
Model* familyModel = dialog->getFamilyList()->getModel();
ASSERT_TRUE( familyModel != nullptr );
for ( size_t row = 0; row < familyModel->rowCount(); row++ ) {
const std::string label = familyModel->index( row ).data().toString();
- foundSystemFamily |= label == family;
- foundExternalFamily |= label == family + " [External]";
+ matchingFamilies += label == family;
}
- EXPECT_TRUE( foundSystemFamily );
- EXPECT_TRUE( foundExternalFamily );
+ EXPECT_EQ( 1u, matchingFamilies );
+
+ bool foundSystemStyle = false;
+ bool foundExternalStyle = false;
+ Model* styleModel = dialog->getStyleList()->getModel();
+ ASSERT_TRUE( styleModel != nullptr );
+ for ( size_t row = 0; row < styleModel->rowCount(); row++ ) {
+ const std::string label = styleModel->index( row ).data().toString();
+ foundSystemStyle |= label.find( "[External:" ) == std::string::npos;
+ foundExternalStyle |= label.find( "[External:" ) != std::string::npos;
+ }
+ EXPECT_TRUE( foundSystemStyle );
+ EXPECT_TRUE( foundExternalStyle );
EXPECT_EQ( managedFont.get(), resourceScope.findFont( externalName ).get() );
+ TestFontPickerDialog* hiddenStyleDialog =
+ TestFontPickerDialog::New( UIFontPickerDialog::ShowSize );
+ hiddenStyleDialog->setSelectedFont( externalPath );
+ pumpUntil( app.getUI(),
+ [hiddenStyleDialog] { return hiddenStyleDialog->getButtonOK()->isEnabled(); } );
+ EXPECT_STDSTREQ( externalPath, hiddenStyleDialog->getSelection().font.path );
+ EXPECT_FALSE( hiddenStyleDialog->getStyleList()->getParent()->isVisible() );
+ EXPECT_TRUE( hiddenStyleDialog->getFamilyList()->getSelection().first().data().toString().find(
+ "[External:" ) != std::string::npos );
+
dialog->releasePreviewFont();
+ hiddenStyleDialog->releasePreviewFont();
FileSystem::fileRemove( externalPath );
}
@@ -324,7 +352,44 @@ UTEST( UIFontPickerDialog, AsyncLoadPreservesExternalFontPreselection ) {
EXPECT_FALSE( dialog->getFamilyList()->getSelection().isEmpty() );
EXPECT_FALSE( dialog->getStyleList()->getSelection().isEmpty() );
EXPECT_TRUE( dialog->getFamilyList()->getSelection().first().data().toString().find(
- "[External]" ) != std::string::npos );
+ "[External" ) != std::string::npos );
+}
+
+UTEST( UIFontPickerDialog, AsyncLoadPromotesEnumeratedExternalFontToSystemFamily ) {
+ std::vector fonts = SystemFontResolver::instance()->enumerate();
+ auto regular = std::find_if( fonts.begin(), fonts.end(), []( const FontDesc& font ) {
+ return font.weight == FontWeight::Normal && !font.italic && !font.family.empty() &&
+ !font.path.empty();
+ } );
+ if ( regular == fonts.end() )
+ UTEST_SKIP( "no regular system font available" );
+ const FontDesc regularFont = *regular;
+
+ UIApplication app(
+ WindowSettings( 320, 240, "eepp - UIFontPickerDialog Test", WindowStyle::Default,
+ WindowBackend::Default, 32 ),
+ UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 1 ) );
+
+ TestFontPickerDialog* dialog = TestFontPickerDialog::New();
+ dialog->setFontsForTest( fonts );
+ dialog->setSelectedFont( regularFont );
+ dialog->markSelectedFontExternalForTest();
+ EXPECT_STDSTREQ( regularFont.family + " [External]",
+ dialog->getFamilyList()->getSelection().first().data().toString() );
+ EXPECT_TRUE( dialog->getStyleList()->getSelection().first().data().toString().find(
+ "[External:" ) != std::string::npos );
+
+ dialog->setFontsForTest( std::move( fonts ) );
+ EXPECT_STDSTREQ( regularFont.path, dialog->getSelection().font.path );
+ EXPECT_EQ( regularFont.faceIndex, dialog->getSelection().font.faceIndex );
+ EXPECT_EQ( FontWeight::Normal, dialog->getSelection().font.weight );
+ EXPECT_FALSE( dialog->getSelection().font.italic );
+ EXPECT_STDSTREQ( regularFont.family,
+ dialog->getFamilyList()->getSelection().first().data().toString() );
+ EXPECT_TRUE( dialog->getStyleList()->getSelection().first().data().toString().find(
+ "[External:" ) == std::string::npos );
+
+ dialog->releasePreviewFont();
}
UTEST( UIFontPickerDialog, DefaultColorComesFromTheme ) {
@@ -415,6 +480,43 @@ UTEST( UIFontPickerDialog, StyleLabelsIncludeFontStretch ) {
EXPECT_TRUE( foundStyle );
}
+UTEST( UIFontPickerDialog, PreservesVisibleStyleAndPrefersRegularWhenHidden ) {
+ std::vector fonts = SystemFontResolver::instance()->enumerate();
+ auto italic = std::find_if( fonts.begin(), fonts.end(), [&]( const FontDesc& candidate ) {
+ if ( !candidate.italic )
+ return false;
+ return std::find_if( fonts.begin(), fonts.end(), [&]( const FontDesc& regular ) {
+ return regular.family == candidate.family &&
+ regular.weight == FontWeight::Normal && !regular.italic;
+ } ) != fonts.end();
+ } );
+ if ( italic == fonts.end() )
+ UTEST_SKIP( "no family with regular and italic styles available" );
+ const FontDesc italicFont = *italic;
+
+ UIApplication app(
+ WindowSettings( 320, 240, "eepp - UIFontPickerDialog Test", WindowStyle::Default,
+ WindowBackend::Default, 32 ),
+ UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 1 ) );
+
+ TestFontPickerDialog* visible = TestFontPickerDialog::New( UIFontPickerDialog::ShowStyle );
+ visible->setFontsForTest( fonts );
+ visible->setSelectedFont( italicFont );
+ EXPECT_TRUE( visible->getStyleList()->getParent()->isVisible() );
+ EXPECT_EQ( italicFont.weight, visible->getSelection().font.weight );
+ EXPECT_TRUE( visible->getSelection().font.italic );
+
+ TestFontPickerDialog* hidden = TestFontPickerDialog::New( UIFontPickerDialog::ShowSize );
+ hidden->setFontsForTest( std::move( fonts ) );
+ hidden->setSelectedFont( italicFont );
+ EXPECT_FALSE( hidden->getStyleList()->getParent()->isVisible() );
+ EXPECT_EQ( FontWeight::Normal, hidden->getSelection().font.weight );
+ EXPECT_FALSE( hidden->getSelection().font.italic );
+
+ visible->releasePreviewFont();
+ hidden->releasePreviewFont();
+}
+
UTEST( UIFontPickerDialog, ApplyButtonEmitsOnApply ) {
UIApplication app(
WindowSettings( 320, 240, "eepp - UIFontPickerDialog Test", WindowStyle::Default,
diff --git a/src/tools/ecode/fontpickercontroller.cpp b/src/tools/ecode/fontpickercontroller.cpp
index be8745f23..124342b82 100644
--- a/src/tools/ecode/fontpickercontroller.cpp
+++ b/src/tools/ecode/fontpickercontroller.cpp
@@ -105,7 +105,8 @@ void FontPickerController::openFontDialog( std::string& fontPath, bool loadingMo
defaultResourceScope().publishLocalFont( std::move( fontName ), font );
};
- const Uint32 flags = ( pickFontSize ? UIFontPickerDialog::ShowSize : 0 ) |
+ const Uint32 flags = UIFontPickerDialog::ShowStyle |
+ ( pickFontSize ? UIFontPickerDialog::ShowSize : 0 ) |
( loadingMonoFont ? UIFontPickerDialog::MonospaceOnly : 0 );
UIFontPickerDialog* dialog = UIFontPickerDialog::New( flags );
dialog->setTitle( mApp->i18n( "select_font", "Select Font" ) );