Merge pull request #4259 from zhaowenlan1779/game-list
citra_qt: Add Game List configuration
This commit is contained in:
commit
9458ae0977
15 changed files with 427 additions and 121 deletions
|
@ -219,6 +219,28 @@ void Config::ReadValues() {
|
|||
ReadSetting("microProfileDialogVisible", false).toBool();
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("GameList");
|
||||
int icon_size = ReadSetting("iconSize", 2).toInt();
|
||||
if (icon_size < 0 || icon_size > 2) {
|
||||
icon_size = 2;
|
||||
}
|
||||
UISettings::values.game_list_icon_size = UISettings::GameListIconSize{icon_size};
|
||||
|
||||
int row_1 = ReadSetting("row1", 2).toInt();
|
||||
if (row_1 < 0 || row_1 > 3) {
|
||||
row_1 = 2;
|
||||
}
|
||||
UISettings::values.game_list_row_1 = UISettings::GameListText{row_1};
|
||||
|
||||
int row_2 = ReadSetting("row2", 0).toInt();
|
||||
if (row_2 < -1 || row_2 > 3) {
|
||||
row_2 = 0;
|
||||
}
|
||||
UISettings::values.game_list_row_2 = UISettings::GameListText{row_2};
|
||||
|
||||
UISettings::values.game_list_hide_no_icon = ReadSetting("hideNoIcon", false).toBool();
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("Paths");
|
||||
UISettings::values.roms_path = ReadSetting("romsPath").toString();
|
||||
UISettings::values.symbols_path = ReadSetting("symbolsPath").toString();
|
||||
|
@ -448,6 +470,13 @@ void Config::SaveValues() {
|
|||
WriteSetting("microProfileDialogVisible", UISettings::values.microprofile_visible, false);
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("GameList");
|
||||
WriteSetting("iconSize", static_cast<int>(UISettings::values.game_list_icon_size), 2);
|
||||
WriteSetting("row1", static_cast<int>(UISettings::values.game_list_row_1), 2);
|
||||
WriteSetting("row2", static_cast<int>(UISettings::values.game_list_row_2), 0);
|
||||
WriteSetting("hideNoIcon", UISettings::values.game_list_hide_no_icon, false);
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("Paths");
|
||||
WriteSetting("romsPath", UISettings::values.roms_path);
|
||||
WriteSetting("symbolsPath", UISettings::values.symbols_path);
|
||||
|
|
|
@ -63,6 +63,11 @@
|
|||
<string>Web</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="ConfigureUi" name="uiTab">
|
||||
<attribute name="title">
|
||||
<string>UI</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
@ -125,6 +130,12 @@
|
|||
<header>configuration/configure_web.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ConfigureUi</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>configuration/configure_ui.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections>
|
||||
|
|
|
@ -16,8 +16,7 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, const HotkeyRegistry& registry
|
|||
ui->generalTab->PopulateHotkeyList(registry);
|
||||
this->setConfiguration();
|
||||
this->PopulateSelectionList();
|
||||
connect(ui->generalTab, &ConfigureGeneral::languageChanged, this,
|
||||
&ConfigureDialog::onLanguageChanged);
|
||||
connect(ui->uiTab, &ConfigureUi::languageChanged, this, &ConfigureDialog::onLanguageChanged);
|
||||
connect(ui->selectorList, &QListWidget::itemSelectionChanged, this,
|
||||
&ConfigureDialog::UpdateVisibleTabs);
|
||||
|
||||
|
@ -39,6 +38,7 @@ void ConfigureDialog::applyConfiguration() {
|
|||
ui->cameraTab->applyConfiguration();
|
||||
ui->debugTab->applyConfiguration();
|
||||
ui->webTab->applyConfiguration();
|
||||
ui->uiTab->applyConfiguration();
|
||||
Settings::Apply();
|
||||
Settings::LogSettings();
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ void ConfigureDialog::applyConfiguration() {
|
|||
void ConfigureDialog::PopulateSelectionList() {
|
||||
|
||||
const std::array<std::pair<QString, QStringList>, 4> items{
|
||||
{{tr("General"), {tr("General"), tr("Web"), tr("Debug")}},
|
||||
{{tr("General"), {tr("General"), tr("Web"), tr("Debug"), tr("UI")}},
|
||||
{tr("System"), {tr("System"), tr("Audio"), tr("Camera")}},
|
||||
{tr("Graphics"), {tr("Graphics")}},
|
||||
{tr("Controls"), {tr("Input")}}}};
|
||||
|
@ -70,6 +70,7 @@ void ConfigureDialog::onLanguageChanged(const QString& locale) {
|
|||
ui->cameraTab->retranslateUi();
|
||||
ui->debugTab->retranslateUi();
|
||||
ui->webTab->retranslateUi();
|
||||
ui->uiTab->retranslateUi();
|
||||
}
|
||||
|
||||
void ConfigureDialog::UpdateVisibleTabs() {
|
||||
|
@ -77,11 +78,15 @@ void ConfigureDialog::UpdateVisibleTabs() {
|
|||
if (items.isEmpty())
|
||||
return;
|
||||
|
||||
const QHash<QString, QWidget*> widgets = {
|
||||
{tr("General"), ui->generalTab}, {tr("System"), ui->systemTab},
|
||||
{tr("Input"), ui->inputTab}, {tr("Graphics"), ui->graphicsTab},
|
||||
{tr("Audio"), ui->audioTab}, {tr("Camera"), ui->cameraTab},
|
||||
{tr("Debug"), ui->debugTab}, {tr("Web"), ui->webTab}};
|
||||
const QHash<QString, QWidget*> widgets = {{tr("General"), ui->generalTab},
|
||||
{tr("System"), ui->systemTab},
|
||||
{tr("Input"), ui->inputTab},
|
||||
{tr("Graphics"), ui->graphicsTab},
|
||||
{tr("Audio"), ui->audioTab},
|
||||
{tr("Camera"), ui->cameraTab},
|
||||
{tr("Debug"), ui->debugTab},
|
||||
{tr("Web"), ui->webTab},
|
||||
{tr("UI"), ui->uiTab}};
|
||||
|
||||
ui->tabWidget->clear();
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <QDirIterator>
|
||||
#include "citra_qt/configuration/configure_general.h"
|
||||
#include "citra_qt/ui_settings.h"
|
||||
#include "core/core.h"
|
||||
|
@ -13,28 +12,6 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
|
|||
: QWidget(parent), ui(new Ui::ConfigureGeneral) {
|
||||
|
||||
ui->setupUi(this);
|
||||
ui->language_combobox->addItem(tr("<System>"), QString(""));
|
||||
ui->language_combobox->addItem(tr("English"), QString("en"));
|
||||
QDirIterator it(":/languages", QDirIterator::NoIteratorFlags);
|
||||
while (it.hasNext()) {
|
||||
QString locale = it.next();
|
||||
locale.truncate(locale.lastIndexOf('.'));
|
||||
locale.remove(0, locale.lastIndexOf('/') + 1);
|
||||
QString lang = QLocale::languageToString(QLocale(locale).language());
|
||||
ui->language_combobox->addItem(lang, locale);
|
||||
}
|
||||
|
||||
// Unlike other configuration changes, interface language changes need to be reflected on the
|
||||
// interface immediately. This is done by passing a signal to the main window, and then
|
||||
// retranslating when passing back.
|
||||
connect(ui->language_combobox,
|
||||
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
|
||||
&ConfigureGeneral::onLanguageChanged);
|
||||
|
||||
for (const auto& theme : UISettings::themes) {
|
||||
ui->theme_combobox->addItem(theme.first, theme.second);
|
||||
}
|
||||
|
||||
this->setConfiguration();
|
||||
|
||||
ui->updateBox->setVisible(UISettings::values.updater_found);
|
||||
|
@ -50,10 +27,6 @@ void ConfigureGeneral::setConfiguration() {
|
|||
|
||||
// The first item is "auto-select" with actual value -1, so plus one here will do the trick
|
||||
ui->region_combobox->setCurrentIndex(Settings::values.region_value + 1);
|
||||
|
||||
ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme));
|
||||
ui->language_combobox->setCurrentIndex(
|
||||
ui->language_combobox->findData(UISettings::values.language));
|
||||
}
|
||||
|
||||
void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) {
|
||||
|
@ -62,8 +35,6 @@ void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) {
|
|||
|
||||
void ConfigureGeneral::applyConfiguration() {
|
||||
UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
|
||||
UISettings::values.theme =
|
||||
ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString();
|
||||
|
||||
UISettings::values.check_for_update_on_start = ui->toggle_update_check->isChecked();
|
||||
UISettings::values.update_on_close = ui->toggle_auto_update->isChecked();
|
||||
|
@ -71,13 +42,6 @@ void ConfigureGeneral::applyConfiguration() {
|
|||
Settings::values.region_value = ui->region_combobox->currentIndex() - 1;
|
||||
}
|
||||
|
||||
void ConfigureGeneral::onLanguageChanged(int index) {
|
||||
if (index == -1)
|
||||
return;
|
||||
|
||||
emit languageChanged(ui->language_combobox->itemData(index).toString());
|
||||
}
|
||||
|
||||
void ConfigureGeneral::retranslateUi() {
|
||||
ui->retranslateUi(this);
|
||||
ui->hotkeysDialog->retranslateUi();
|
||||
|
|
|
@ -24,12 +24,6 @@ public:
|
|||
void applyConfiguration();
|
||||
void retranslateUi();
|
||||
|
||||
private slots:
|
||||
void onLanguageChanged(int index);
|
||||
|
||||
signals:
|
||||
void languageChanged(const QString& locale);
|
||||
|
||||
private:
|
||||
void setConfiguration();
|
||||
|
||||
|
|
|
@ -31,20 +31,6 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="language_label">
|
||||
<property name="text">
|
||||
<string>Interface language</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="language_combobox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
|
@ -145,33 +131,6 @@
|
|||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="theme_group_box">
|
||||
<property name="title">
|
||||
<string>Theme</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="theme_qhbox_layout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="theme_qvbox_layout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="theme_qhbox_layout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="theme_label">
|
||||
<property name="text">
|
||||
<string>Theme:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="theme_combobox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_3">
|
||||
<property name="title">
|
||||
|
|
72
src/citra_qt/configuration/configure_ui.cpp
Normal file
72
src/citra_qt/configuration/configure_ui.cpp
Normal file
|
@ -0,0 +1,72 @@
|
|||
// Copyright 2018 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <QDirIterator>
|
||||
#include "citra_qt/configuration/configure_ui.h"
|
||||
#include "citra_qt/ui_settings.h"
|
||||
#include "ui_configure_ui.h"
|
||||
|
||||
ConfigureUi::ConfigureUi(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureUi) {
|
||||
ui->setupUi(this);
|
||||
ui->language_combobox->addItem(tr("<System>"), QString(""));
|
||||
ui->language_combobox->addItem(tr("English"), QString("en"));
|
||||
QDirIterator it(":/languages", QDirIterator::NoIteratorFlags);
|
||||
while (it.hasNext()) {
|
||||
QString locale = it.next();
|
||||
locale.truncate(locale.lastIndexOf('.'));
|
||||
locale.remove(0, locale.lastIndexOf('/') + 1);
|
||||
QString lang = QLocale::languageToString(QLocale(locale).language());
|
||||
ui->language_combobox->addItem(lang, locale);
|
||||
}
|
||||
|
||||
// Unlike other configuration changes, interface language changes need to be reflected on the
|
||||
// interface immediately. This is done by passing a signal to the main window, and then
|
||||
// retranslating when passing back.
|
||||
connect(ui->language_combobox,
|
||||
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
|
||||
&ConfigureUi::onLanguageChanged);
|
||||
|
||||
for (const auto& theme : UISettings::themes) {
|
||||
ui->theme_combobox->addItem(theme.first, theme.second);
|
||||
}
|
||||
|
||||
this->setConfiguration();
|
||||
}
|
||||
|
||||
ConfigureUi::~ConfigureUi() = default;
|
||||
|
||||
void ConfigureUi::setConfiguration() {
|
||||
ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme));
|
||||
ui->language_combobox->setCurrentIndex(
|
||||
ui->language_combobox->findData(UISettings::values.language));
|
||||
ui->icon_size_combobox->setCurrentIndex(
|
||||
static_cast<int>(UISettings::values.game_list_icon_size));
|
||||
ui->row_1_text_combobox->setCurrentIndex(static_cast<int>(UISettings::values.game_list_row_1));
|
||||
ui->row_2_text_combobox->setCurrentIndex(static_cast<int>(UISettings::values.game_list_row_2) +
|
||||
1);
|
||||
ui->toggle_hide_no_icon->setChecked(UISettings::values.game_list_hide_no_icon);
|
||||
}
|
||||
|
||||
void ConfigureUi::applyConfiguration() {
|
||||
UISettings::values.theme =
|
||||
ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString();
|
||||
UISettings::values.game_list_icon_size =
|
||||
static_cast<UISettings::GameListIconSize>(ui->icon_size_combobox->currentIndex());
|
||||
UISettings::values.game_list_row_1 =
|
||||
static_cast<UISettings::GameListText>(ui->row_1_text_combobox->currentIndex());
|
||||
UISettings::values.game_list_row_2 =
|
||||
static_cast<UISettings::GameListText>(ui->row_2_text_combobox->currentIndex() - 1);
|
||||
UISettings::values.game_list_hide_no_icon = ui->toggle_hide_no_icon->isChecked();
|
||||
}
|
||||
|
||||
void ConfigureUi::onLanguageChanged(int index) {
|
||||
if (index == -1)
|
||||
return;
|
||||
|
||||
emit languageChanged(ui->language_combobox->itemData(index).toString());
|
||||
}
|
||||
|
||||
void ConfigureUi::retranslateUi() {
|
||||
ui->retranslateUi(this);
|
||||
}
|
34
src/citra_qt/configuration/configure_ui.h
Normal file
34
src/citra_qt/configuration/configure_ui.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
// Copyright 2018 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <QWidget>
|
||||
|
||||
namespace Ui {
|
||||
class ConfigureUi;
|
||||
}
|
||||
|
||||
class ConfigureUi : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ConfigureUi(QWidget* parent = nullptr);
|
||||
~ConfigureUi();
|
||||
|
||||
void applyConfiguration();
|
||||
void retranslateUi();
|
||||
|
||||
private slots:
|
||||
void onLanguageChanged(int index);
|
||||
|
||||
signals:
|
||||
void languageChanged(const QString& locale);
|
||||
|
||||
private:
|
||||
void setConfiguration();
|
||||
|
||||
std::unique_ptr<Ui::ConfigureUi> ui;
|
||||
};
|
194
src/citra_qt/configuration/configure_ui.ui
Normal file
194
src/citra_qt/configuration/configure_ui.ui
Normal file
|
@ -0,0 +1,194 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ConfigureUi</class>
|
||||
<widget class="QWidget" name="ConfigureUi">
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>290</width>
|
||||
<height>280</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="general_groupBox">
|
||||
<property name="title">
|
||||
<string>General</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="language_label">
|
||||
<property name="text">
|
||||
<string>Interface language:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="language_combobox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="theme_label">
|
||||
<property name="text">
|
||||
<string>Theme:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="theme_combobox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="game_list_groupBox">
|
||||
<property name="title">
|
||||
<string>Game List</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<item>
|
||||
<widget class="QLabel" name="icon_size_label">
|
||||
<property name="text">
|
||||
<string>Icon Size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="icon_size_combobox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>None</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Small (24x24)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Large (48x48)</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<item>
|
||||
<widget class="QLabel" name="row_1_text_label">
|
||||
<property name="text">
|
||||
<string>Row 1 Text:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="row_1_text_combobox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>File Name</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Full Path</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Title Name</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Title ID</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_7">
|
||||
<item>
|
||||
<widget class="QLabel" name="row_2_text_label">
|
||||
<property name="text">
|
||||
<string>Row 2 Text:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="row_2_text_combobox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>None</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>File Name</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Full Path</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Title Name</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Title ID</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_hide_no_icon">
|
||||
<property name="text">
|
||||
<string>Hide Titles without Icon</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
Loading…
Add table
Add a link
Reference in a new issue