Files
OrcaSlicer/src/slic3r/GUI/DeviceCore/DevUtil.h
T
NoisyfoxandClaude 63044b7661 feat: Add layout debugging/inspecting tool (#14919)
* Add wxInspector dep

* Initial intergration of wxInspector

* docs: add wxInspector plugins design spec

Design spec for two wxInspector plugins (DPIAware + CustomWidgets) that expose
OrcaSlicer's custom control properties in the inspector property grid.

Covers: DPIAware scale-factor properties, Button, CheckBox, TextInput,
SwitchButton, ProgressBar, Label, and LabeledStaticBox.

* docs: add wxInspector plugins implementation plan

6-task plan covering: source changes to existing widget headers,
DPIAwarePlugin, CustomWidgetsPlugin, registration helper,
MainFrame/CMake wiring, and build verification.

* feat: add getters/setters for wxInspector plugin access

Add minimal public accessors to DPIAware (set_scale_factor,
set_prev_scale_factor, set_em_unit, force_rescale), Button
(GetStyle, GetType, IsSelected), CheckBox (IsHalfChecked),
TextInput (GetCornerRadius), and LabeledStaticBox
(GetCornerRadius, GetBorderWidth, GetBorderColor, GetScale).

* feat: add wxInspector plugin registration helper

Add RegisterOrcaInspectorPlugins() inline function that creates
and registers the DPIAwarePlugin and CustomWidgetsPlugin as
static instances (matching wxInspector's built-in pattern).

* feat: add DPIAware wxInspector plugin

Exposes DPI scaling properties (scale_factor, prev_scale_factor,
em_unit, normal_font, force_rescale) on DPIFrame and DPIDialog
widgets. Uses dynamic_cast for detection and a template helper
to capture the correct static type for lambda accessors.

* feat: add OrcaCustomWidgets wxInspector plugin

Exposes Orca-specific properties on 7 widget types:
- Button: Style, Type, Selected
- CheckBox: Half Checked
- TextInput: Label, Text Value, Corner Radius
- SwitchButton: Value
- ProgressBar: Proportion, Show Number
- Label: Is Hyperlink, Font Point Size
- LabeledStaticBox: Corner Radius, Border Width, Border Color, Scale

Each widget type uses dynamic_cast for safe detection.

* feat: wire wxInspector plugins into MainFrame and build

Call RegisterOrcaInspectorPlugins() in MainFrame constructor after
SetupInspectorAccelerator(). Add all 5 plugin source files to
SLIC3R_GUI_SOURCES in CMakeLists.txt.

* fix: move plugin registration to GUI_App::on_init_inner

Register plugins once in app init rather than in MainFrame
constructor, which may be recreated during the application
lifetime.

* fix: include plugin headers in Registration.hpp for complete types

Static locals require complete type. Include DPIAwarePlugin.hpp and
CustomWidgetsPlugin.hpp instead of forward-declaring. Also remove
unused include from MainFrame.cpp (registration moved to GUI_App).

* fix: qualify DPIFrame/DPIDialog with Slic3r::GUI namespace

* Make DPIDialog inspectable. For other dialogs, we will add them if necessary later.

* docs: add spec for moving wxInspectable into DPIAware template

Move wxInspector::wxInspectable base class from DPIDialog and MainFrame
into the common DPIAware<P> template, making all DPIAware widgets
automatically visible in the inspector tree.

Co-Authored-By: Claude <noreply@anthropic.com>

* docs: add implementation plan for moving wxInspectable into DPIAware

Co-Authored-By: Claude <noreply@anthropic.com>

* docs: update spec/plan — move SetupInspectorAccelerator into DPIAware too

Co-Authored-By: Claude <noreply@anthropic.com>

* refactor: move wxInspectable and SetupInspectorAccelerator into DPIAware

DPIAware<P> now inherits wxInspector::wxInspectable and calls
SetupInspectorAccelerator in its constructor, making all DPIAware
widgets automatically appear in the inspector tree with the
Ctrl+Shift+I shortcut. DPIDialog now uses 'using' to inherit the
constructor. Remove redundant wxInspectable inheritance and
SetupInspectorAccelerator calls from DPIDialog and MainFrame.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: use LB_HYPERLINK constant instead of magic number 0x0020

Co-Authored-By: Claude <noreply@anthropic.com>

* Clean up

* Fix Linux build

* Don't build wxInspector sample

* Use shallow clone

* Try fix flatpak build

* Attempt to fix build again

* Fix build failure caused by https://github.com/wxWidgets/wxWidgets/commit/436c16135ec7ddf580f44624bf74c592aae43b66

* wxWidgets build only download required submodules

* This should fix build on Windows on ARM

* Enable PIC

* Disable layout inspector by default for public release

* Use wxInspector 1.0.0 release

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-26 20:25:25 +08:00

213 lines
5.2 KiB
C++

/**
* @file DevUtil.h
* @brief Provides common static utility methods for general use.
*
* This class offers a collection of static helper functions such as string manipulation,
* file operations, and other frequently used utilities.
*/
#pragma once
#include <string>
#include <sstream>
#include <stdexcept>
#include <boost/log/trivial.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/format.hpp>
#include "nlohmann/json.hpp"
/* Sequence Id*/
#define STUDIO_START_SEQ_ID 20000
#define STUDIO_END_SEQ_ID 30000
#define CLOUD_SEQ_ID 0
namespace Slic3r
{
class DevUtil
{
public:
DevUtil() = delete;
DevUtil(const DevUtil&) = delete;
DevUtil& operator=(const DevUtil&) = delete;
public:
static int get_flag_bits(std::string str, int start, int count = 1);
static int get_flag_bits(int num, int start, int count = 1, int base = 10);
static uint32_t get_flag_bits_no_border(std::string str, int start_idx, int count = 1);
// eg. get_hex_bits(16, 1, 10) = 1
static int get_hex_bits(int num, int pos, int input_num_base = 10) { return get_flag_bits(num, pos * 4, 4, input_num_base);};
static float string_to_float(const std::string& str_value);
static std::string convertToIp(long long ip);
// sequence id check
static bool is_studio_cmd(int seq) { return seq >= STUDIO_START_SEQ_ID && seq < STUDIO_END_SEQ_ID;};
static bool is_cloud_cmd(int seq) { return seq == CLOUD_SEQ_ID;};
};
class DevJsonValParser
{
public:
template<typename T>
static T GetVal(const nlohmann::json& j, const std::string& key, const T& default_val = T())
{
try
{
if (j.contains(key)) { return j[key].get<T>(); }
}
catch (const nlohmann::json::exception& e)
{
assert(0 && __FUNCTION__);
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": " << e.what();
}
return default_val;
}
template<typename T>
static void ParseVal(const nlohmann::json& j, const std::string& key, T& val)
{
try
{
if (j.contains(key)) { val = j[key].get<T>(); }
}
catch (const nlohmann::json::exception& e)
{
assert(0 && __FUNCTION__);
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": " << e.what();
}
}
template<typename T>
static void ParseVal(const nlohmann::json& j, const std::string& key, T& val, T default_val)
{
try
{
j.contains(key) ? (val = j[key].get<T>()) : (val = default_val);
}
catch (const nlohmann::json::exception& e)
{
assert(0 && __FUNCTION__);
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": " << e.what();
}
}
public:
static std::string get_longlong_val(const nlohmann::json& j);
};
struct NumericStrCompare
{
bool operator()(const std::string& a, const std::string& b) const noexcept
{
int ai = -1;
try {
ai = std::stoi(a);
} catch (...) { };
int bi = -1;
try {
bi = std::stoi(b);
} catch (...) { };
return ai < bi;
}
};
enum class DirtyMode{
COUNTER,
TIMER
};
template<typename T>
class DevDirtyHandler{
public:
DevDirtyHandler(T init_value, int setting_threshold, DirtyMode mode): m_value(init_value), m_setting_threshold(setting_threshold), m_mode(mode)
{
m_threshold = setting_threshold;
}
~DevDirtyHandler(){};
T GetValue() const { return m_value; };
void SetOptimisticValue(const T& data)
{
m_value = data;
m_start_time = time(nullptr);
m_threshold = m_setting_threshold;
}
void UpdateValue(const T& data)
{
if (m_mode == DirtyMode::COUNTER)
{
if (m_threshold > 0)
m_threshold--;
else
m_value = data;
}
else if (m_mode == DirtyMode::TIMER)
{
if (time(nullptr) - m_start_time > m_threshold)
m_value = data;
}
}
private:
T m_value;
DirtyMode m_mode;
int m_setting_threshold{0};
int m_start_time{0};
int m_threshold{0};
};
static std::string s_get_diameter_str(float diameter)
{
return (boost::format("%.2f") % diameter).str();
}
static std::string s_get_diameter_str(const std::string& diameter)
{
try {
float dia = boost::lexical_cast<float>(diameter);
return s_get_diameter_str(dia);
} catch (...) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " failed to boost::lexical_cast: " << diameter;
return diameter;
}
try {
float dia = std::stof(diameter);
return s_get_diameter_str(dia);
} catch (...) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " std::stof: " << diameter;
return diameter;
}
}
static float s_get_diameter(const std::string& diameter)
{
try {
return boost::lexical_cast<float>(diameter);
} catch (...) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " failed to boost::lexical_cast: " << diameter;
}
try {
return std::stof(diameter);
} catch (...) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " std::stof: " << diameter;
}
return 0.0;
}
}; // namespace Slic3r