Files

183 lines
4.9 KiB
QML

pragma Singleton
import QtQuick
import QtQuick.Controls
import Quickshell
import Quickshell.Io
import qs.Commons
Singleton {
id: root
property ListModel availableFonts: ListModel {}
property ListModel monospaceFonts: ListModel {}
property bool fontsLoaded: false
property bool isLoading: false
function init() {
if (fontsLoaded || isLoading)
return;
Logger.i("Font", "Service started");
loadFontsViaFcList();
}
// Load all fonts using fc-list in background - no main thread blocking
function loadFontsViaFcList() {
if (isLoading)
return;
isLoading = true;
allFontsProcess.running = true;
}
function populateModels(allFontsText, monoFontsText) {
// Parse monospace fonts into a lookup set
// fc-list returns comma-separated family names for fonts with multiple families
var monoLookup = {};
var monoLines = monoFontsText.split('\n');
for (var i = 0; i < monoLines.length; i++) {
var line = monoLines[i].trim();
if (line) {
var monoFamilies = line.split(',');
for (var mi = 0; mi < monoFamilies.length; mi++) {
var monoName = monoFamilies[mi].trim();
if (monoName)
monoLookup[monoName] = true;
}
}
}
// Parse all fonts - split comma-separated family names
var allLines = allFontsText.split('\n');
var fontSet = {}; // Deduplicate font families
for (var j = 0; j < allLines.length; j++) {
var line = allLines[j].trim();
if (line) {
var families = line.split(',');
for (var fi = 0; fi < families.length; fi++) {
var fontName = families[fi].trim();
if (fontName && !fontSet[fontName]) {
fontSet[fontName] = true;
}
}
}
}
// Sort font names
var sortedFonts = Object.keys(fontSet).sort(function (a, b) {
return a.localeCompare(b);
});
// Build arrays for batch insert
var allBatch = [];
var monoBatch = [];
for (var k = 0; k < sortedFonts.length; k++) {
var name = sortedFonts[k];
var fontObj = {
"key": name,
"name": name
};
allBatch.push(fontObj);
// Check if monospace
if (monoLookup[name] || name.toLowerCase().includes("mono")) {
monoBatch.push(fontObj);
}
}
// Clear and populate models (single batch operation)
availableFonts.clear();
monospaceFonts.clear();
availableFonts.append({
"key": Qt.application.font.family,
"name": I18n.tr("panels.indicator.system-default")
});
monospaceFonts.append({
"key": "monospace",
"name": I18n.tr("panels.indicator.system-default")
});
for (var m = 0; m < allBatch.length; m++)
availableFonts.append(allBatch[m]);
for (var n = 0; n < monoBatch.length; n++)
monospaceFonts.append(monoBatch[n]);
fontsLoaded = true;
isLoading = false;
Logger.i("Font", "Loaded", availableFonts.count, "fonts,", monospaceFonts.count, "monospace");
}
// Temporary storage for process outputs
property string _allFontsOutput: ""
property string _monoFontsOutput: ""
property bool _allFontsDone: false
property bool _monoFontsDone: false
function checkBothProcessesDone() {
if (_allFontsDone && _monoFontsDone) {
populateModels(_allFontsOutput, _monoFontsOutput);
// Clear temp storage
_allFontsOutput = "";
_monoFontsOutput = "";
_allFontsDone = false;
_monoFontsDone = false;
}
}
// Process to get all font families (runs in background)
Process {
id: allFontsProcess
command: ["fc-list", "--format", "%{family}\\n"]
running: false
stdout: StdioCollector {
onStreamFinished: {
root._allFontsOutput = this.text;
root._allFontsDone = true;
root.checkBothProcessesDone();
}
}
onRunningChanged: {
if (running) {
// Start mono fonts process in parallel
monoFontsProcess.running = true;
}
}
onExited: function (exitCode, exitStatus) {
if (exitCode !== 0) {
Logger.w("Font", "fc-list failed with exit code", exitCode);
root._allFontsOutput = "";
root._allFontsDone = true;
root.checkBothProcessesDone();
}
}
}
// Process to get monospace font families (runs in background, parallel)
Process {
id: monoFontsProcess
command: ["fc-list", ":mono", "--format", "%{family}\\n"]
running: false
stdout: StdioCollector {
onStreamFinished: {
root._monoFontsOutput = this.text;
root._monoFontsDone = true;
root.checkBothProcessesDone();
}
}
onExited: function (exitCode, exitStatus) {
if (exitCode !== 0) {
root._monoFontsOutput = "";
root._monoFontsDone = true;
root.checkBothProcessesDone();
}
}
}
}