File indexing completed on 2025-02-02 04:11:28
0001 /* 0002 * SPDX-FileCopyrightText: 2019-2023 Mattia Basaglia <dev@dragon.best> 0003 * 0004 * SPDX-License-Identifier: GPL-3.0-or-later 0005 */ 0006 0007 #include "font_model.hpp" 0008 0009 #include <set> 0010 0011 #include <QIcon> 0012 0013 #include "model/assets/assets.hpp" 0014 0015 class glaxnimate::gui::font::FontModel::Private 0016 { 0017 public: 0018 QString family(int index) 0019 { 0020 if ( index < 0 ) 0021 return ""; 0022 0023 if ( index < filtered_faves.size() ) 0024 return filtered_faves[index]; 0025 0026 index -= filtered_faves.size(); 0027 if ( index < fonts.size() ) 0028 return fonts[index]; 0029 0030 return ""; 0031 } 0032 0033 bool valid(const QString& family) const 0034 { 0035 constexpr const int scalable_mask = (ScalableFonts | NonScalableFonts); 0036 constexpr const int spacing_mask = (ProportionalFonts | MonospacedFonts); 0037 0038 if ( (filters & scalable_mask) && (filters & scalable_mask) != scalable_mask ) 0039 if ( bool(filters & ScalableFonts) != database.isSmoothlyScalable(family) ) 0040 return false; 0041 0042 if ( (filters & spacing_mask) && (filters & spacing_mask) != spacing_mask ) 0043 if ( bool(filters & MonospacedFonts) != database.isFixedPitch(family) ) 0044 return false; 0045 0046 return true; 0047 } 0048 0049 QStringList rebuild_faves() 0050 { 0051 QStringList filtered_faves; 0052 0053 for ( const auto& fam : faves ) 0054 { 0055 if ( valid(fam) ) 0056 filtered_faves.push_back(fam); 0057 } 0058 0059 return filtered_faves; 0060 } 0061 0062 void rebuild() 0063 { 0064 filtered_faves = rebuild_faves(); 0065 0066 fonts.clear(); 0067 0068 std::set<QString> font_set; 0069 for ( const auto& fam : database.families(system) ) 0070 { 0071 if ( valid(fam) ) 0072 font_set.insert(fam); 0073 } 0074 0075 if ( document ) 0076 { 0077 for ( const auto& font : document->assets()->fonts->values ) 0078 font_set.insert(font->family()); 0079 0080 } 0081 0082 fonts.reserve(font_set.size()); 0083 for ( const auto& family : font_set ) 0084 fonts.push_back(family); 0085 } 0086 0087 void maybe_remove_family(const QString& family, FontModel* parent) 0088 { 0089 if ( database.families(system).contains(family) ) 0090 return; 0091 0092 int row = fonts.indexOf(family); 0093 if ( row == -1 ) 0094 return; 0095 0096 parent->beginRemoveRows({}, row, row); 0097 0098 fonts.erase(fonts.begin() + row); 0099 0100 auto it = std::find(filtered_faves.begin(), filtered_faves.end(), family); 0101 if ( it != filtered_faves.end() ) 0102 filtered_faves.erase(it); 0103 0104 parent->endRemoveRows(); 0105 } 0106 0107 void maybe_add_family(const QString& family, FontModel* parent) 0108 { 0109 if ( fonts.contains(family) ) 0110 return; 0111 0112 auto iter = std::upper_bound(fonts.begin(), fonts.end(), family); 0113 int row = iter - fonts.begin(); 0114 parent->beginInsertRows({}, row, row); 0115 fonts.insert(iter, family); 0116 parent->endInsertRows(); 0117 } 0118 0119 QFontDatabase database; 0120 QFontDatabase::WritingSystem system = QFontDatabase::Any; 0121 FontFilters filters = AllFonts; 0122 bool preview_font = true; 0123 0124 std::set<QString> faves; 0125 QStringList filtered_faves; 0126 QStringList fonts; 0127 model::Document* document = nullptr; 0128 }; 0129 0130 glaxnimate::gui::font::FontModel::FontModel(QObject* parent) 0131 : QAbstractListModel(parent), d(std::make_unique<Private>()) 0132 { 0133 d->rebuild(); 0134 } 0135 0136 glaxnimate::gui::font::FontModel::~FontModel() 0137 { 0138 } 0139 0140 void glaxnimate::gui::font::FontModel::set_document(model::Document* document) 0141 { 0142 if ( d->document == document ) 0143 return; 0144 0145 if ( d->document ) 0146 { 0147 auto fonts = document->assets()->fonts.get(); 0148 0149 disconnect(fonts, nullptr, this, nullptr); 0150 0151 for ( const auto& font : fonts->values ) 0152 d->maybe_remove_family(font->family(), this); 0153 } 0154 0155 d->document = document; 0156 0157 if ( d->document ) 0158 { 0159 auto fonts = document->assets()->fonts.get(); 0160 0161 for ( const auto& font : fonts->values ) 0162 d->maybe_add_family(font->family(), this); 0163 0164 0165 connect(fonts, &model::FontList::font_added, this, [this](model::EmbeddedFont* font){ 0166 if ( font->database_index() != -1 ) 0167 d->maybe_add_family(font->family(), this); 0168 }); 0169 0170 connect(fonts, &model::DocumentNode::docnode_child_remove_end, this, [this](model::DocumentNode* node){ 0171 auto font = static_cast<model::EmbeddedFont*>(node); 0172 d->maybe_remove_family(font->family(), this); 0173 }); 0174 } 0175 } 0176 0177 0178 QVariant glaxnimate::gui::font::FontModel::data(const QModelIndex& index, int role) const 0179 { 0180 QString family = d->family(index.row()); 0181 if ( family.isEmpty() ) 0182 return {}; 0183 0184 if ( index.column() == 1 ) 0185 { 0186 if ( role == Qt::DecorationRole ) 0187 { 0188 if ( d->faves.count(family) ) 0189 return QIcon::fromTheme("starred-symbolic"); 0190 else 0191 return QIcon::fromTheme("non-starred-symbolic"); 0192 } 0193 return {}; 0194 } 0195 0196 switch ( role ) 0197 { 0198 case Qt::ToolTipRole: 0199 case Qt::WhatsThisRole: 0200 case Qt::DisplayRole: 0201 case Qt::EditRole: 0202 return family; 0203 case Qt::FontRole: 0204 // if ( d->preview_font ) 0205 // return QFont(family); 0206 return {}; 0207 case Qt::DecorationRole: 0208 if ( index.row() < d->filtered_faves.size() ) 0209 return QIcon::fromTheme("favorite"); 0210 // return QIcon::fromTheme("font-ttf"); 0211 } 0212 0213 return {}; 0214 } 0215 0216 int glaxnimate::gui::font::FontModel::columnCount(const QModelIndex& parent) const 0217 { 0218 if ( parent.isValid() ) 0219 return 0; 0220 return 2; 0221 } 0222 0223 QStringList glaxnimate::gui::font::FontModel::favourites() const 0224 { 0225 #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) 0226 return QStringList(d->faves.begin(), d->faves.end()); 0227 #else 0228 QStringList l; 0229 for ( const auto& f : d->faves ) 0230 l.push_back(f); 0231 return l; 0232 #endif 0233 } 0234 0235 void glaxnimate::gui::font::FontModel::set_favourites(const QStringList& faves) 0236 { 0237 beginRemoveRows({}, 0, d->filtered_faves.size()); 0238 d->faves.clear(); 0239 d->faves.insert(faves.begin(), faves.end()); 0240 d->filtered_faves.clear(); 0241 endRemoveRows(); 0242 auto new_faves = d->rebuild_faves(); 0243 beginInsertRows({}, 0, new_faves.size()); 0244 d->filtered_faves = new_faves; 0245 endInsertRows(); 0246 } 0247 0248 glaxnimate::gui::font::FontModel::FontFilters glaxnimate::gui::font::FontModel::font_filters() const 0249 { 0250 return d->filters; 0251 } 0252 0253 void glaxnimate::gui::font::FontModel::set_font_filters(glaxnimate::gui::font::FontModel::FontFilters filters) 0254 { 0255 d->filters = filters; 0256 reset(); 0257 } 0258 0259 void glaxnimate::gui::font::FontModel::reset() 0260 { 0261 beginResetModel(); 0262 d->rebuild(); 0263 endResetModel(); 0264 } 0265 0266 QModelIndex glaxnimate::gui::font::FontModel::index_for_font(const QString& family) 0267 { 0268 int index = d->fonts.indexOf(family); 0269 if ( index == -1 ) 0270 return {}; 0271 return createIndex(index + d->filtered_faves.size(), 0, nullptr); 0272 } 0273 0274 bool glaxnimate::gui::font::FontModel::preview_font() const 0275 { 0276 return d->preview_font; 0277 } 0278 0279 void glaxnimate::gui::font::FontModel::set_preview_font(bool preview) 0280 { 0281 d->preview_font = preview; 0282 Q_EMIT dataChanged( 0283 createIndex(0, 0, nullptr), 0284 createIndex(d->fonts.size() + d->filtered_faves.size(), 0, nullptr), 0285 {Qt::FontRole} 0286 ); 0287 } 0288 0289 QFontDatabase::WritingSystem glaxnimate::gui::font::FontModel::writing_system() const 0290 { 0291 return d->system; 0292 } 0293 0294 void glaxnimate::gui::font::FontModel::set_writing_system(QFontDatabase::WritingSystem system) 0295 { 0296 d->system = system; 0297 reset(); 0298 } 0299 0300 int glaxnimate::gui::font::FontModel::rowCount(const QModelIndex& parent) const 0301 { 0302 if ( parent.isValid() ) 0303 return 0; 0304 return d->fonts.size() + d->filtered_faves.size(); 0305 } 0306 0307 void glaxnimate::gui::font::FontModel::set_favourite(const QString& family, bool favourite) 0308 { 0309 if ( favourite == bool(d->faves.count(family)) ) 0310 return; 0311 0312 if ( favourite ) 0313 { 0314 d->faves.insert(family); 0315 if ( d->valid(family) ) 0316 { 0317 int index; 0318 for ( index = 0; index < d->filtered_faves.size(); index++ ) 0319 { 0320 if ( d->filtered_faves[index] > family ) 0321 break; 0322 } 0323 0324 beginInsertRows({}, index, index); 0325 d->filtered_faves.insert(index, family); 0326 endInsertRows(); 0327 } 0328 } 0329 else 0330 { 0331 d->faves.erase(family); 0332 if ( d->valid(family) ) 0333 { 0334 int index; 0335 for ( index = 0; index < d->filtered_faves.size(); index++ ) 0336 { 0337 if ( d->filtered_faves[index] == family ) 0338 break; 0339 } 0340 0341 beginRemoveRows({}, index, index); 0342 d->filtered_faves.removeAt(index); 0343 endRemoveRows(); 0344 } 0345 } 0346 } 0347 0348 void glaxnimate::gui::font::FontModel::toggle_favourite(const QString& family) 0349 { 0350 set_favourite(family, d->faves.count(family) == 0); 0351 }