Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 57 additions & 1 deletion framework/interactive/internal/interactive.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,44 @@ static void setCustomColors(const std::vector<QColor>& customColors)
}
}

//! NOTE Qt's default "Basic colors" grid is an unsorted historical palette (a patchwork of
//! seemingly random hues). Replace it once with a grayscale ramp followed by a hue sweep, so it
//! reads as an ordered gradient instead.
static void ensureStandardColorsAreSortedByHue()
{
static bool initialized = false;
if (initialized) {
return;
}
initialized = true;

constexpr int GRID_ROWS = 6;
constexpr int GRID_COLS = 8;
constexpr int GRID_SIZE = GRID_ROWS * GRID_COLS;
constexpr int GRAYSCALE_COUNT = GRID_COLS;

//! NOTE Qt's standard color grid is indexed column-major (index = row + col * GRID_ROWS),
//! so a plain sequential index would run down the first column instead of across the first
//! row. Map our row-major fill order onto Qt's column-major index instead.
auto setColorAt = [](int pos, const QColor& c) {
const int row = pos / GRID_COLS;
const int col = pos % GRID_COLS;
QColorDialog::setStandardColor(row + col * GRID_ROWS, c);
};

int pos = 0;
for (int i = 0; i < GRAYSCALE_COUNT; ++i) {
const int v = static_cast<int>(255.0 * i / (GRAYSCALE_COUNT - 1));
setColorAt(pos++, QColor(v, v, v));
}

const int hueSteps = GRID_SIZE - GRAYSCALE_COUNT;
for (int i = 0; i < hueSteps; ++i) {
const qreal hue = qreal(i) / hueSteps;
setColorAt(pos++, QColor::fromHsvF(hue, 0.65, 0.95));
}
}

async::Promise<Color> Interactive::selectColor(const Color& color, const std::string& title, bool allowAlpha)
{
if (m_isSelectColorOpened) {
Expand All @@ -531,6 +569,7 @@ async::Promise<Color> Interactive::selectColor(const Color& color, const std::st

m_isSelectColorOpened = true;

ensureStandardColorsAreSortedByHue();
setCustomColors(uiConfiguration()->colorDialogCustomColors());

return async::make_promise<Color>([this, color, title, allowAlpha](auto resolve, auto reject) {
Expand All @@ -551,6 +590,19 @@ async::Promise<Color> Interactive::selectColor(const Color& color, const std::st

dlg->setCurrentColor(currentColor);
dlg->setOption(QColorDialog::ShowAlphaChannel, allowAlpha);
//! NOTE Force Qt's own cross-platform dialog instead of the OS-native one (e.g. NSColorPanel on
//! macOS), so it picks up the app's theme palette applied globally by WidgetStyle.
dlg->setOption(QColorDialog::DontUseNativeDialog, true);
//! NOTE The app's windows (main window, Preferences, etc.) are QQuickWindows, not QWidgets, so
//! Qt::ApplicationModal alone doesn't block or stack above them: Qt only enforces widget modality
//! within the QWidget world. Force native window creation and set an explicit transient parent
//! so the platform's own window manager (which operates below the Widget/Quick split) keeps this
//! dialog on top of, and modal to, whichever window is actually active.
dlg->setWindowModality(Qt::ApplicationModal);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
dlg->winId();
if (QWindow* dlgWindow = dlg->windowHandle()) {
dlgWindow->setTransientParent(topWindow());
}

QObject::connect(dlg, &QColorDialog::finished, [this, dlg, resolve, reject](int result) {
DEFER {
Expand All @@ -573,7 +625,11 @@ async::Promise<Color> Interactive::selectColor(const Color& color, const std::st
(void)resolve(Color::fromQColor(selectedColor));
});

dlg->open();
//! NOTE Not open(): QDialog::open() unconditionally forces Qt::WindowModal before showing,
//! which would undo the Qt::ApplicationModal set above. show() displays the dialog without
//! touching its modality, and the finished signal still fires the same way once the dialog
//! is accepted/rejected.
dlg->show();

return async::Promise<Color>::dummy_result();
}, async::PromiseType::AsyncByBody);
Expand Down