[Client] Settings: enlarge the settings dialog (#7483)

* [Client] Settings: enlarge the settings dialog

Keep settings modal per review: size to ~90% of the main window
(5% margins each side) with screen fallback, size grip and
750x700 minimum bounded to the screen. Drops the tab approach
while giving dense pages room without the tab-switch friction.

* [Client] Settings: clamp dialog width to 1280 per review

* [Client] Settings: extract dialog size calculation per review

Move the host/screen target math out of the DlgSettings constructor into a calculateDialogSize helper. Drop the leftover 'per review' wording from the clamp comment. No behavior change.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
BruebachL 2026-10-18 22:48:50 +02:00 • committed by GitHub
parent 679762c624
commit 665f7267e4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 46 additions and 7 deletions

View file

@ -20,6 +20,7 @@
#include <QMessageBox> #include <QMessageBox>
#include <QMessageLogger> #include <QMessageLogger>
#include <QPushButton> #include <QPushButton>
#include <QRect>
#include <QScreen> #include <QScreen>
#include <QSize> #include <QSize>
#include <QVBoxLayout> #include <QVBoxLayout>
@ -28,10 +29,49 @@
#include <libcockatrice/settings/paths_settings.h> #include <libcockatrice/settings/paths_settings.h>
#include <libcockatrice/settings/personal_settings.h> #include <libcockatrice/settings/personal_settings.h>
static constexpr int MIN_DIALOG_WIDTH = 750;
static constexpr int MIN_DIALOG_HEIGHT = 700;
static constexpr int MAX_DIALOG_WIDTH = 1280;
static constexpr double DIALOG_SIZE_RATIO = 0.9;
/**
* @brief Calculates the initial dialog size from the host window or screen
* @param hostSize Size of the hosting main window, or empty when parentless
* @param availableSize Available screen area the dialog must fit into
* @param minimumSize Minimum dialog size already applied via setMinimumSize
* @return Target size at ~90% of the host with small margins, clamped to fit
*/
static QSize calculateDialogSize(const QSize &hostSize, const QSize &availableSize, const QSize &minimumSize)
{
QSize target = !hostSize.isEmpty() ? QSize(qRound(hostSize.width() * DIALOG_SIZE_RATIO),
qRound(hostSize.height() * DIALOG_SIZE_RATIO))
: QSize(qRound(availableSize.width() * DIALOG_SIZE_RATIO),
qRound(availableSize.height() * DIALOG_SIZE_RATIO));
// Evenly-spaced columns leave unnatural dead space on very wide windows, so clamp the width.
target.setWidth(qMin(target.width(), MAX_DIALOG_WIDTH));
return target.expandedTo(minimumSize).boundedTo(availableSize);
}
DlgSettings::DlgSettings(QWidget *parent) : QDialog(parent) DlgSettings::DlgSettings(QWidget *parent) : QDialog(parent)
{ {
auto rec = QGuiApplication::primaryScreen()->availableGeometry(); // Fill the main window with small margins so multi-group pages (e.g. Deck Editor) are not buried in a scroll.
setMinimumSize(qMin(750, rec.width()), qMin(700, rec.height())); // 90% of the host window leaves ~5% margins on each side; falls back to 90% of the screen when parentless.
QWidget *hostWindow = parent ? parent->window() : nullptr;
QScreen *screen = nullptr;
QSize hostSize;
if (hostWindow) {
hostSize = hostWindow->size();
screen = QGuiApplication::screenAt(hostWindow->geometry().center());
}
if (!screen) {
screen = QGuiApplication::primaryScreen();
}
const QRect available = screen ? screen->availableGeometry() : QRect(0, 0, 1280, 800);
const QSize minimum(qMin(MIN_DIALOG_WIDTH, available.width()), qMin(MIN_DIALOG_HEIGHT, available.height()));
setMinimumSize(minimum);
resize(calculateDialogSize(hostSize, available.size(), minimum));
setSizeGripEnabled(true);
connect(&SettingsCache::instance().personal(), &PersonalSettings::langChanged, this, &DlgSettings::updateLanguage); connect(&SettingsCache::instance().personal(), &PersonalSettings::langChanged, this, &DlgSettings::updateLanguage);
@ -54,8 +94,6 @@ DlgSettings::DlgSettings(QWidget *parent) : QDialog(parent)
connect(&SettingsCache::instance().personal(), &PersonalSettings::langChanged, this, &DlgSettings::retranslateUi); connect(&SettingsCache::instance().personal(), &PersonalSettings::langChanged, this, &DlgSettings::retranslateUi);
retranslateUi(); retranslateUi();
adjustSize();
} }
void DlgSettings::setTab(int index) void DlgSettings::setTab(int index)

View file

@ -22,7 +22,8 @@ inline Q_LOGGING_CATEGORY(DlgSettingsLog, "dlg_settings");
* *
* Thin QDialog frame around SettingsView: only the window title, the OK button * Thin QDialog frame around SettingsView: only the window title, the OK button
* and the close validation live here. All pages, navigation and search are * and the close validation live here. All pages, navigation and search are
* owned by SettingsView so other hosts (e.g. a settings tab) can reuse them. * owned by SettingsView. The dialog fills ~90% of the main window so dense
* pages stay visible without losing the modal adjust-and-return flow.
*/ */
class DlgSettings : public QDialog class DlgSettings : public QDialog
{ {

View file

@ -1,6 +1,6 @@
/** /**
* @file settings_view.h * @file settings_view.h
* @brief Settings content widget hosted by the settings dialog (and, later, the settings tab) * @brief Settings content widget hosted by the settings dialog
* @ingroup Dialogs * @ingroup Dialogs
*/ */
#ifndef SETTINGS_VIEW_H #ifndef SETTINGS_VIEW_H
@ -28,7 +28,7 @@ class SettingsSearchDelegate;
* @brief Settings pages with tabbed navigation and search, without any dialog chrome * @brief Settings pages with tabbed navigation and search, without any dialog chrome
* *
* Owns the search bar, the tab bar, the stacked settings pages and the search * Owns the search bar, the tab bar, the stacked settings pages and the search
* results view. Dialog/tab hosts only add their own frame (buttons, titles). * results view. The settings dialog only adds its own frame (buttons, titles).
*/ */
class SettingsView : public QWidget class SettingsView : public QWidget
{ {