Files
86Box/src/qt/qt_progsettings.cpp
aubymori 7f69f00ed2 Query UI font from system on Windows
Previously, we determined the UI font from a predefined set of fonts
each mapped to a language. This works well if the user doesn't change
their Windows UI font, but if one does, they will get the default UI
font associated with their language instead of the one the set.

This commit replaces ProgSettings::getFontName with
ProgSettings::getUIFont, which uses the SystemParametersInfo API to
query the message font from the system, which will allow users to have a
custom font. It will also not interfere with different languages, as the
message font will be appropriately set by default there.
2026-02-02 19:15:31 -06:00

316 lines
11 KiB
C++

/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* Program settings UI module.
*
* Authors: Cacodemon345
*
* Copyright 2021-2022 Cacodemon345
*/
#include <QDebug>
#include "qt_progsettings.hpp"
#include "ui_qt_progsettings.h"
#include "qt_mainwindow.hpp"
#include "ui_qt_mainwindow.h"
#include "qt_machinestatus.hpp"
#include <QTimer>
#include <QMap>
#include <QDir>
#include <QFile>
#include <QLibraryInfo>
#ifdef Q_OS_WINDOWS
# include <QSysInfo>
# include <QVersionNumber>
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
#endif
extern "C" {
#include <86box/86box.h>
#include <86box/version.h>
#include <86box/config.h>
#include <86box/plat.h>
#include <86box/mem.h>
#include <86box/rom.h>
#include <86box/video.h>
}
extern MainWindow *main_window;
ProgSettings::CustomTranslator *ProgSettings::translator = nullptr;
QTranslator *ProgSettings::qtTranslator = nullptr;
QVector<QPair<QString, QString>> ProgSettings::languages = {
{ "system", "(System Default)" },
{ "ca-ES", "Catalan (Spain)" },
{ "zh-CN", "Chinese (Simplified)" },
{ "zh-TW", "Chinese (Traditional)" },
{ "hr-HR", "Croatian (Croatia)" },
{ "cs-CZ", "Czech (Czech Republic)" },
{ "de-DE", "German (Germany)" },
{ "el-GR", "Greek (Greece)" },
{ "en-GB", "English (United Kingdom)" },
{ "en-US", "English (United States)" },
{ "fi-FI", "Finnish (Finland)" },
{ "fr-FR", "French (France)" },
{ "it-IT", "Italian (Italy)" },
{ "ja-JP", "Japanese (Japan)" },
{ "ko-KR", "Korean (Korea)" },
{ "nl-NL", "Dutch (Netherlands)" },
{ "nb-NO", "Norwegian (Bokmål)" },
{ "pl-PL", "Polish (Poland)" },
{ "pt-BR", "Portuguese (Brazil)" },
{ "pt-PT", "Portuguese (Portugal)" },
{ "ru-RU", "Russian (Russia)" },
{ "sk-SK", "Slovak (Slovakia)" },
{ "sl-SI", "Slovenian (Slovenia)" },
{ "sv-SE", "Swedish (Sweden)" },
{ "es-ES", "Spanish (Spain)" },
{ "tr-TR", "Turkish (Turkey)" },
{ "uk-UA", "Ukrainian (Ukraine)" },
{ "vi-VN", "Vietnamese (Vietnam)" },
};
ProgSettings::ProgSettings(QWidget *parent)
: QDialog(parent)
, ui(new Ui::ProgSettings)
{
ui->setupUi(this);
ui->comboBoxLanguage->setItemData(0, 0);
for (int i = 1; i < languages.length(); i++) {
ui->comboBoxLanguage->addItem(languages[i].second, i);
if (i == lang_id) {
ui->comboBoxLanguage->setCurrentIndex(ui->comboBoxLanguage->findData(i));
}
}
ui->comboBoxLanguage->model()->sort(Qt::AscendingOrder);
mouseSensitivity = mouse_sensitivity;
ui->horizontalSlider->setValue(mouseSensitivity * 100.);
ui->openDirUsrPath->setChecked(open_dir_usr_path > 0);
ui->checkBoxMultimediaKeys->setChecked(inhibit_multimedia_keys);
ui->checkBoxConfirmExit->setChecked(confirm_exit);
ui->checkBoxConfirmSave->setChecked(confirm_save);
ui->checkBoxConfirmHardReset->setChecked(confirm_reset);
ui->radioButtonSystem->setChecked(color_scheme == 0);
ui->radioButtonLight->setChecked(color_scheme == 1);
ui->radioButtonDark->setChecked(color_scheme == 2);
#ifndef Q_OS_WINDOWS
ui->checkBoxMultimediaKeys->setHidden(true);
ui->groupBox->setHidden(true);
#endif
}
void
ProgSettings::accept()
{
auto size = main_window->centralWidget()->size();
lang_id = ui->comboBoxLanguage->currentData().toInt();
open_dir_usr_path = ui->openDirUsrPath->isChecked() ? 1 : 0;
confirm_exit = ui->checkBoxConfirmExit->isChecked() ? 1 : 0;
confirm_save = ui->checkBoxConfirmSave->isChecked() ? 1 : 0;
confirm_reset = ui->checkBoxConfirmHardReset->isChecked() ? 1 : 0;
inhibit_multimedia_keys = ui->checkBoxMultimediaKeys->isChecked() ? 1 : 0;
color_scheme = (ui->radioButtonSystem->isChecked()) ? 0 : (ui->radioButtonLight->isChecked() ? 1 : 2);
#ifdef Q_OS_WINDOWS
extern void selectDarkMode();
selectDarkMode();
#endif
loadTranslators(QCoreApplication::instance());
reloadStrings();
update_mouse_msg();
main_window->ui->retranslateUi(main_window);
QString vmname(vm_name);
if (vmname.at(vmname.size() - 1) == '"' || vmname.at(vmname.size() - 1) == '\'')
vmname.truncate(vmname.size() - 1);
main_window->setWindowTitle(QString("%1 - %2 %3").arg(vmname, EMU_NAME, EMU_VERSION_FULL));
QString msg = main_window->status->getMessage();
main_window->status.reset(new MachineStatus(main_window));
main_window->refreshMediaMenu();
main_window->status->message(msg);
connect(main_window, &MainWindow::updateStatusBarTip, main_window->status.get(), &MachineStatus::updateTip);
connect(main_window, &MainWindow::statusBarMessage, main_window->status.get(), &MachineStatus::message, Qt::QueuedConnection);
mouse_sensitivity = mouseSensitivity;
config_save_global();
QTimer::singleShot(200, [size] () {
main_window->centralWidget()->setFixedSize(size);
QApplication::processEvents();
if (vid_resize == 1) {
main_window->centralWidget()->setFixedSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
}
});
QDialog::accept();
}
ProgSettings::~ProgSettings()
{
delete ui;
}
#ifdef Q_OS_WINDOWS
/* Returns the standard UI font for Windows, which by default varies for different
languages. It can also be changed via external tools, if the user wants that.
We use the message font here since that is what most Windows components and
other third-party programs use. */
QFont
ProgSettings::getUIFont()
{
// Get the system (primary monitor) DPI. The font returned by
// SystemParametersInfo is scaled according to this and we need
// to get the font size in points to pass into QFont's constructor.
HDC hdc = GetDC(NULL);
int systemDpi = GetDeviceCaps(hdc, LOGPIXELSY);
ReleaseDC(NULL, hdc);
// Get the font metrics.
NONCLIENTMETRICSW ncm = {};
ncm.cbSize = sizeof(ncm);
// This should never happen, but just to be safe, return Segoe UI if
// SPI fails.
if (!SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0))
{
return QFont("Segoe UI", 9);
}
QString fontName = QString::fromWCharArray(ncm.lfMessageFont.lfFaceName);
// Windows' conversion from points to pixels goes as follows:
//
// -MulDiv(PointSize, GetDeviceCaps(hDC, LOGPIXELSY), 72)
//
// (source: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createfontw)
//
// Let's reverse that calculation to get the point size from the message font.
int fontSize = -MulDiv(ncm.lfMessageFont.lfHeight, 72, systemDpi);
return QFont(fontName, fontSize);
}
#endif
int
ProgSettings::languageCodeToId(QString langCode)
{
for (int i = 0; i < languages.length(); i++) {
if (languages[i].first == langCode) {
return i;
}
}
return 0;
}
QString
ProgSettings::languageIdToCode(int id)
{
if ((id <= 0) || (id >= languages.length())) {
return "system";
}
return languages[id].first;
}
void
ProgSettings::loadTranslators(QObject *parent)
{
if (qtTranslator) {
QApplication::removeTranslator(qtTranslator);
qtTranslator = nullptr;
}
if (translator) {
QApplication::removeTranslator(translator);
translator = nullptr;
}
qtTranslator = new QTranslator(parent);
translator = new CustomTranslator(parent);
QString localetofilename = "";
if (lang_id == 0 || lang_id >= languages.length()) {
for (int i = 0; i < QLocale::system().uiLanguages().size(); i++) {
localetofilename = QLocale::system().uiLanguages()[i];
if (translator->load(QLatin1String("86box_") + localetofilename, QLatin1String(":/"))) {
qDebug() << "Translations loaded.";
QCoreApplication::installTranslator(translator);
/* First try qtbase */
if (!loadQtTranslations(QLatin1String("qtbase_") + localetofilename.replace('-', '_')))
/* If that fails, try legacy qt_* translations */
if (!loadQtTranslations(QLatin1String("qt_") + localetofilename.replace('-', '_')))
qDebug() << "Failed to find Qt translations!";
if (QCoreApplication::installTranslator(qtTranslator))
qDebug() << "Qt translations loaded.";
break;
}
}
} else {
if (translator->load(QLatin1String("86box_") + languages[lang_id].first, QLatin1String(":/")))
qDebug() << "Translations loaded.";
QCoreApplication::installTranslator(translator);
/* First try qtbase */
if (!loadQtTranslations(QLatin1String("qtbase_") + QString(languages[lang_id].first).replace('-', '_')))
/* If that fails, try legacy qt_* translations */
if (!loadQtTranslations(QLatin1String("qt_") + QString(languages[lang_id].first).replace('-', '_')))
qDebug() << "Failed to find Qt translations!";
if (QCoreApplication::installTranslator(qtTranslator))
qDebug() << "Qt translations loaded.";
}
}
bool
ProgSettings::loadQtTranslations(const QString name)
{
QString name_lang_only = name.left(name.indexOf('_'));
/* System-wide translations */
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
if (qtTranslator->load(name, QLibraryInfo::path(QLibraryInfo::TranslationsPath)))
#else
if (qtTranslator->load(name, QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
#endif
return true;
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
else if (qtTranslator->load(name_lang_only, QLibraryInfo::path(QLibraryInfo::TranslationsPath)))
#else
else if (qtTranslator->load(name_lang_only, QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
#endif
return true;
/* Bundled translations (embedded) */
else if (qtTranslator->load(name, QLatin1String(":/")))
return true;
else if (qtTranslator->load(name_lang_only, QLatin1String(":/")))
return true;
/* Bundled translations (external) */
else if (qtTranslator->load(name, QApplication::applicationDirPath() + "/./translations/"))
return true;
else if (qtTranslator->load(name_lang_only, QApplication::applicationDirPath() + "/./translations/"))
return true;
else
return false;
}
void
ProgSettings::on_pushButtonLanguage_released()
{
ui->comboBoxLanguage->setCurrentIndex(0);
}
void
ProgSettings::on_horizontalSlider_valueChanged(int value)
{
mouseSensitivity = (double) value / 100.;
}
void
ProgSettings::on_pushButton_2_clicked()
{
mouseSensitivity = 1.0;
ui->horizontalSlider->setValue(100);
}