408 lines
12 KiB
C++
408 lines
12 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;
|
|
|
|
#if ENABLE_WX_3_1_3_DPI_CHANGED_EVENT
|
|
#define wxVERSION_EQUAL_OR_GREATER_THAN(major, minor, release) ((wxMAJOR_VERSION > major) || ((wxMAJOR_VERSION == major) && (wxMINOR_VERSION > minor)) || ((wxMAJOR_VERSION == major) && (wxMINOR_VERSION == minor) && (wxRELEASE_NUMBER >= release)))
|
|
#else
|
|
#define wxVERSION_EQUAL_OR_GREATER_THAN(major, minor, release) 0
|
|
#endif // ENABLE_WX_3_1_3_DPI_CHANGED_EVENT
|
|
|
|
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);
|
|
|
|
#if !wxVERSION_EQUAL_OR_GREATER_THAN(3,1,3)
|
|
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);
|
|
#endif // !wxVERSION_EQUAL_OR_GREATER_THAN
|
|
|
|
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.
|
|
#if ENABLE_WX_3_1_3_DPI_CHANGED_EVENT
|
|
m_em_unit = std::max<size_t>(10, 10.0f * m_scale_factor);
|
|
#else
|
|
m_em_unit = std::max<size_t>(10, this->GetTextExtent("m").x - 1);
|
|
#endif // ENABLE_WX_3_1_3_DPI_CHANGED_EVENT
|
|
|
|
// recalc_font();
|
|
|
|
#if wxVERSION_EQUAL_OR_GREATER_THAN(3,1,3)
|
|
this->Bind(wxEVT_DPI_CHANGED, [this](wxDPIChangedEvent& evt) {
|
|
m_scale_factor = (float)evt.GetNewDPI().x / (float)DPI_DEFAULT;
|
|
|
|
m_new_font_point_size = get_default_font_for_dpi(evt.GetNewDPI().x).GetPointSize();
|
|
|
|
if (!m_can_rescale)
|
|
return;
|
|
|
|
#if ENABLE_LAYOUT_NO_RESTART
|
|
if (m_force_rescale || is_new_scale_factor())
|
|
rescale(wxRect());
|
|
#else
|
|
if (is_new_scale_factor())
|
|
rescale(wxRect());
|
|
#endif // ENABLE_LAYOUT_NO_RESTART
|
|
});
|
|
#else
|
|
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 ENABLE_LAYOUT_NO_RESTART
|
|
if (m_force_rescale || is_new_scale_factor())
|
|
rescale(evt.rect);
|
|
#else
|
|
if (is_new_scale_factor())
|
|
rescale(evt.rect);
|
|
#endif // ENABLE_LAYOUT_NO_RESTART
|
|
});
|
|
#endif // wxVERSION_EQUAL_OR_GREATER_THAN
|
|
|
|
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;
|
|
});
|
|
|
|
this->Bind(wxEVT_SYS_COLOUR_CHANGED, [this](wxSysColourChangedEvent& event)
|
|
{
|
|
event.Skip();
|
|
on_sys_color_changed();
|
|
});
|
|
}
|
|
|
|
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; }
|
|
#if ENABLE_LAYOUT_NO_RESTART
|
|
void enable_force_rescale() { m_force_rescale = true; }
|
|
#endif // ENABLE_LAYOUT_NO_RESTART
|
|
|
|
protected:
|
|
virtual void on_dpi_changed(const wxRect &suggested_rect) = 0;
|
|
virtual void on_sys_color_changed() {};
|
|
|
|
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 };
|
|
#if ENABLE_LAYOUT_NO_RESTART
|
|
bool m_force_rescale{ false };
|
|
#endif // ENABLE_LAYOUT_NO_RESTART
|
|
|
|
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();
|
|
|
|
#if ENABLE_LAYOUT_NO_RESTART && wxVERSION_EQUAL_OR_GREATER_THAN(3,1,3)
|
|
if (m_force_rescale) {
|
|
#endif // ENABLE_LAYOUT_NO_RESTART
|
|
// 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);
|
|
#if ENABLE_LAYOUT_NO_RESTART && wxVERSION_EQUAL_OR_GREATER_THAN(3,1,3)
|
|
m_force_rescale = false;
|
|
}
|
|
#endif // ENABLE_LAYOUT_NO_RESTART
|
|
|
|
// set normal application font as a current window font
|
|
m_normal_font = this->GetFont();
|
|
|
|
// update em_unit value for new window font
|
|
#if ENABLE_WX_3_1_3_DPI_CHANGED_EVENT
|
|
m_em_unit = std::max<int>(10, 10.0f * m_scale_factor);
|
|
#else
|
|
m_em_unit = std::max<size_t>(10, this->GetTextExtent("m").x - 1);
|
|
#endif // ENABLE_WX_3_1_3_DPI_CHANGED_EVENT
|
|
|
|
// 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);
|
|
|
|
const 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
|