2f6326a2eb
PrusaSlicer newly registers by Windows operating system for HID USB plug / unplug notifications and for Volume attach / detach notifications, and the background threads of the two respective services are waken up on these Windows notifications. The RemovableDriveManager also wakes up every 30 seconds to cope with the drives ejected from Windows Explorer or from another application, for example Cura, for which Windows OS does not send out notifications.
350 lines
10 KiB
C++
350 lines
10 KiB
C++
#ifndef slic3r_GUI_Utils_hpp_
|
|
#define slic3r_GUI_Utils_hpp_
|
|
|
|
#include <memory>
|
|
#include <string>
|
|
#include <ostream>
|
|
#include <functional>
|
|
|
|
#include <boost/optional.hpp>
|
|
|
|
#include <wx/frame.h>
|
|
#include <wx/dialog.h>
|
|
#include <wx/event.h>
|
|
#include <wx/filedlg.h>
|
|
#include <wx/gdicmn.h>
|
|
#include <wx/panel.h>
|
|
#include <wx/dcclient.h>
|
|
#include <wx/debug.h>
|
|
#include <wx/settings.h>
|
|
|
|
#include "Event.hpp"
|
|
|
|
class wxCheckBox;
|
|
class wxTopLevelWindow;
|
|
class wxRect;
|
|
|
|
|
|
namespace Slic3r {
|
|
namespace GUI {
|
|
|
|
#ifdef _WIN32
|
|
// USB HID attach / detach events from Windows OS.
|
|
using HIDDeviceAttachedEvent = Event<std::string>;
|
|
using HIDDeviceDetachedEvent = Event<std::string>;
|
|
wxDECLARE_EVENT(EVT_HID_DEVICE_ATTACHED, HIDDeviceAttachedEvent);
|
|
wxDECLARE_EVENT(EVT_HID_DEVICE_DETACHED, HIDDeviceDetachedEvent);
|
|
|
|
// Disk aka Volume attach / detach events from Windows OS.
|
|
using VolumeAttachedEvent = SimpleEvent;
|
|
using VolumeDetachedEvent = SimpleEvent;
|
|
wxDECLARE_EVENT(EVT_VOLUME_ATTACHED, VolumeAttachedEvent);
|
|
wxDECLARE_EVENT(EVT_VOLUME_DETACHED, VolumeDetachedEvent);
|
|
#endif /* _WIN32 */
|
|
|
|
wxTopLevelWindow* find_toplevel_parent(wxWindow *window);
|
|
|
|
void on_window_geometry(wxTopLevelWindow *tlw, std::function<void()> callback);
|
|
|
|
enum { DPI_DEFAULT = 96 };
|
|
|
|
int get_dpi_for_window(wxWindow *window);
|
|
wxFont get_default_font_for_dpi(int dpi);
|
|
|
|
struct DpiChangedEvent : public wxEvent {
|
|
int dpi;
|
|
wxRect rect;
|
|
|
|
DpiChangedEvent(wxEventType eventType, int dpi, wxRect rect)
|
|
: wxEvent(0, eventType), dpi(dpi), rect(rect)
|
|
{}
|
|
|
|
virtual wxEvent *Clone() const
|
|
{
|
|
return new DpiChangedEvent(*this);
|
|
}
|
|
};
|
|
|
|
wxDECLARE_EVENT(EVT_DPI_CHANGED_SLICER, DpiChangedEvent);
|
|
|
|
template<class P> class DPIAware : public P
|
|
{
|
|
public:
|
|
DPIAware(wxWindow *parent, wxWindowID id, const wxString &title, const wxPoint &pos=wxDefaultPosition,
|
|
const wxSize &size=wxDefaultSize, long style=wxDEFAULT_FRAME_STYLE, const wxString &name=wxFrameNameStr)
|
|
: P(parent, id, title, pos, size, style, name)
|
|
{
|
|
int dpi = get_dpi_for_window(this);
|
|
m_scale_factor = (float)dpi / (float)DPI_DEFAULT;
|
|
m_prev_scale_factor = m_scale_factor;
|
|
m_normal_font = get_default_font_for_dpi(dpi);
|
|
|
|
/* Because of default window font is a primary display font,
|
|
* We should set correct font for window before getting em_unit value.
|
|
*/
|
|
#ifndef __WXOSX__ // Don't call SetFont under OSX to avoid name cutting in ObjectList
|
|
this->SetFont(m_normal_font);
|
|
#endif
|
|
// initialize default width_unit according to the width of the one symbol ("m") of the currently active font of this window.
|
|
m_em_unit = std::max<size_t>(10, this->GetTextExtent("m").x - 1);
|
|
|
|
// recalc_font();
|
|
|
|
this->Bind(EVT_DPI_CHANGED_SLICER, [this](const DpiChangedEvent &evt) {
|
|
m_scale_factor = (float)evt.dpi / (float)DPI_DEFAULT;
|
|
|
|
m_new_font_point_size = get_default_font_for_dpi(evt.dpi).GetPointSize();
|
|
|
|
if (!m_can_rescale)
|
|
return;
|
|
|
|
if (is_new_scale_factor())
|
|
rescale(evt.rect);
|
|
});
|
|
|
|
this->Bind(wxEVT_MOVE_START, [this](wxMoveEvent& event)
|
|
{
|
|
event.Skip();
|
|
|
|
// Suppress application rescaling, when a MainFrame moving is not ended
|
|
m_can_rescale = false;
|
|
});
|
|
|
|
this->Bind(wxEVT_MOVE_END, [this](wxMoveEvent& event)
|
|
{
|
|
event.Skip();
|
|
|
|
m_can_rescale = is_new_scale_factor();
|
|
|
|
// If scale factor is different after moving of MainFrame ...
|
|
if (m_can_rescale)
|
|
// ... rescale application
|
|
rescale(event.GetRect());
|
|
else
|
|
// set value to _true_ in purpose of possibility of a display dpi changing from System Settings
|
|
m_can_rescale = true;
|
|
});
|
|
}
|
|
|
|
virtual ~DPIAware() {}
|
|
|
|
float scale_factor() const { return m_scale_factor; }
|
|
float prev_scale_factor() const { return m_prev_scale_factor; }
|
|
|
|
int em_unit() const { return m_em_unit; }
|
|
// int font_size() const { return m_font_size; }
|
|
const wxFont& normal_font() const { return m_normal_font; }
|
|
|
|
protected:
|
|
virtual void on_dpi_changed(const wxRect &suggested_rect) = 0;
|
|
|
|
private:
|
|
float m_scale_factor;
|
|
int m_em_unit;
|
|
// int m_font_size;
|
|
|
|
wxFont m_normal_font;
|
|
float m_prev_scale_factor;
|
|
bool m_can_rescale{ true };
|
|
|
|
int m_new_font_point_size;
|
|
|
|
// void recalc_font()
|
|
// {
|
|
// wxClientDC dc(this);
|
|
// const auto metrics = dc.GetFontMetrics();
|
|
// m_font_size = metrics.height;
|
|
// m_em_unit = metrics.averageWidth;
|
|
// }
|
|
|
|
// check if new scale is differ from previous
|
|
bool is_new_scale_factor() const { return fabs(m_scale_factor - m_prev_scale_factor) > 0.001; }
|
|
|
|
// function for a font scaling of the window
|
|
void scale_win_font(wxWindow *window, const int font_point_size)
|
|
{
|
|
wxFont new_font(window->GetFont());
|
|
new_font.SetPointSize(font_point_size);
|
|
window->SetFont(new_font);
|
|
}
|
|
|
|
// recursive function for scaling fonts for all controls in Window
|
|
void scale_controls_fonts(wxWindow *window, const int font_point_size)
|
|
{
|
|
auto children = window->GetChildren();
|
|
|
|
for (auto child : children) {
|
|
scale_controls_fonts(child, font_point_size);
|
|
scale_win_font(child, font_point_size);
|
|
}
|
|
|
|
window->Layout();
|
|
}
|
|
|
|
void rescale(const wxRect &suggested_rect)
|
|
{
|
|
this->Freeze();
|
|
|
|
// rescale fonts of all controls
|
|
scale_controls_fonts(this, m_new_font_point_size);
|
|
// rescale current window font
|
|
scale_win_font(this, m_new_font_point_size);
|
|
|
|
|
|
// set normal application font as a current window font
|
|
m_normal_font = this->GetFont();
|
|
|
|
// update em_unit value for new window font
|
|
m_em_unit = std::max<size_t>(10, this->GetTextExtent("m").x - 1);
|
|
|
|
// rescale missed controls sizes and images
|
|
on_dpi_changed(suggested_rect);
|
|
|
|
this->Layout();
|
|
this->Thaw();
|
|
|
|
// reset previous scale factor from current scale factor value
|
|
m_prev_scale_factor = m_scale_factor;
|
|
}
|
|
|
|
};
|
|
|
|
typedef DPIAware<wxFrame> DPIFrame;
|
|
typedef DPIAware<wxDialog> DPIDialog;
|
|
|
|
|
|
class EventGuard
|
|
{
|
|
// This is a RAII-style smart-ptr-like guard that will bind any event to any event handler
|
|
// and unbind it as soon as it goes out of scope or unbind() is called.
|
|
// This can be used to solve the annoying problem of wx events being delivered to freed objects.
|
|
|
|
private:
|
|
// This is a way to type-erase both the event type as well as the handler:
|
|
|
|
struct EventStorageBase {
|
|
virtual ~EventStorageBase() {}
|
|
};
|
|
|
|
template<class EvTag, class Fun>
|
|
struct EventStorageFun : EventStorageBase {
|
|
wxEvtHandler *emitter;
|
|
EvTag tag;
|
|
Fun fun;
|
|
|
|
EventStorageFun(wxEvtHandler *emitter, const EvTag &tag, Fun fun)
|
|
: emitter(emitter)
|
|
, tag(tag)
|
|
, fun(std::move(fun))
|
|
{
|
|
emitter->Bind(this->tag, this->fun);
|
|
}
|
|
|
|
virtual ~EventStorageFun() { emitter->Unbind(tag, fun); }
|
|
};
|
|
|
|
template<typename EvTag, typename Class, typename EvArg, typename EvHandler>
|
|
struct EventStorageMethod : EventStorageBase {
|
|
typedef void(Class::* MethodPtr)(EvArg &);
|
|
|
|
wxEvtHandler *emitter;
|
|
EvTag tag;
|
|
MethodPtr method;
|
|
EvHandler *handler;
|
|
|
|
EventStorageMethod(wxEvtHandler *emitter, const EvTag &tag, MethodPtr method, EvHandler *handler)
|
|
: emitter(emitter)
|
|
, tag(tag)
|
|
, method(method)
|
|
, handler(handler)
|
|
{
|
|
emitter->Bind(tag, method, handler);
|
|
}
|
|
|
|
virtual ~EventStorageMethod() { emitter->Unbind(tag, method, handler); }
|
|
};
|
|
|
|
std::unique_ptr<EventStorageBase> event_storage;
|
|
public:
|
|
EventGuard() {}
|
|
EventGuard(const EventGuard&) = delete;
|
|
EventGuard(EventGuard &&other) : event_storage(std::move(other.event_storage)) {}
|
|
|
|
template<class EvTag, class Fun>
|
|
EventGuard(wxEvtHandler *emitter, const EvTag &tag, Fun fun)
|
|
:event_storage(new EventStorageFun<EvTag, Fun>(emitter, tag, std::move(fun)))
|
|
{}
|
|
|
|
template<typename EvTag, typename Class, typename EvArg, typename EvHandler>
|
|
EventGuard(wxEvtHandler *emitter, const EvTag &tag, void(Class::* method)(EvArg &), EvHandler *handler)
|
|
:event_storage(new EventStorageMethod<EvTag, Class, EvArg, EvHandler>(emitter, tag, method, handler))
|
|
{}
|
|
|
|
EventGuard& operator=(const EventGuard&) = delete;
|
|
EventGuard& operator=(EventGuard &&other)
|
|
{
|
|
event_storage = std::move(other.event_storage);
|
|
return *this;
|
|
}
|
|
|
|
void unbind() { event_storage.reset(nullptr); }
|
|
explicit operator bool() const { return !!event_storage; }
|
|
};
|
|
|
|
|
|
class CheckboxFileDialog : public wxFileDialog
|
|
{
|
|
public:
|
|
CheckboxFileDialog(wxWindow *parent,
|
|
const wxString &checkbox_label,
|
|
bool checkbox_value,
|
|
const wxString &message = wxFileSelectorPromptStr,
|
|
const wxString &default_dir = wxEmptyString,
|
|
const wxString &default_file = wxEmptyString,
|
|
const wxString &wildcard = wxFileSelectorDefaultWildcardStr,
|
|
long style = wxFD_DEFAULT_STYLE,
|
|
const wxPoint &pos = wxDefaultPosition,
|
|
const wxSize &size = wxDefaultSize,
|
|
const wxString &name = wxFileDialogNameStr
|
|
);
|
|
|
|
bool get_checkbox_value() const;
|
|
|
|
private:
|
|
struct ExtraPanel : public wxPanel
|
|
{
|
|
wxCheckBox *cbox;
|
|
|
|
ExtraPanel(wxWindow *parent);
|
|
static wxWindow* ctor(wxWindow *parent);
|
|
};
|
|
|
|
wxString checkbox_label;
|
|
};
|
|
|
|
|
|
class WindowMetrics
|
|
{
|
|
private:
|
|
wxRect rect;
|
|
bool maximized;
|
|
|
|
WindowMetrics() : maximized(false) {}
|
|
public:
|
|
static WindowMetrics from_window(wxTopLevelWindow *window);
|
|
static boost::optional<WindowMetrics> deserialize(const std::string &str);
|
|
|
|
wxRect get_rect() const { return rect; }
|
|
bool get_maximized() const { return maximized; }
|
|
|
|
void sanitize_for_display(const wxRect &screen_rect);
|
|
std::string serialize() const;
|
|
};
|
|
|
|
std::ostream& operator<<(std::ostream &os, const WindowMetrics& metrics);
|
|
|
|
|
|
}}
|
|
|
|
#endif
|