Add font size selection to ecode font picker:

Allow UIFontPickerDialog sections to be hidden and make ecode's UI, editor, and terminal font dialogs select both the font and its size.
Keep external fonts selectable across asynchronous font enumeration, including when they share a family name with a system font, and label them as external. Also normalize UILinearLayout weights across visible children so hidden picker columns release their space correctly.
Update font picker previews, size units, menu labels, translations, and lifetime handling.

Addresses SpartanJ/ecode#944.
This commit is contained in:
Martín Lucas Golini
2026-08-11 12:38:34 -03:00
parent 10a5d7effc
commit 6df0c0738f
17 changed files with 397 additions and 115 deletions
+100 -39
View File
@@ -22,7 +22,6 @@
#include <eepp/ui/uitextview.hpp>
#include <eepp/ui/uitheme.hpp>
#include <eepp/ui/uithememanager.hpp>
#include <set>
#include <unordered_map>
using namespace EE::UI::Abstract;
@@ -89,7 +88,7 @@ static const char* FONT_PICKER_LAYOUT = R"xml(
<TextView class="column_label" layout_width="match_parent" layout_height="wrap_content" text='@string(font_picker_font_family, "Font Family")' />
<ListView id="family_list" layout_width="match_parent" layout_height="0dp" layout_weight="1" />
</LinearLayout>
<LinearLayout orientation="vertical" layout_width="0dp" layout_weight="0.18" layout_height="match_parent" margin-left="8dp">
<LinearLayout id="style_column" orientation="vertical" layout_width="0dp" layout_weight="0.18" layout_height="match_parent" margin-left="8dp">
<TextView class="column_label" layout_width="match_parent" layout_height="wrap_content" text='@string(font_picker_style, "Style")' />
<ListView id="style_list" layout_width="match_parent" layout_height="0dp" layout_weight="1" />
</LinearLayout>
@@ -100,7 +99,7 @@ static const char* FONT_PICKER_LAYOUT = R"xml(
<LinearLayout orientation="vertical" layout_width="0dp" layout_weight="0.54" layout_height="match_parent" margin-left="8dp">
<TextView class="column_label" layout_width="match_parent" layout_height="wrap_content" text='@string(font_picker_preview, "Preview")' />
<TextView id="preview_text" class="preview_text" layout_width="match_parent" layout_height="0dp" layout_weight="1" text='The quick brown fox&#10;jumps over the lazy dog' gravity="center" />
<TextInput id="preview_input" layout_width="match_parent" layout_height="28dp" margin-top="8dp" />
<TextInput id="preview_input" layout_width="match_parent" layout_height="wrap_content" min-height="28dp" margin-top="8dp" />
</LinearLayout>
</LinearLayout>
<Loader id="font_loader" lw="64dp" lh="64dp" outline-thickness="6dp" lg="center" visible="false" />
@@ -151,6 +150,33 @@ class StyleListModel final : public Model {
const std::vector<UIFontPickerDialog::FontStyleEntry>* mData{ nullptr };
};
class FamilyListModel final : public Model {
public:
explicit FamilyListModel( const std::vector<UIFontPickerDialog::FontFamilyEntry>* data ) :
mData( data ) {}
size_t rowCount( const ModelIndex& ) const override { return mData ? mData->size() : 0; }
size_t columnCount( const ModelIndex& ) const override { return 1; }
ModelIndex index( int row, int column,
const ModelIndex& parent = ModelIndex() ) const override {
if ( row >= static_cast<int>( rowCount( parent ) ) || column >= 1 )
return {};
return Model::index( row, column, parent );
}
Variant data( const ModelIndex& index, ModelRole role = ModelRole::Display ) const override {
if ( role == ModelRole::Display && mData &&
index.row() < static_cast<Int64>( mData->size() ) )
return Variant( ( *mData )[index.row()].label );
return {};
}
private:
const std::vector<UIFontPickerDialog::FontFamilyEntry>* mData{ nullptr };
};
static Uint64 loadFontsTaskTag( const UIFontPickerDialog* dialog ) {
return reinterpret_cast<Uint64>( dialog );
}
@@ -231,7 +257,9 @@ UIFontPickerDialog::UIFontPickerDialog( Uint32 flags ) : UIWindow(), mFlags( fla
mStyleConfig.WinFlags = UI_WIN_DEFAULT_FLAGS | UI_WIN_MAXIMIZE_BUTTON | UI_WIN_MODAL;
updateWinFlags();
mSizes = { 8, 9, 10, 11, 12, 14, 16, 18, 20, 24, 32, 48, 64, 72 };
mSizes.reserve( 67 );
for ( Uint32 size = 6; size <= 72; size++ )
mSizes.emplace_back( size );
mSelection.size = 12;
setTitle( i18n( "font_picker_select_font", "Select Font" ) );
@@ -343,6 +371,11 @@ void UIFontPickerDialog::loadWidgets() {
sizeColumn->setVisible( false )->setEnabled( false );
}
if ( ( mFlags & ShowStyle ) == 0 ) {
if ( auto styleColumn = root->find<UIWidget>( "style_column" ) )
styleColumn->setVisible( false )->setEnabled( false );
}
if ( ( mFlags & ShowEffects ) == 0 ) {
mAntialiasing->setVisible( false )->setEnabled( false );
mUnderline->setVisible( false )->setEnabled( false );
@@ -441,10 +474,13 @@ void UIFontPickerDialog::setFonts( std::vector<FontDesc> fonts ) {
FontDesc selectedFont = mSelection.font;
mergeLoadedFonts( fonts );
for ( const auto& font : mFonts ) {
if ( std::find_if( fonts.begin(), fonts.end(), [&]( const FontDesc& desc ) {
return desc.sameFile( font );
} ) == fonts.end() )
auto found = std::find_if( fonts.begin(), fonts.end(),
[&]( const FontDesc& desc ) { return desc.sameFile( font ); } );
if ( found == fonts.end() ) {
fonts.push_back( font );
} else if ( mExternalFontKeys.find( font.getFileKey() ) != mExternalFontKeys.end() ) {
*found = font;
}
}
mFonts = std::move( fonts );
@@ -536,29 +572,33 @@ bool UIFontPickerDialog::wantsMonospaceOnly() const {
}
void UIFontPickerDialog::updateFamilies() {
std::string previousFamily = mSelection.font.family;
if ( !mFamilyList->getSelection().isEmpty() &&
mFamilyList->getSelection().first().row() < static_cast<Int64>( mFamilies.size() ) )
previousFamily = mFamilies[mFamilyList->getSelection().first().row()];
FontDesc previousFont = mSelection.font;
const std::string query = String::toLower( mSearchInput->getText().toUtf8() );
std::set<std::string> families;
UnorderedSet<std::string> familyKeys;
mFamilies.clear();
for ( const auto& font : mFonts ) {
if ( wantsMonospaceOnly() && !font.monospace )
const bool external =
mExternalFontKeys.find( font.getFileKey() ) != mExternalFontKeys.end();
if ( wantsMonospaceOnly() && !font.monospace && !external )
continue;
if ( !query.empty() && String::toLower( font.family ).find( query ) == std::string::npos )
const std::string label = font.family + ( external ? " [External]" : "" );
if ( !query.empty() && String::toLower( label ).find( query ) == std::string::npos )
continue;
families.insert( font.family );
const std::string familyKey =
font.family + ( external ? "\n" + font.getFileKey() : std::string{} );
if ( familyKeys.insert( familyKey ).second )
mFamilies.push_back(
{ label, font.family, external ? font.getFileKey() : std::string{} } );
}
mFamilies.assign( families.begin(), families.end() );
mFamilyModel = ItemListModel<std::string>::create( mFamilies );
mFamilyModel = std::make_shared<FamilyListModel>( &mFamilies );
mUpdating = true;
mFamilyList->setModel( mFamilyModel );
mUpdating = false;
if ( !previousFamily.empty() )
selectFamily( previousFamily );
if ( !previousFont.family.empty() )
selectFamily( previousFont );
if ( mFamilyList->getSelection().isEmpty() && !mFamilies.empty() )
mFamilyList->setSelection( mFamilyModel->index( 0 ) );
@@ -572,9 +612,15 @@ void UIFontPickerDialog::updateStyles() {
if ( !mFamilyList->getSelection().isEmpty() ) {
const Int64 row = mFamilyList->getSelection().first().row();
if ( row >= 0 && row < static_cast<Int64>( mFamilies.size() ) ) {
const std::string& family = mFamilies[row];
const FontFamilyEntry& family = mFamilies[row];
for ( const auto& font : mFonts ) {
if ( font.family == family && ( !wantsMonospaceOnly() || font.monospace ) ) {
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 &&
( !wantsMonospaceOnly() || font.monospace || external ) ) {
std::string label( styleLabel( font ) );
auto tagIt = mFontTags.find( font.getFileKey() );
if ( tagIt != mFontTags.end() )
@@ -651,8 +697,9 @@ void UIFontPickerDialog::updatePreview() {
}
}
mPreviewText->setFontSize( PixelDensity::dpToPxI( mSelection.size * 2 ) );
mPreviewInput->setFontSize( PixelDensity::dpToPxI( 12 ) );
const Uint32 previewSize = PixelDensity::dpToPxI( mSelection.size );
mPreviewText->setFontSize( previewSize );
mPreviewInput->setFontSize( previewSize );
mPreviewText->setFontStyle( styleFlags( mSelection ) );
mPreviewInput->setFontStyle( styleFlags( mSelection ) );
mPreviewText->setFontColor( mSelection.color );
@@ -668,8 +715,8 @@ void UIFontPickerDialog::updatePreview() {
if ( mSelection.font.faceIndex != 0 )
faceIndexSuffix = " #" + String::toString( mSelection.font.faceIndex );
mDetailsText->setText(
String::format( "%s %s - %u pt - %s%s", mSelection.font.family.c_str(),
styleLabel( mSelection.font ).c_str(), mSelection.size,
String::format( "%s %s - %u dp (%u px) - %s%s", mSelection.font.family.c_str(),
styleLabel( mSelection.font ).c_str(), mSelection.size, previewSize,
mSelection.font.path.c_str(), faceIndexSuffix.c_str() ) );
}
}
@@ -677,6 +724,10 @@ void UIFontPickerDialog::updatePreview() {
void UIFontPickerDialog::clearPreviewFont() {
if ( !mPreviewFont )
return;
if ( SceneManager::isShuttingDown() ) {
mPreviewFont.reset();
return;
}
if ( mPreviewText && mPreviewTextDefaultFont && mPreviewText->getFont() == mPreviewFont.get() )
mPreviewText->setFont( mPreviewTextDefaultFont );
if ( mPreviewInput && mPreviewInputDefaultFont &&
@@ -693,11 +744,15 @@ void UIFontPickerDialog::selectInitialRows() {
mSizeList->setSelection( mSizeModel->index( 4 ) );
}
void UIFontPickerDialog::selectFamily( const std::string& family ) {
if ( family.empty() || !mFamilyModel )
void UIFontPickerDialog::selectFamily( const FontDesc& font ) {
if ( font.family.empty() || !mFamilyModel )
return;
const bool external = mExternalFontKeys.find( font.getFileKey() ) != mExternalFontKeys.end();
for ( size_t i = 0; i < mFamilies.size(); i++ ) {
if ( mFamilies[i] == family ) {
const FontFamilyEntry& family = mFamilies[i];
if ( family.family == font.family &&
( external ? family.externalFontKey == font.getFileKey()
: family.externalFontKey.empty() ) ) {
mFamilyList->setSelection( mFamilyModel->index( i ) );
return;
}
@@ -738,6 +793,13 @@ void UIFontPickerDialog::selectSize( Uint32 size ) {
return;
}
}
auto position = std::lower_bound( mSizes.begin(), mSizes.end(), size );
const size_t index = position - mSizes.begin();
mSizes.insert( position, size );
mSizeModel = ItemListModel<Uint32>::create( mSizes );
mSizeList->setModel( mSizeModel );
mSizeList->setSelection( mSizeModel->index( index ) );
}
void UIFontPickerDialog::emitPicked() {
@@ -808,25 +870,24 @@ bool UIFontPickerDialog::addExternalFont( const std::string& path, Uint32 faceIn
return true;
}
const std::string fontName(
FileSystem::fileRemoveExtension( FileSystem::fileNameFromPath( path ) ) );
if ( !getUISceneNode() )
return false;
ResourceScope& resourceScope = *getUISceneNode()->getResourceScope();
FontTrueTypePtr font = FontTrueType::New( fontName, resourceScope );
if ( !font || !font->loadFromFile( path, faceIndex ) ) {
resourceScope.eraseLocalFont( font.get() );
FontDesc requestedFont;
requestedFont.path = path;
requestedFont.faceIndex = faceIndex;
FontTrueTypePtr font = resourceScope.getFontService().loadSystemFont( requestedFont );
if ( !font )
return false;
}
FontDesc desc;
if ( !font->getFontDesc( desc ) ) {
resourceScope.eraseLocalFont( font.get() );
if ( !font->getFontDesc( desc ) )
return false;
}
resourceScope.eraseLocalFont( font.get() );
if ( desc.family.empty() )
desc.family = FileSystem::fileRemoveExtension( FileSystem::fileNameFromPath( path ) );
mLoadedFontKeys.insert( desc.getFileKey() );
mExternalFontKeys.insert( desc.getFileKey() );
mFonts.push_back( desc );
sortFonts();
updateFontTags();
@@ -867,7 +928,7 @@ void UIFontPickerDialog::setSelectedFont( const FontDesc& desc ) {
mSelection.font = selection;
if ( selection.monospace && mMonospaceOnly )
mMonospaceOnly->setChecked( true );
selectFamily( selection.family );
selectFamily( selection );
updateStyles();
selectStyle( selection );
updateSelectionFromLists( false );
+14 -10
View File
@@ -180,7 +180,7 @@ void UILinearLayout::packVertical() {
Float curY = mPaddingPx.Top;
Float maxX = 0;
Sizei freeSize = getTotalUsedSize();
auto [freeSize, totalWeight] = getTotalUsedSize();
Node* child = mChild;
@@ -193,15 +193,16 @@ void UILinearLayout::packVertical() {
Vector2f pos( mPaddingPx.Left, curY );
if ( widget->getLayoutWeight() != 0 ) {
if ( widget->getLayoutWeight() > 0 && totalWeight > 0 ) {
Float totSize =
( getLayoutHeightPolicy() == SizePolicy::MatchParent ||
getLayoutHeightPolicy() == SizePolicy::Fixed )
? getPixelsSize().getHeight() - mPaddingPx.Top - mPaddingPx.Bottom
: getParent()->getPixelsSize().getHeight() - mLayoutMarginPx.Bottom -
mLayoutMarginPx.Top - mPaddingPx.Top - mPaddingPx.Bottom;
Float newSize = eemax(
eeceil( totSize - freeSize.getHeight() ) * widget->getLayoutWeight(), 0.f );
Float newSize = eemax( eeceil( totSize - freeSize.getHeight() ) *
widget->getLayoutWeight() / totalWeight,
0.f );
widget->setPixelsSize( widget->getPixelsSize().getWidth(), newSize );
}
@@ -315,7 +316,7 @@ void UILinearLayout::packHorizontal() {
Float curX = mPaddingPx.Left;
Float maxY = 0;
Sizei freeSize = getTotalUsedSize();
auto [freeSize, totalWeight] = getTotalUsedSize();
Node* child = mChild;
@@ -328,15 +329,16 @@ void UILinearLayout::packHorizontal() {
Vector2f pos( curX, mPaddingPx.Top );
if ( widget->getLayoutWeight() != 0 ) {
if ( widget->getLayoutWeight() > 0 && totalWeight > 0 ) {
Float totSize =
( getLayoutWidthPolicy() == SizePolicy::MatchParent ||
getLayoutWidthPolicy() == SizePolicy::Fixed )
? getPixelsSize().getWidth() - mPaddingPx.Left - mPaddingPx.Right
: getParent()->getPixelsSize().getWidth() - mLayoutMarginPx.Right -
mLayoutMarginPx.Left - mPaddingPx.Left - mPaddingPx.Right;
Float newSize = eemax(
eeceil( totSize - freeSize.getWidth() ) * widget->getLayoutWeight(), 0.f );
Float newSize = eemax( eeceil( totSize - freeSize.getWidth() ) *
widget->getLayoutWeight() / totalWeight,
0.f );
widget->setPixelsSize( newSize, widget->getPixelsSize().getHeight() );
}
@@ -412,13 +414,15 @@ void UILinearLayout::packHorizontal() {
mPacking = false;
}
Sizei UILinearLayout::getTotalUsedSize() {
std::pair<Sizei, Float> UILinearLayout::getTotalUsedSize() {
Node* child = mChild;
Sizei size( 0, 0 );
Float totalWeight = 0;
while ( NULL != child ) {
if ( child->isWidget() && child->isVisible() ) {
UIWidget* widget = static_cast<UIWidget*>( child );
totalWeight += eemax( widget->getLayoutWeight(), 0.f );
Rectf margin = widget->getLayoutPixelsMargin();
size.x += margin.Left + margin.Right;
@@ -443,7 +447,7 @@ Sizei UILinearLayout::getTotalUsedSize() {
child = child->getNextNode();
}
return size;
return { size, totalWeight };
}
std::string UILinearLayout::getPropertyString( const PropertyDefinition* propertyDef,
@@ -2,6 +2,7 @@
#include <algorithm>
#include <eepp/graphics/fonttruetype.hpp>
#include <eepp/graphics/globalbatchrenderer.hpp>
#include <eepp/graphics/pixeldensity.hpp>
#include <eepp/graphics/systemfontresolver.hpp>
#include <eepp/graphics/texturefactory.hpp>
#include <eepp/system/filesystem.hpp>
@@ -9,9 +10,12 @@
#include <eepp/ui/abstract/uiabstractview.hpp>
#include <eepp/ui/tools/uifontpickerdialog.hpp>
#include <eepp/ui/uiapplication.hpp>
#include <eepp/ui/uilinearlayout.hpp>
#include <eepp/ui/uilistview.hpp>
#include <eepp/ui/uipushbutton.hpp>
#include <eepp/ui/uiscenenode.hpp>
#include <eepp/ui/uitextinput.hpp>
#include <eepp/ui/uitextview.hpp>
using namespace EE;
using namespace EE::UI;
@@ -51,12 +55,19 @@ template <typename Predicate> static void pumpUntil( UISceneNode* sceneNode, Pre
class TestFontPickerDialog : public UIFontPickerDialog {
public:
static TestFontPickerDialog* New() { return eeNew( TestFontPickerDialog, () ); }
static TestFontPickerDialog* New( Uint32 flags = DefaultFlags ) {
return eeNew( TestFontPickerDialog, ( flags ) );
}
FontTrueTypeWeakPtr getPreviewFontHandle() const { return mPreviewFont; }
void selectCustomSize( Uint32 size ) { selectSize( size ); }
void releasePreviewFont() { clearPreviewFont(); }
Uint32 getPreviewTextSize() const { return mPreviewText->getFontSize(); }
Uint32 getPreviewInputSize() const { return mPreviewInput->getFontSize(); }
String getDetails() const { return mDetailsText->getText(); }
protected:
TestFontPickerDialog() : UIFontPickerDialog() {}
TestFontPickerDialog( Uint32 flags ) : UIFontPickerDialog( flags ) {}
};
UTEST( UIFontPickerDialog, PreselectsExternalFontPath ) {
@@ -74,6 +85,8 @@ UTEST( UIFontPickerDialog, PreselectsExternalFontPath ) {
EXPECT_FALSE( dialog->getSelection().font.family.empty() );
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 );
UIFontPickerDialog* selectionDialog = UIFontPickerDialog::New();
UIFontSelection selection;
@@ -84,6 +97,58 @@ UTEST( UIFontPickerDialog, PreselectsExternalFontPath ) {
EXPECT_FALSE( selectionDialog->getSelection().font.family.empty() );
}
UTEST( UIFontPickerDialog, ExternalFontKeepsSeparateFamilyEntryOnNameCollision ) {
std::vector<FontDesc> fonts = SystemFontResolver::instance()->enumerate();
auto fontIt = std::find_if( fonts.begin(), fonts.end(), []( const FontDesc& font ) {
return !font.family.empty() && !font.path.empty() && FileSystem::fileExists( font.path );
} );
if ( fontIt == fonts.end() )
UTEST_SKIP( "no system font available" );
const std::string externalPath = Sys::getTempPath() + "UIFontPickerDialogExternal-" +
String::toString( Sys::getProcessID() ) + "." +
FileSystem::fileExtension( fontIt->path );
ASSERT_TRUE( FileSystem::fileCopy( fontIt->path, externalPath ) );
UIApplication app(
WindowSettings( 320, 240, "eepp - UIFontPickerDialog Test", WindowStyle::Default,
WindowBackend::Default, 32 ),
UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 1 ) );
ResourceScope& resourceScope = *app.getUI()->getResourceScope();
const std::string externalName(
FileSystem::fileRemoveExtension( FileSystem::fileNameFromPath( externalPath ) ) );
const std::string managedFontPath =
Sys::getProcessPath() + "assets/fonts/NotoSansKR-Regular.ttf";
FontTrueTypePtr managedFont = FontTrueType::New( externalName, managedFontPath, resourceScope );
ASSERT_TRUE( managedFont && managedFont->loaded() );
TestFontPickerDialog* dialog = TestFontPickerDialog::New();
dialog->setSelectedFont( externalPath );
pumpUntil( app.getUI(), [dialog] { return dialog->getButtonOK()->isEnabled(); } );
EXPECT_STDSTREQ( externalPath, dialog->getSelection().font.path );
EXPECT_FALSE( dialog->getFamilyList()->getSelection().isEmpty() );
EXPECT_STDSTREQ( fontIt->family + " [External]",
dialog->getFamilyList()->getSelection().first().data().toString() );
bool foundSystemFamily = false;
bool foundExternalFamily = false;
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 == fontIt->family;
foundExternalFamily |= label == fontIt->family + " [External]";
}
EXPECT_TRUE( foundSystemFamily );
EXPECT_TRUE( foundExternalFamily );
EXPECT_EQ( managedFont.get(), resourceScope.findFont( externalName ).get() );
dialog->releasePreviewFont();
FileSystem::fileRemove( externalPath );
}
UTEST( UIFontPickerDialog, PreviewFontsDoNotPopulateSceneFontCatalog ) {
UIApplication app(
WindowSettings( 320, 240, "eepp - UIFontPickerDialog Test", WindowStyle::Default,
@@ -130,6 +195,18 @@ UTEST( UIFontPickerDialog, SelectingFontDoesNotCreateMetricOnlyPage ) {
EXPECT_TRUE( foundPreviewPage );
}
UTEST( UIFontPickerDialog, TeardownWithActivePreviewDoesNotInvalidateDestroyedLayouts ) {
UIApplication app(
WindowSettings( 320, 240, "eepp - UIFontPickerDialog Test", WindowStyle::Default,
WindowBackend::Default, 32 ),
UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 1 ) );
UILinearLayout::New()->setParent( app.getUI()->getRoot() );
const std::string fontPath = Sys::getProcessPath() + "assets/fonts/NotoSansKR-Regular.ttf";
ASSERT_TRUE( FileSystem::fileExists( fontPath ) );
UIFontPickerDialog::New()->setSelectedFont( fontPath );
}
UTEST( UIFontPickerDialog, ReleasesPreviewFontTexturesOnClose ) {
UIApplication app(
WindowSettings( 320, 240, "eepp - UIFontPickerDialog Test", WindowStyle::Default,
@@ -238,7 +315,7 @@ UTEST( UIFontPickerDialog, AsyncLoadPreservesExternalFontPreselection ) {
const std::string fontPath = Sys::getProcessPath() + "assets/fonts/NotoSansKR-Regular.ttf";
ASSERT_TRUE( FileSystem::fileExists( fontPath ) );
UIFontPickerDialog* dialog = UIFontPickerDialog::New();
UIFontPickerDialog* dialog = UIFontPickerDialog::New( UIFontPickerDialog::MonospaceOnly );
EXPECT_FALSE( dialog->getButtonOK()->isEnabled() );
dialog->setSelectedFont( fontPath );
@@ -249,6 +326,8 @@ UTEST( UIFontPickerDialog, AsyncLoadPreservesExternalFontPreselection ) {
EXPECT_FALSE( dialog->getSelection().font.family.empty() );
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 );
}
UTEST( UIFontPickerDialog, DefaultColorComesFromTheme ) {
@@ -360,3 +439,23 @@ UTEST( UIFontPickerDialog, ApplyButtonEmitsOnApply ) {
EXPECT_TRUE( picked );
EXPECT_FALSE( confirmed );
}
UTEST( UIFontPickerDialog, HidesStyleAndSupportsEveryIntegerSize ) {
UIApplication app(
WindowSettings( 320, 240, "eepp - UIFontPickerDialog Test", WindowStyle::Default,
WindowBackend::Default, 32 ),
UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 1 ) );
TestFontPickerDialog* dialog = TestFontPickerDialog::New( UIFontPickerDialog::ShowSize );
dialog->selectCustomSize( 15 );
EXPECT_FALSE( dialog->getStyleList()->getParent()->isVisible() );
EXPECT_EQ( 67u, dialog->getSizeList()->getModel()->rowCount() );
EXPECT_EQ( 15u, dialog->getSelection().size );
EXPECT_FALSE( dialog->getSizeList()->getSelection().isEmpty() );
EXPECT_EQ( 15u, dialog->getSizeList()->getSelection().first().data().asUint() );
EXPECT_EQ( static_cast<Uint32>( PixelDensity::dpToPxI( 15 ) ), dialog->getPreviewTextSize() );
EXPECT_EQ( dialog->getPreviewTextSize(), dialog->getPreviewInputSize() );
EXPECT_TRUE( dialog->getDetails().contains( "15 dp" ) );
dialog->releasePreviewFont();
}
+62
View File
@@ -80,3 +80,65 @@ UTEST( UILinearLayout, CrossAxisAlignmentUsesOwnPadding ) {
EXPECT_NEAR( 40.f, middle->getPixelsPosition().y, 0.1f );
EXPECT_NEAR( 60.f, bottom->getPixelsPosition().y, 0.1f );
}
UTEST( UILinearLayout, HorizontalWeightsNormalizeAcrossVisibleChildren ) {
UIApplication app(
WindowSettings( 320, 240, "eepp - UILinearLayout Test", WindowStyle::Default,
WindowBackend::Default, 32 ),
UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 1 ) );
UILinearLayout* layout = UILinearLayout::NewHorizontal();
layout->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::Fixed );
layout->setPixelsSize( 300, 100 );
layout->setParent( app.getUI()->getRoot() );
const auto addChild = [layout]( Float weight ) {
UIWidget* child = UIWidget::New();
child->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::Fixed );
child->setPixelsSize( 0, 20 );
child->setLayoutWeight( weight );
child->setParent( layout );
return child;
};
UIWidget* first = addChild( 1 );
UIWidget* middle = addChild( 2 );
UIWidget* last = addChild( 1 );
app.getUI()->updateDirtyLayouts();
EXPECT_NEAR( 75.f, first->getPixelsSize().getWidth(), 0.1f );
EXPECT_NEAR( 150.f, middle->getPixelsSize().getWidth(), 0.1f );
EXPECT_NEAR( 75.f, last->getPixelsSize().getWidth(), 0.1f );
middle->setVisible( false );
app.getUI()->updateDirtyLayouts();
EXPECT_NEAR( 150.f, first->getPixelsSize().getWidth(), 0.1f );
EXPECT_NEAR( 150.f, last->getPixelsSize().getWidth(), 0.1f );
}
UTEST( UILinearLayout, VerticalWeightsNormalize ) {
UIApplication app(
WindowSettings( 320, 240, "eepp - UILinearLayout Test", WindowStyle::Default,
WindowBackend::Default, 32 ),
UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 1 ) );
UILinearLayout* layout = UILinearLayout::NewVertical();
layout->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::Fixed );
layout->setPixelsSize( 100, 200 );
layout->setParent( app.getUI()->getRoot() );
UIWidget* first = UIWidget::New();
first->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::Fixed );
first->setPixelsSize( 20, 0 );
first->setLayoutWeight( 1 );
first->setParent( layout );
UIWidget* second = UIWidget::New();
second->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::Fixed );
second->setPixelsSize( 20, 0 );
second->setLayoutWeight( 3 );
second->setParent( layout );
app.getUI()->updateDirtyLayouts();
EXPECT_NEAR( 50.f, first->getPixelsSize().getHeight(), 0.1f );
EXPECT_NEAR( 150.f, second->getPixelsSize().getHeight(), 0.1f );
}
+2 -2
View File
@@ -442,9 +442,9 @@ void App::openFolderDialog() {
}
void App::openFontDialog( std::string& fontPath, bool loadingMonoFont, bool terminalFont,
std::function<void()> onFinish ) {
std::function<void()> onFinish, bool pickFontSize ) {
mFontPickerController->openFontDialog( fontPath, loadingMonoFont, terminalFont,
std::move( onFinish ) );
std::move( onFinish ), pickFontSize );
}
void App::updateInputFonts() {
+11 -8
View File
@@ -80,7 +80,7 @@ class App : public UICodeEditorSplitter::Client, public PluginContextProvider {
void openFolderDialog();
void openFontDialog( std::string& fontPath, bool loadingMonoFont, bool terminalFont = false,
std::function<void()> onFinish = {} );
std::function<void()> onFinish = {}, bool pickFontSize = true );
void updateInputFonts();
@@ -356,13 +356,16 @@ class App : public UICodeEditorSplitter::Client, public PluginContextProvider {
t.setCommand( "terminal-font",
[this] { openFontDialog( mConfig.ui.terminalFont, true, true ); } );
t.setCommand( "fallback-font", [this] {
openFontDialog( mConfig.ui.fallbackFont, false, false, [this] {
UIMessageBox::New( UIMessageBox::OK,
i18n( "new_fallback_font_requires_restart",
"New fallback font has been set. Application must be "
"restarted in order to see the changes." ) )
->showWhenReady();
} );
openFontDialog(
mConfig.ui.fallbackFont, false, false,
[this] {
UIMessageBox::New( UIMessageBox::OK,
i18n( "new_fallback_font_requires_restart",
"New fallback font has been set. Application must be "
"restarted in order to see the changes." ) )
->showWhenReady();
},
false );
} );
t.setCommand( "tree-view-configure-ignore-files",
[this] { treeViewConfigureIgnoreFiles(); } );
+29 -7
View File
@@ -1,5 +1,6 @@
#include "fontpickercontroller.hpp"
#include "ecode.hpp"
#include "settingsactions.hpp"
#include <eepp/graphics/fontfamily.hpp>
#include <eepp/scene/scenemanager.hpp>
#include <eepp/ui/tools/uifontpickerdialog.hpp>
@@ -18,7 +19,8 @@ struct MonospaceFontPreview {
};
void FontPickerController::openFontDialog( std::string& fontPath, bool loadingMonoFont,
bool terminalFont, std::function<void()> onFinish ) {
bool terminalFont, std::function<void()> onFinish,
bool pickFontSize ) {
std::string absoluteFontPath( fontPath );
if ( FileSystem::isRelativePath( absoluteFontPath ) )
absoluteFontPath = mApp->resPath() + fontPath;
@@ -103,19 +105,20 @@ void FontPickerController::openFontDialog( std::string& fontPath, bool loadingMo
defaultResourceScope().publishLocalFont( std::move( fontName ), font );
};
const Uint32 flags = UIFontPickerDialog::DefaultFlags |
const Uint32 flags = ( pickFontSize ? UIFontPickerDialog::ShowSize : 0 ) |
( loadingMonoFont ? UIFontPickerDialog::MonospaceOnly : 0 );
UIFontPickerDialog* dialog = UIFontPickerDialog::New( flags );
dialog->setTitle( mApp->i18n( "select_font", "Select Font" ) );
dialog->setCloseShortcut( KEY_ESCAPE );
dialog->on( Event::OnWindowClose, [this, preview, applyMonospaceFont]( const Event* ) {
if ( !App::instance() || SceneManager::isShuttingDown() )
return;
if ( preview && !preview->confirmed )
applyMonospaceFont( preview->originalFont, false );
if ( App::instance() && mApp->getSplitter() && mApp->getSplitter()->getCurWidget() &&
!SceneManager::instance()->isShuttingDown() ) {
if ( mApp->getSplitter() && mApp->getSplitter()->getCurWidget() )
mApp->getSplitter()->getCurWidget()->setFocus();
}
} );
if ( loadingMonoFont ) {
dialog->setOnFontSelectionChanged(
@@ -138,9 +141,18 @@ void FontPickerController::openFontDialog( std::string& fontPath, bool loadingMo
}
} );
}
dialog->setOnFontPicked( [&fontPath, loadingMonoFont, onFinish, preview, normalizedFontPath,
loadPreviewFont, publishPreviewFont,
dialog->setOnFontPicked( [this, &fontPath, loadingMonoFont, terminalFont, onFinish, preview,
normalizedFontPath, pickFontSize, loadPreviewFont, publishPreviewFont,
applyMonospaceFont]( const UIFontSelection& selection ) {
if ( pickFontSize ) {
const StyleSheetLength size( selection.size, StyleSheetLength::Dp );
if ( terminalFont )
mApp->getSettingsActions()->setTerminalFontSize( size );
else if ( loadingMonoFont )
mApp->getSettingsActions()->setEditorFontSize( size );
else
mApp->getSettingsActions()->setUIFontSize( size );
}
auto newPath = normalizedFontPath( selection.font.path );
if ( newPath.empty() )
return;
@@ -168,6 +180,16 @@ void FontPickerController::openFontDialog( std::string& fontPath, bool loadingMo
applyMonospaceFont( preview->originalFont, false );
}
} );
if ( pickFontSize ) {
UIFontSelection selection = dialog->getSelection();
const Float currentSize =
terminalFont ? mApp->getConfig().term.fontSize.asDp( 0, Sizef(), mApp->getDisplayDPI() )
: loadingMonoFont
? mApp->getConfig().editor.fontSize.asDp( 0, Sizef(), mApp->getDisplayDPI() )
: mApp->getConfig().ui.fontSize.asDp( 0, Sizef(), mApp->getDisplayDPI() );
selection.size = static_cast<Uint32>( currentSize );
dialog->setSelection( selection );
}
dialog->setSelectedFont( absoluteFontPath );
dialog->center();
dialog->show();
+1 -1
View File
@@ -13,7 +13,7 @@ class FontPickerController {
explicit FontPickerController( App* app ) : mApp( app ) {}
void openFontDialog( std::string& fontPath, bool loadingMonoFont, bool terminalFont = false,
std::function<void()> onFinish = {} );
std::function<void()> onFinish = {}, bool pickFontSize = true );
private:
App* mApp{ nullptr };
+37 -27
View File
@@ -316,15 +316,18 @@ void SettingsActions::setEditorFontSize() {
msgBox->setCloseShortcut( { KEY_ESCAPE, 0 } );
msgBox->showWhenReady();
msgBox->on( Event::OnConfirm, [this, msgBox]( const Event* ) {
mApp->getConfig().editor.fontSize = StyleSheetLength( msgBox->getTextInput()->getText() );
mApp->getSplitter()->forEachEditor( [this]( UICodeEditor* editor ) {
editor->setFontSize(
mApp->getConfig().editor.fontSize.asPixels( 0, Sizef(), mApp->getDisplayDPI() ) );
} );
setEditorFontSize( StyleSheetLength( msgBox->getTextInput()->getText() ) );
} );
mApp->setFocusEditorOnClose( msgBox );
}
void SettingsActions::setEditorFontSize( const StyleSheetLength& size ) {
mApp->getConfig().editor.fontSize = size;
const Float fontSize = size.asPixels( 0, Sizef(), mApp->getDisplayDPI() );
mApp->getSplitter()->forEachEditor(
[fontSize]( UICodeEditor* editor ) { editor->setFontSize( fontSize ); } );
}
void SettingsActions::setTerminalFontSize() {
UIMessageBox* msgBox = UIMessageBox::New(
UIMessageBox::INPUT, i18n( "set_terminal_font_size", "Set the terminal font size:" ) );
@@ -333,16 +336,20 @@ void SettingsActions::setTerminalFontSize() {
msgBox->setCloseShortcut( { KEY_ESCAPE, 0 } );
msgBox->showWhenReady();
msgBox->on( Event::OnConfirm, [this, msgBox]( const Event* ) {
mApp->getConfig().term.fontSize = StyleSheetLength( msgBox->getTextInput()->getText() );
mApp->getSplitter()->forEachWidget( [this]( UIWidget* widget ) {
if ( widget && widget->isType( UI_TYPE_TERMINAL ) )
widget->asType<UITerminal>()->setFontSize(
mApp->getConfig().term.fontSize.asPixels( 0, Sizef(), mApp->getDisplayDPI() ) );
} );
setTerminalFontSize( StyleSheetLength( msgBox->getTextInput()->getText() ) );
} );
mApp->setFocusEditorOnClose( msgBox );
}
void SettingsActions::setTerminalFontSize( const StyleSheetLength& size ) {
mApp->getConfig().term.fontSize = size;
const Float fontSize = size.asPixels( 0, Sizef(), mApp->getDisplayDPI() );
mApp->getSplitter()->forEachWidget( [fontSize]( UIWidget* widget ) {
if ( widget && widget->isType( UI_TYPE_TERMINAL ) )
widget->asType<UITerminal>()->setFontSize( fontSize );
} );
}
void SettingsActions::setUIFontSize() {
UIMessageBox* msgBox = UIMessageBox::New( UIMessageBox::INPUT,
i18n( "set_ui_font_size", "Set the UI font size:" ) );
@@ -351,27 +358,30 @@ void SettingsActions::setUIFontSize() {
msgBox->setCloseShortcut( { KEY_ESCAPE, 0 } );
msgBox->showWhenReady();
msgBox->on( Event::OnConfirm, [this, msgBox]( const Event* ) {
mApp->getConfig().ui.fontSize = StyleSheetLength( msgBox->getTextInput()->getText() );
Float fontSize =
mApp->getConfig().ui.fontSize.asPixels( 0, Sizef(), mApp->getDisplayDPI() );
UIThemeManager* manager = mApp->getUISceneNode()->getUIThemeManager();
manager->setDefaultFontSize( fontSize );
manager->getDefaultTheme()->setDefaultFontSize( fontSize );
mApp->getUISceneNode()->forEachNode( [this]( Node* node ) {
if ( node->isType( UI_TYPE_TEXTVIEW ) ) {
UITextView* textView = node->asType<UITextView>();
if ( !textView->getUIStyle()->hasProperty( PropertyId::FontSize ) ) {
textView->setFontSize( mApp->getConfig().ui.fontSize.asPixels(
node->getParent()->getPixelsSize().getWidth(), Sizef(),
mApp->getUISceneNode()->getDPI() ) );
}
}
} );
setUIFontSize( StyleSheetLength( msgBox->getTextInput()->getText() ) );
msgBox->closeWindow();
} );
mApp->setFocusEditorOnClose( msgBox );
}
void SettingsActions::setUIFontSize( const StyleSheetLength& size ) {
mApp->getConfig().ui.fontSize = size;
const Float fontSize = size.asPixels( 0, Sizef(), mApp->getDisplayDPI() );
UIThemeManager* manager = mApp->getUISceneNode()->getUIThemeManager();
manager->setDefaultFontSize( fontSize );
manager->getDefaultTheme()->setDefaultFontSize( fontSize );
mApp->getUISceneNode()->forEachNode( [this]( Node* node ) {
if ( node->isType( UI_TYPE_TEXTVIEW ) ) {
UITextView* textView = node->asType<UITextView>();
if ( !textView->getUIStyle()->hasProperty( PropertyId::FontSize ) ) {
textView->setFontSize( mApp->getConfig().ui.fontSize.asPixels(
node->getParent()->getPixelsSize().getWidth(), Sizef(),
mApp->getUISceneNode()->getDPI() ) );
}
}
} );
}
void SettingsActions::setUIPanelFontSize() {
UIMessageBox* msgBox = UIMessageBox::New(
UIMessageBox::INPUT, i18n( "set_side_panel_font_size", "Set side panel font size:" ) );
+3 -1
View File
@@ -30,10 +30,13 @@ class SettingsActions {
void setUIScaleFactor();
void setUIFontSize();
void setUIFontSize( const StyleSheetLength& size );
void setEditorFontSize();
void setEditorFontSize( const StyleSheetLength& size );
void setTerminalFontSize();
void setTerminalFontSize( const StyleSheetLength& size );
void setUIPanelFontSize();
@@ -43,7 +46,6 @@ class SettingsActions {
String i18n( const std::string& key, const String& def );
void checkForUpdatesResponse( Http::Response&& response, bool fromStartup );
};
} // namespace ecode
+6 -9
View File
@@ -3300,21 +3300,18 @@ UIMenu* SettingsMenu::createFontsMenu() {
createFontHintMenu() );
mFontsMenu->addSubMenu( i18n( "ui_font_antialiasing", "Font Anti-Aliasing" ),
findIcon( "font-size" ), createFontAntiAliasingMenu() );
mFontsMenu->add( i18n( "ui_font_size", "UI Font Size" ), findIcon( "font-size" ) )
->setId( "ui-font-size" );
mFontsMenu->add( i18n( "ui_panel_font_size", "UI Panel Font Size" ), findIcon( "font-size" ) )
->setId( "ui-panel-font-size" );
mFontsMenu->add( i18n( "editor_font_size", "Editor Font Size" ), findIcon( "font-size" ) )
->setId( "editor-font-size" );
mFontsMenu->add( i18n( "terminal_font_size", "Terminal Font Size" ), findIcon( "font-size" ) )
->setId( "terminal-font-size" );
mFontsMenu
->add( i18n( "sans-serif_font_ellipsis", "Sans-Serif Font..." ), findIcon( "font-size" ) )
->add( i18n( "ui_font_and_size_ellipsis", "UI Font & Size..." ), findIcon( "font-size" ) )
->setId( "sans-serif-font" );
mFontsMenu
->add( i18n( "monospace_font_ellipsis", "Monospace Font..." ), findIcon( "font-size" ) )
->add( i18n( "editor_font_and_size_ellipsis", "Editor Font & Size..." ),
findIcon( "font-size" ) )
->setId( "monospace-font" );
mFontsMenu->add( i18n( "terminal_font_ellipsis", "Terminal Font..." ), findIcon( "font-size" ) )
mFontsMenu
->add( i18n( "terminal_font_and_size_ellipsis", "Terminal Font & Size..." ),
findIcon( "font-size" ) )
->setId( "terminal-font" );
mFontsMenu->add( i18n( "fallback_font_ellipsis", "Fallback Font..." ), findIcon( "font-size" ) )
->setId( "fallback-font" );