ENH: Flash filament dialog on outside click to prompt focus

Brief flicker reminds users to act on the dialog before proceeding elsewhere.

jira: STUDIO-13492

Change-Id: I3daaa567f4aa738094fc01effcd9b9245aea9d2c
(cherry picked from commit 3decd7a5e4f5a13b7fd1dbb0fc39e0b3849ed6d1)
This commit is contained in:
fei2.fang 2025-07-23 19:19:42 +08:00 committed by Noisyfox
parent 44c13f3967
commit 723ce9d431
2 changed files with 174 additions and 9 deletions

View file

@ -13,6 +13,10 @@
#include <wx/stattext.h>
#include <wx/button.h>
#ifdef _WIN32
#include <windows.h>
#endif
#define COLOR_DEMO_SIZE wxSize(FromDIP(50), FromDIP(50))
#define COLOR_BTN_BITMAP_SIZE wxSize(FromDIP(24), FromDIP(24))
#define COLOR_BTN_SIZE wxSize(FromDIP(30), FromDIP(30))
@ -37,6 +41,21 @@ void FilamentPickerDialog::on_dpi_changed(const wxRect &suggested_rect)
Layout();
}
// Flash effect implementation
void FilamentPickerDialog::StartFlashing()
{
// Stop any existing flash timer
if (m_flash_timer) {
m_flash_timer->Stop();
delete m_flash_timer;
}
m_flash_timer = new wxTimer(this, wxID_ANY + 1);
Bind(wxEVT_TIMER, &FilamentPickerDialog::OnFlashTimer, this, m_flash_timer->GetId());
m_flash_step = 0;
m_flash_timer->Start(50);
}
FilamentPickerDialog::FilamentPickerDialog(wxWindow *parent, const wxString& fila_id, const FilamentColor& fila_color, const std::string& fila_type)
: DPIDialog(parent ? parent : wxGetApp().mainframe,
wxID_ANY,
@ -118,10 +137,16 @@ FilamentPickerDialog::FilamentPickerDialog(wxWindow *parent, const wxString& fil
// Set window transparency
SetTransparent(255);
BindEvents();
// Start click detection timer for outside click detection
StartClickDetection();
}
FilamentPickerDialog::~FilamentPickerDialog()
{
// Clean up all timers
CleanupTimers();
delete m_color_query;
m_color_query = nullptr;
@ -584,19 +609,12 @@ wxColourData FilamentPickerDialog::GetSingleColorData()
void FilamentPickerDialog::BindEvents()
{
// Bind mouse events
// Bind mouse events for window dragging
Bind(wxEVT_LEFT_DOWN, &FilamentPickerDialog::OnMouseLeftDown, this);
Bind(wxEVT_MOTION, &FilamentPickerDialog::OnMouseMove, this);
Bind(wxEVT_LEFT_UP, &FilamentPickerDialog::OnMouseLeftUp, this);
// Add safety event handlers to ensure mouse capture is released
Bind(wxEVT_KILL_FOCUS, [this](wxFocusEvent& event) {
if (HasCapture()) {
ReleaseMouse();
}
event.Skip();
});
// Essential window events
Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& event) {
if (HasCapture()) {
ReleaseMouse();
@ -728,5 +746,132 @@ void FilamentPickerDialog::OnButtonPaint(wxPaintEvent& event)
dc.DrawRectangle(1, 1, COLOR_BTN_SIZE.GetWidth() - 1, COLOR_BTN_SIZE.GetHeight() - 1);
}
bool FilamentPickerDialog::IsClickOnTopMostWindow(const wxPoint& mouse_pos)
{
wxWindow* main_window = wxGetApp().mainframe;
if (!main_window) {
return false;
}
wxRect main_rect = main_window->GetScreenRect();
bool in_main_app = main_rect.Contains(mouse_pos);
if (!in_main_app) {
return false;
}
#ifdef _WIN32
// Windows: Use WindowFromPoint to check actual topmost window
POINT pt = {mouse_pos.x, mouse_pos.y};
HWND hwnd_at_point = WindowFromPoint(pt);
HWND main_hwnd = (HWND)main_window->GetHandle();
// Check if clicked window belongs to our main window hierarchy
HWND parent_hwnd = hwnd_at_point;
while (parent_hwnd != NULL) {
if (parent_hwnd == main_hwnd) {
return true;
}
parent_hwnd = ::GetParent(parent_hwnd);
}
return false;
#elif defined(__WXOSX__)
// macOS: Use focus and active window check
return (wxGetActiveWindow() == main_window) || main_window->HasFocus();
#else
// Linux: Use focus check (similar to macOS)
return (wxGetActiveWindow() == main_window) || main_window->HasFocus();
#endif
}
void FilamentPickerDialog::StartClickDetection()
{
if (m_click_timer) {
StopClickDetection();
}
m_click_timer = new wxTimer(this, wxID_ANY);
Bind(wxEVT_TIMER, &FilamentPickerDialog::OnTimerCheck, this, m_click_timer->GetId());
m_click_timer->Start(50);
}
void FilamentPickerDialog::StopClickDetection()
{
if (m_click_timer) {
m_click_timer->Stop();
delete m_click_timer;
m_click_timer = nullptr;
}
}
void FilamentPickerDialog::CleanupTimers()
{
StopClickDetection();
if (m_flash_timer) {
m_flash_timer->Stop();
delete m_flash_timer;
m_flash_timer = nullptr;
}
}
// Only perform complex detection when the mouse state actually changes
void FilamentPickerDialog::OnTimerCheck(wxTimerEvent& event)
{
static wxPoint last_mouse_pos(-1, -1);
wxPoint current_pos = wxGetMousePosition();
// If the mouse position and button state haven't changed, skip the detection
if (current_pos == last_mouse_pos &&
wxGetMouseState().LeftIsDown() == m_last_mouse_down) {
return;
}
last_mouse_pos = current_pos;
wxPoint mouse_pos = wxGetMousePosition();
wxRect window_rect = GetScreenRect();
// Check if mouse button state changed
bool mouse_down = wxGetMouseState().LeftIsDown();
if (mouse_down != m_last_mouse_down) {
if (mouse_down) {
bool in_dialog = window_rect.Contains(mouse_pos);
bool is_valid_click = IsClickOnTopMostWindow(mouse_pos);
if (is_valid_click && !in_dialog) {
StartFlashing();
}
}
m_last_mouse_down = mouse_down;
}
}
void FilamentPickerDialog::OnFlashTimer(wxTimerEvent& event)
{
// 5 flashes = 10 steps (semi-transparent -> opaque for each flash)
if (m_flash_step < 10) {
if (m_flash_step % 2 == 0) {
// Even steps: semi-transparent
SetTransparent(120);
} else {
// Odd steps: opaque
SetTransparent(255);
}
m_flash_step++;
} else {
// Complete flash sequence
SetTransparent(255);
m_flash_timer->Stop();
delete m_flash_timer;
m_flash_timer = nullptr;
}
}
}} // namespace Slic3r::GUI

View file

@ -12,6 +12,7 @@
#include <wx/scrolwin.h>
#include <wx/bitmap.h>
#include <wx/region.h>
#include <wx/timer.h>
#include <vector>
#include <string>
@ -39,6 +40,14 @@ protected:
void OnMouseMove(wxMouseEvent& event);
void OnMouseLeftUp(wxMouseEvent& event);
void OnButtonPaint(wxPaintEvent& event);
void OnTimerCheck(wxTimerEvent& event);
void OnFlashTimer(wxTimerEvent& event);
// Platform-independent window detection
bool IsClickOnTopMostWindow(const wxPoint& mouse_pos);
void StartClickDetection();
void StopClickDetection();
void CleanupTimers();
private:
// UI creation methods
@ -67,6 +76,9 @@ private:
bool LoadFilamentData(const wxString& fila_id);
wxColourData GetSingleColorData();
// Flash effect
void StartFlashing();
// UI elements
wxStaticBitmap* m_color_demo{nullptr};
wxStaticText* m_label_preview_color{nullptr};
@ -90,6 +102,14 @@ private:
// Mouse drag members
wxPoint m_drag_delta;
// Click detection timers
wxTimer* m_click_timer{nullptr};
bool m_last_mouse_down{false};
// Flash effect timer
wxTimer* m_flash_timer{nullptr};
int m_flash_step{0};
};
}} // namespace Slic3r::GUI