mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-05-17 18:42:24 +00:00
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:
@@ -13,6 +13,10 @@
|
|||||||
#include <wx/stattext.h>
|
#include <wx/stattext.h>
|
||||||
#include <wx/button.h>
|
#include <wx/button.h>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#define COLOR_DEMO_SIZE wxSize(FromDIP(50), FromDIP(50))
|
#define COLOR_DEMO_SIZE wxSize(FromDIP(50), FromDIP(50))
|
||||||
#define COLOR_BTN_BITMAP_SIZE wxSize(FromDIP(24), FromDIP(24))
|
#define COLOR_BTN_BITMAP_SIZE wxSize(FromDIP(24), FromDIP(24))
|
||||||
#define COLOR_BTN_SIZE wxSize(FromDIP(30), FromDIP(30))
|
#define COLOR_BTN_SIZE wxSize(FromDIP(30), FromDIP(30))
|
||||||
@@ -37,6 +41,21 @@ void FilamentPickerDialog::on_dpi_changed(const wxRect &suggested_rect)
|
|||||||
Layout();
|
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)
|
FilamentPickerDialog::FilamentPickerDialog(wxWindow *parent, const wxString& fila_id, const FilamentColor& fila_color, const std::string& fila_type)
|
||||||
: DPIDialog(parent ? parent : wxGetApp().mainframe,
|
: DPIDialog(parent ? parent : wxGetApp().mainframe,
|
||||||
wxID_ANY,
|
wxID_ANY,
|
||||||
@@ -118,10 +137,16 @@ FilamentPickerDialog::FilamentPickerDialog(wxWindow *parent, const wxString& fil
|
|||||||
// Set window transparency
|
// Set window transparency
|
||||||
SetTransparent(255);
|
SetTransparent(255);
|
||||||
BindEvents();
|
BindEvents();
|
||||||
|
|
||||||
|
// Start click detection timer for outside click detection
|
||||||
|
StartClickDetection();
|
||||||
}
|
}
|
||||||
|
|
||||||
FilamentPickerDialog::~FilamentPickerDialog()
|
FilamentPickerDialog::~FilamentPickerDialog()
|
||||||
{
|
{
|
||||||
|
// Clean up all timers
|
||||||
|
CleanupTimers();
|
||||||
|
|
||||||
delete m_color_query;
|
delete m_color_query;
|
||||||
m_color_query = nullptr;
|
m_color_query = nullptr;
|
||||||
|
|
||||||
@@ -584,19 +609,12 @@ wxColourData FilamentPickerDialog::GetSingleColorData()
|
|||||||
|
|
||||||
void FilamentPickerDialog::BindEvents()
|
void FilamentPickerDialog::BindEvents()
|
||||||
{
|
{
|
||||||
// Bind mouse events
|
// Bind mouse events for window dragging
|
||||||
Bind(wxEVT_LEFT_DOWN, &FilamentPickerDialog::OnMouseLeftDown, this);
|
Bind(wxEVT_LEFT_DOWN, &FilamentPickerDialog::OnMouseLeftDown, this);
|
||||||
Bind(wxEVT_MOTION, &FilamentPickerDialog::OnMouseMove, this);
|
Bind(wxEVT_MOTION, &FilamentPickerDialog::OnMouseMove, this);
|
||||||
Bind(wxEVT_LEFT_UP, &FilamentPickerDialog::OnMouseLeftUp, this);
|
Bind(wxEVT_LEFT_UP, &FilamentPickerDialog::OnMouseLeftUp, this);
|
||||||
|
|
||||||
// Add safety event handlers to ensure mouse capture is released
|
// Essential window events
|
||||||
Bind(wxEVT_KILL_FOCUS, [this](wxFocusEvent& event) {
|
|
||||||
if (HasCapture()) {
|
|
||||||
ReleaseMouse();
|
|
||||||
}
|
|
||||||
event.Skip();
|
|
||||||
});
|
|
||||||
|
|
||||||
Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& event) {
|
Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& event) {
|
||||||
if (HasCapture()) {
|
if (HasCapture()) {
|
||||||
ReleaseMouse();
|
ReleaseMouse();
|
||||||
@@ -728,5 +746,132 @@ void FilamentPickerDialog::OnButtonPaint(wxPaintEvent& event)
|
|||||||
dc.DrawRectangle(1, 1, COLOR_BTN_SIZE.GetWidth() - 1, COLOR_BTN_SIZE.GetHeight() - 1);
|
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
|
}} // namespace Slic3r::GUI
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include <wx/scrolwin.h>
|
#include <wx/scrolwin.h>
|
||||||
#include <wx/bitmap.h>
|
#include <wx/bitmap.h>
|
||||||
#include <wx/region.h>
|
#include <wx/region.h>
|
||||||
|
#include <wx/timer.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@@ -39,6 +40,14 @@ protected:
|
|||||||
void OnMouseMove(wxMouseEvent& event);
|
void OnMouseMove(wxMouseEvent& event);
|
||||||
void OnMouseLeftUp(wxMouseEvent& event);
|
void OnMouseLeftUp(wxMouseEvent& event);
|
||||||
void OnButtonPaint(wxPaintEvent& 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:
|
private:
|
||||||
// UI creation methods
|
// UI creation methods
|
||||||
@@ -67,6 +76,9 @@ private:
|
|||||||
bool LoadFilamentData(const wxString& fila_id);
|
bool LoadFilamentData(const wxString& fila_id);
|
||||||
wxColourData GetSingleColorData();
|
wxColourData GetSingleColorData();
|
||||||
|
|
||||||
|
// Flash effect
|
||||||
|
void StartFlashing();
|
||||||
|
|
||||||
// UI elements
|
// UI elements
|
||||||
wxStaticBitmap* m_color_demo{nullptr};
|
wxStaticBitmap* m_color_demo{nullptr};
|
||||||
wxStaticText* m_label_preview_color{nullptr};
|
wxStaticText* m_label_preview_color{nullptr};
|
||||||
@@ -90,6 +102,14 @@ private:
|
|||||||
|
|
||||||
// Mouse drag members
|
// Mouse drag members
|
||||||
wxPoint m_drag_delta;
|
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
|
}} // namespace Slic3r::GUI
|
||||||
|
|||||||
Reference in New Issue
Block a user