Files
leocad/common/lc_colorpicker.cpp
T
Leonardo Zide bc3d0f66bd Fix popup placement on secondary monitors
Color pickers compared global coordinates with screen dimensions, placing
Minifig Wizard and Blender preferences palettes far from their controls on
secondary monitors. The numeric ladder also used incorrect half-size bounds.

Share monitor selection and available-area clamping through
lcGetPopupPosition(), using the anchor or cursor to select the monitor.
Size color palettes before placement and reuse the helper when resizing
part-selection menus. Preserve monitor offsets, reserve desktop work areas,
and align oversized popups to the screen's top or left edge.

Validation: macOS Qt 6 build and 49 offscreen placement checks across four
simulated monitors, including negative coordinates, oversized popups, and
primary-screen fallback. Windows hardware testing remains outstanding.

Fixes #1041
2026-10-23 14:58:56 -07:00

198 lines
4.3 KiB
C++

#include "lc_global.h"
#include "lc_colorpicker.h"
#include "lc_colorlist.h"
#include "lc_colors.h"
#include "lc_qutils.h"
lcColorPickerPopup::lcColorPickerPopup(QWidget* Parent, int ColorIndex, bool AllowNoColor)
: QFrame(Parent, Qt::Popup)
{
setFrameStyle(QFrame::StyledPanel);
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
setFocusPolicy(Qt::StrongFocus);
setMouseTracking(true);
QGridLayout *layout = new QGridLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
setLayout(layout);
mColorList = new lcColorList(this, AllowNoColor);
connect(mColorList, &lcColorList::ColorChanged, this, &lcColorPickerPopup::ColorChanged);
connect(mColorList, &lcColorList::ColorSelected, this, &lcColorPickerPopup::ColorSelected);
layout->addWidget(mColorList);
mColorList->blockSignals(true);
mColorList->SetCurrentColor(ColorIndex);
mColorList->blockSignals(false);
mEventLoop = nullptr;
}
lcColorPickerPopup::~lcColorPickerPopup()
{
if (mEventLoop)
mEventLoop->exit();
}
void lcColorPickerPopup::exec()
{
show();
QEventLoop EventLoop;
mEventLoop = &EventLoop;
(void) EventLoop.exec();
mEventLoop = nullptr;
}
void lcColorPickerPopup::SetPosition(const QWidget* Anchor)
{
ensurePolished();
adjustSize();
const QPoint Position = Anchor->mapToGlobal(Anchor->rect().bottomLeft());
const QPoint ScreenPosition = Anchor->mapToGlobal(Anchor->rect().center());
move(lcGetPopupPosition(Position, size(), ScreenPosition));
}
void lcColorPickerPopup::mouseReleaseEvent(QMouseEvent* MouseEvent)
{
if (!rect().contains(MouseEvent->pos()))
hide();
}
void lcColorPickerPopup::hideEvent(QHideEvent* HideEvent)
{
if (mEventLoop)
mEventLoop->exit();
emit Hid();
QFrame::hideEvent(HideEvent);
}
void lcColorPickerPopup::ColorChanged(int ColorIndex)
{
emit Changed(ColorIndex);
}
void lcColorPickerPopup::ColorSelected(int ColorIndex)
{
emit Selected(ColorIndex);
hide();
}
void lcColorPickerPopup::showEvent(QShowEvent* ShowEvent)
{
Q_UNUSED(ShowEvent);
mColorList->setFocus();
}
lcColorPicker::lcColorPicker(QWidget* Parent, bool AllowNoColor)
: QPushButton(Parent), mAllowNoColor(AllowNoColor)
{
setFocusPolicy(Qt::StrongFocus);
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
setAutoDefault(false);
setCheckable(true);
UpdateIcon();
connect(this, &QPushButton::toggled, this, &lcColorPicker::ButtonPressed);
}
lcColorPicker::~lcColorPicker()
{
}
void lcColorPicker::SetCurrentColor(int ColorIndex)
{
Selected(ColorIndex);
}
void lcColorPicker::SetCurrentColorCode(int ColorCode)
{
SetCurrentColor(lcGetColorIndex(ColorCode));
}
int lcColorPicker::GetCurrentColor() const
{
return mCurrentColorIndex;
}
int lcColorPicker::GetCurrentColorCode() const
{
return gColorList[mCurrentColorIndex].Code;
}
void lcColorPicker::ButtonPressed(bool Toggled)
{
if (!Toggled)
return;
lcColorPickerPopup* Popup = new lcColorPickerPopup(this, mCurrentColorIndex, mAllowNoColor);
connect(Popup, &lcColorPickerPopup::Changed, this, &lcColorPicker::Changed);
connect(Popup, &lcColorPickerPopup::Selected, this, &lcColorPicker::Selected);
connect(Popup, &lcColorPickerPopup::Hid, this, &lcColorPicker::PopupClosed);
Popup->setMinimumSize(300, 200);
Popup->SetPosition(this);
clearFocus();
update();
Popup->setFocus();
Popup->show();
}
void lcColorPicker::UpdateIcon()
{
const int IconSize = 14;//style()->pixelMetric(QStyle::PM_SmallIconSize);
QPixmap Pixmap(IconSize, IconSize);
QPainter Painter(&Pixmap);
Painter.setPen(Qt::darkGray);
const lcColor* Color = &gColorList[mCurrentColorIndex];
if (Color->Code != LC_COLOR_NOCOLOR)
{
Painter.setBrush(QColor::fromRgbF(Color->Value[0], Color->Value[1], Color->Value[2]));
Painter.drawRect(0, 0, Pixmap.width() - 1, Pixmap.height() - 1);
}
else
lcDrawNoColorRect(Painter, QRect(0, 0, Pixmap.width() - 1, Pixmap.height() - 1));
Painter.end();
setIcon(QIcon(Pixmap));
}
void lcColorPicker::PopupClosed()
{
if (mInitialColorIndex != mCurrentColorIndex)
Changed(mInitialColorIndex);
setChecked(false);
setFocus();
}
void lcColorPicker::Changed(int ColorIndex)
{
if (ColorIndex == mCurrentColorIndex)
return;
mCurrentColorIndex = ColorIndex;
UpdateIcon();
update();
emit ColorChanged(mCurrentColorIndex);
}
void lcColorPicker::Selected(int ColorIndex)
{
mInitialColorIndex = ColorIndex;
Changed(ColorIndex);
}