mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-02 13:20:59 +00:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a493c3c80f | ||
|
|
6842d9c778 | ||
|
|
1a5bc8982d |
@@ -55,8 +55,6 @@ public:
|
|||||||
static bool parse_color(const std::string& scolor, unsigned char* rgb_out);
|
static bool parse_color(const std::string& scolor, unsigned char* rgb_out);
|
||||||
static bool parse_color4(const std::string& scolor, unsigned char* rgba_out);
|
static bool parse_color4(const std::string& scolor, unsigned char* rgba_out);
|
||||||
|
|
||||||
// Rasterizes the SVG into a freshly generated GL texture; the caller owns it and has to delete
|
|
||||||
// it (ImGuiWrapper::svg_texture() caches the result for the whole session).
|
|
||||||
static bool load_from_svg_file_change_color(const std::string &filename, unsigned width, unsigned height, ImTextureID &texture_id, const char *hexColor);
|
static bool load_from_svg_file_change_color(const std::string &filename, unsigned width, unsigned height, ImTextureID &texture_id, const char *hexColor);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3245,8 +3245,7 @@ void GLCanvas3D::load_sla_preview()
|
|||||||
|
|
||||||
void GLCanvas3D::bind_event_handlers()
|
void GLCanvas3D::bind_event_handlers()
|
||||||
{
|
{
|
||||||
// Every view switch binds, so binding twice would run each handler twice per event.
|
if (m_canvas != nullptr) {
|
||||||
if (m_canvas != nullptr && !m_event_handlers_bound) {
|
|
||||||
m_canvas->Bind(wxEVT_SIZE, &GLCanvas3D::on_size, this);
|
m_canvas->Bind(wxEVT_SIZE, &GLCanvas3D::on_size, this);
|
||||||
m_canvas->Bind(wxEVT_IDLE, &GLCanvas3D::on_idle, this);
|
m_canvas->Bind(wxEVT_IDLE, &GLCanvas3D::on_idle, this);
|
||||||
m_canvas->Bind(wxEVT_CHAR, &GLCanvas3D::on_char, this);
|
m_canvas->Bind(wxEVT_CHAR, &GLCanvas3D::on_char, this);
|
||||||
@@ -3256,9 +3255,9 @@ void GLCanvas3D::bind_event_handlers()
|
|||||||
m_canvas->Bind(wxEVT_TIMER, &GLCanvas3D::on_timer, this);
|
m_canvas->Bind(wxEVT_TIMER, &GLCanvas3D::on_timer, this);
|
||||||
m_canvas->Bind(EVT_GLCANVAS_RENDER_TIMER, &GLCanvas3D::on_render_timer, this);
|
m_canvas->Bind(EVT_GLCANVAS_RENDER_TIMER, &GLCanvas3D::on_render_timer, this);
|
||||||
m_toolbar_highlighter.set_timer_owner(m_canvas, 0);
|
m_toolbar_highlighter.set_timer_owner(m_canvas, 0);
|
||||||
m_canvas->Bind(EVT_GLCANVAS_TOOLBAR_HIGHLIGHTER_TIMER, &GLCanvas3D::on_toolbar_highlighter_timer, this);
|
m_canvas->Bind(EVT_GLCANVAS_TOOLBAR_HIGHLIGHTER_TIMER, [this](wxTimerEvent&) { m_toolbar_highlighter.blink(); });
|
||||||
m_gizmo_highlighter.set_timer_owner(m_canvas, 0);
|
m_gizmo_highlighter.set_timer_owner(m_canvas, 0);
|
||||||
m_canvas->Bind(EVT_GLCANVAS_GIZMO_HIGHLIGHTER_TIMER, &GLCanvas3D::on_gizmo_highlighter_timer, this);
|
m_canvas->Bind(EVT_GLCANVAS_GIZMO_HIGHLIGHTER_TIMER, [this](wxTimerEvent&) { m_gizmo_highlighter.blink(); });
|
||||||
m_canvas->Bind(wxEVT_LEFT_DOWN, &GLCanvas3D::on_mouse, this);
|
m_canvas->Bind(wxEVT_LEFT_DOWN, &GLCanvas3D::on_mouse, this);
|
||||||
m_canvas->Bind(wxEVT_LEFT_UP, &GLCanvas3D::on_mouse, this);
|
m_canvas->Bind(wxEVT_LEFT_UP, &GLCanvas3D::on_mouse, this);
|
||||||
m_canvas->Bind(wxEVT_MIDDLE_DOWN, &GLCanvas3D::on_mouse, this);
|
m_canvas->Bind(wxEVT_MIDDLE_DOWN, &GLCanvas3D::on_mouse, this);
|
||||||
@@ -3273,7 +3272,14 @@ void GLCanvas3D::bind_event_handlers()
|
|||||||
m_canvas->Bind(wxEVT_RIGHT_DCLICK, &GLCanvas3D::on_mouse, this);
|
m_canvas->Bind(wxEVT_RIGHT_DCLICK, &GLCanvas3D::on_mouse, this);
|
||||||
m_canvas->Bind(wxEVT_PAINT, &GLCanvas3D::on_paint, this);
|
m_canvas->Bind(wxEVT_PAINT, &GLCanvas3D::on_paint, this);
|
||||||
m_canvas->Bind(wxEVT_SET_FOCUS, &GLCanvas3D::on_set_focus, this);
|
m_canvas->Bind(wxEVT_SET_FOCUS, &GLCanvas3D::on_set_focus, this);
|
||||||
m_canvas->Bind(wxEVT_KILL_FOCUS, &GLCanvas3D::on_kill_focus, this);
|
m_canvas->Bind(wxEVT_KILL_FOCUS, [this](wxFocusEvent& evt) {
|
||||||
|
// The key-up that would commit a keyboard edit goes to whatever took the focus.
|
||||||
|
if (m_selection_edit.kind != SelectionEdit::None)
|
||||||
|
finish_selection_edit();
|
||||||
|
ImGui::SetWindowFocus(nullptr);
|
||||||
|
render();
|
||||||
|
evt.Skip();
|
||||||
|
});
|
||||||
m_event_handlers_bound = true;
|
m_event_handlers_bound = true;
|
||||||
|
|
||||||
m_canvas->Bind(wxEVT_GESTURE_PAN, &GLCanvas3D::on_gesture, this);
|
m_canvas->Bind(wxEVT_GESTURE_PAN, &GLCanvas3D::on_gesture, this);
|
||||||
@@ -3311,9 +3317,6 @@ void GLCanvas3D::unbind_event_handlers()
|
|||||||
m_canvas->Unbind(wxEVT_RIGHT_DCLICK, &GLCanvas3D::on_mouse, this);
|
m_canvas->Unbind(wxEVT_RIGHT_DCLICK, &GLCanvas3D::on_mouse, this);
|
||||||
m_canvas->Unbind(wxEVT_PAINT, &GLCanvas3D::on_paint, this);
|
m_canvas->Unbind(wxEVT_PAINT, &GLCanvas3D::on_paint, this);
|
||||||
m_canvas->Unbind(wxEVT_SET_FOCUS, &GLCanvas3D::on_set_focus, this);
|
m_canvas->Unbind(wxEVT_SET_FOCUS, &GLCanvas3D::on_set_focus, this);
|
||||||
m_canvas->Unbind(wxEVT_KILL_FOCUS, &GLCanvas3D::on_kill_focus, this);
|
|
||||||
m_canvas->Unbind(EVT_GLCANVAS_TOOLBAR_HIGHLIGHTER_TIMER, &GLCanvas3D::on_toolbar_highlighter_timer, this);
|
|
||||||
m_canvas->Unbind(EVT_GLCANVAS_GIZMO_HIGHLIGHTER_TIMER, &GLCanvas3D::on_gizmo_highlighter_timer, this);
|
|
||||||
m_event_handlers_bound = false;
|
m_event_handlers_bound = false;
|
||||||
|
|
||||||
m_canvas->Unbind(wxEVT_GESTURE_PAN, &GLCanvas3D::on_gesture, this);
|
m_canvas->Unbind(wxEVT_GESTURE_PAN, &GLCanvas3D::on_gesture, this);
|
||||||
@@ -4890,26 +4893,6 @@ void GLCanvas3D::on_set_focus(wxFocusEvent& evt)
|
|||||||
m_is_touchpad_navigation = wxGetApp().app_config->get_bool("camera_navigation_style");
|
m_is_touchpad_navigation = wxGetApp().app_config->get_bool("camera_navigation_style");
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLCanvas3D::on_kill_focus(wxFocusEvent& evt)
|
|
||||||
{
|
|
||||||
// The key-up that would commit a keyboard edit goes to whatever took the focus.
|
|
||||||
if (m_selection_edit.kind != SelectionEdit::None)
|
|
||||||
finish_selection_edit();
|
|
||||||
ImGui::SetWindowFocus(nullptr);
|
|
||||||
render();
|
|
||||||
evt.Skip();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GLCanvas3D::on_toolbar_highlighter_timer(wxTimerEvent& evt)
|
|
||||||
{
|
|
||||||
m_toolbar_highlighter.blink();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GLCanvas3D::on_gizmo_highlighter_timer(wxTimerEvent& evt)
|
|
||||||
{
|
|
||||||
m_gizmo_highlighter.blink();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GLCanvas3D::clicked_button_matches_action(const wxMouseEvent& evt, const MouseAction action, const std::map<MouseButton, MouseAction>& mappings) const
|
bool GLCanvas3D::clicked_button_matches_action(const wxMouseEvent& evt, const MouseAction action, const std::map<MouseButton, MouseAction>& mappings) const
|
||||||
{
|
{
|
||||||
MouseButton clicked = MouseButton::None;
|
MouseButton clicked = MouseButton::None;
|
||||||
|
|||||||
@@ -1133,9 +1133,6 @@ public:
|
|||||||
void on_gesture(wxGestureEvent& evt);
|
void on_gesture(wxGestureEvent& evt);
|
||||||
void on_paint(wxPaintEvent& evt);
|
void on_paint(wxPaintEvent& evt);
|
||||||
void on_set_focus(wxFocusEvent& evt);
|
void on_set_focus(wxFocusEvent& evt);
|
||||||
void on_kill_focus(wxFocusEvent& evt);
|
|
||||||
void on_toolbar_highlighter_timer(wxTimerEvent& evt);
|
|
||||||
void on_gizmo_highlighter_timer(wxTimerEvent& evt);
|
|
||||||
void force_set_focus();
|
void force_set_focus();
|
||||||
|
|
||||||
enum class MouseButton { None, Left, Middle, Right };
|
enum class MouseButton { None, Left, Middle, Right };
|
||||||
|
|||||||
@@ -2057,15 +2057,9 @@ wxMenu* MenuFactory::instance_menu()
|
|||||||
return &m_instance_menu;
|
return &m_instance_menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
MenuWithSeparators* MenuFactory::new_transient_menu()
|
|
||||||
{
|
|
||||||
m_transient_menu = std::make_unique<MenuWithSeparators>();
|
|
||||||
return m_transient_menu.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
wxMenu* MenuFactory::layer_menu()
|
wxMenu* MenuFactory::layer_menu()
|
||||||
{
|
{
|
||||||
MenuWithSeparators* menu = new_transient_menu();
|
MenuWithSeparators* menu = new MenuWithSeparators();
|
||||||
append_menu_item_settings(menu);
|
append_menu_item_settings(menu);
|
||||||
|
|
||||||
return menu;
|
return menu;
|
||||||
@@ -2091,13 +2085,13 @@ wxMenu* MenuFactory::multi_selection_menu()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (all_plates) {
|
if (all_plates) {
|
||||||
wxMenu* menu = new_transient_menu();
|
wxMenu* menu = new MenuWithSeparators();
|
||||||
append_menu_item_replace_all_with_stl(menu);
|
append_menu_item_replace_all_with_stl(menu);
|
||||||
return menu;
|
return menu;
|
||||||
}
|
}
|
||||||
if (undefined_type)
|
if (undefined_type)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
wxMenu* menu = new_transient_menu();
|
wxMenu* menu = new MenuWithSeparators();
|
||||||
if (!multi_volume) {
|
if (!multi_volume) {
|
||||||
int index = 0;
|
int index = 0;
|
||||||
if (obj_list()->can_merge_to_multipart_object()) {
|
if (obj_list()->can_merge_to_multipart_object()) {
|
||||||
@@ -2171,7 +2165,7 @@ wxMenu* MenuFactory::assemble_multi_selection_menu()
|
|||||||
// show this menu only for Objects(Instances mixed with Objects)/Volumes selection
|
// show this menu only for Objects(Instances mixed with Objects)/Volumes selection
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
wxMenu* menu = new_transient_menu();
|
wxMenu* menu = new MenuWithSeparators();
|
||||||
append_menu_item_set_visible(menu);
|
append_menu_item_set_visible(menu);
|
||||||
//append_menu_item_fix_through_cgal(menu);
|
//append_menu_item_fix_through_cgal(menu);
|
||||||
//append_menu_item_simplify(menu);
|
//append_menu_item_simplify(menu);
|
||||||
@@ -2217,7 +2211,7 @@ wxMenu* MenuFactory::plate_menu()
|
|||||||
|
|
||||||
wxMenu* MenuFactory::assemble_object_menu()
|
wxMenu* MenuFactory::assemble_object_menu()
|
||||||
{
|
{
|
||||||
wxMenu* menu = new_transient_menu();
|
wxMenu* menu = new MenuWithSeparators();
|
||||||
// Set Visible
|
// Set Visible
|
||||||
append_menu_item_set_visible(menu);
|
append_menu_item_set_visible(menu);
|
||||||
// Delete
|
// Delete
|
||||||
@@ -2237,7 +2231,7 @@ wxMenu* MenuFactory::assemble_object_menu()
|
|||||||
|
|
||||||
wxMenu* MenuFactory::assemble_part_menu()
|
wxMenu* MenuFactory::assemble_part_menu()
|
||||||
{
|
{
|
||||||
wxMenu* menu = new_transient_menu();
|
wxMenu* menu = new MenuWithSeparators();
|
||||||
|
|
||||||
append_menu_item_set_visible(menu);
|
append_menu_item_set_visible(menu);
|
||||||
append_menu_item_delete(menu);
|
append_menu_item_delete(menu);
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
#define slic3r_GUI_Factories_hpp_
|
#define slic3r_GUI_Factories_hpp_
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
@@ -121,12 +120,6 @@ private:
|
|||||||
MenuWithSeparators m_assemble_part_menu;
|
MenuWithSeparators m_assemble_part_menu;
|
||||||
|
|
||||||
wxMenu m_filament_action_menu;
|
wxMenu m_filament_action_menu;
|
||||||
|
|
||||||
// The selection dependent menus are rebuilt for every popup, so they cannot be members that
|
|
||||||
// outlive a build like the ones above; this owns the current one and destroys the previous.
|
|
||||||
// One slot is enough because PopupMenu() is synchronous: the menu a caller was handed is gone
|
|
||||||
// from the screen before anything can ask for the next one.
|
|
||||||
std::unique_ptr<MenuWithSeparators> m_transient_menu;
|
|
||||||
|
|
||||||
|
|
||||||
// Removed/Prepended Items according to the view mode
|
// Removed/Prepended Items according to the view mode
|
||||||
@@ -134,9 +127,6 @@ private:
|
|||||||
std::array<wxMenuItem*, mtCount> items_decrease;
|
std::array<wxMenuItem*, mtCount> items_decrease;
|
||||||
std::array<wxMenuItem*, mtCount> items_set_number_of_copies;
|
std::array<wxMenuItem*, mtCount> items_set_number_of_copies;
|
||||||
|
|
||||||
// Replaces m_transient_menu with an empty menu and returns it.
|
|
||||||
MenuWithSeparators* new_transient_menu();
|
|
||||||
|
|
||||||
void create_default_menu();
|
void create_default_menu();
|
||||||
void create_common_object_menu(wxMenu *menu);
|
void create_common_object_menu(wxMenu *menu);
|
||||||
void create_object_menu();
|
void create_object_menu();
|
||||||
|
|||||||
@@ -2272,10 +2272,6 @@ void GLGizmoMeasure::update_measurement_result()
|
|||||||
void GLGizmoMeasure::reset_all_pick()
|
void GLGizmoMeasure::reset_all_pick()
|
||||||
{
|
{
|
||||||
std::map<GLVolume*, std::shared_ptr<PickRaycaster>>().swap(m_mesh_raycaster_map);
|
std::map<GLVolume*, std::shared_ptr<PickRaycaster>>().swap(m_mesh_raycaster_map);
|
||||||
// register_single_mesh_pick() fills both maps in lockstep, so the measurings have to go with
|
|
||||||
// the raycasters; otherwise the entries keyed on the GLVolumes of the previous selection stay
|
|
||||||
// behind for the rest of the session.
|
|
||||||
std::map<GLVolume*, std::shared_ptr<Measure::Measuring>>().swap(m_mesh_measure_map);
|
|
||||||
reset_gripper_pick(GripperType::UNDEFINE,true);
|
reset_gripper_pick(GripperType::UNDEFINE,true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ enum class PainterGizmoType {
|
|||||||
FDM_SUPPORTS,
|
FDM_SUPPORTS,
|
||||||
SEAM,
|
SEAM,
|
||||||
MM_SEGMENTATION,
|
MM_SEGMENTATION,
|
||||||
FUZZY_SKIN
|
FUZZY_SKIN,
|
||||||
|
TEXTURE_DISPLACEMENT
|
||||||
};
|
};
|
||||||
|
|
||||||
class TriangleSelectorGUI : public TriangleSelector {
|
class TriangleSelectorGUI : public TriangleSelector {
|
||||||
@@ -101,9 +102,7 @@ class TriangleSelectorPatch : public TriangleSelectorGUI {
|
|||||||
public:
|
public:
|
||||||
explicit TriangleSelectorPatch(const TriangleMesh& mesh, const std::vector<ColorRGBA> ebt_colors, float edge_limit = 0.6f)
|
explicit TriangleSelectorPatch(const TriangleMesh& mesh, const std::vector<ColorRGBA> ebt_colors, float edge_limit = 0.6f)
|
||||||
: TriangleSelectorGUI(mesh, edge_limit), m_ebt_colors(ebt_colors) {}
|
: TriangleSelectorGUI(mesh, edge_limit), m_ebt_colors(ebt_colors) {}
|
||||||
// Releases the VAO and the per-patch VBOs built by finalize_triangle_indices(). The base class
|
virtual ~TriangleSelectorPatch() = default;
|
||||||
// already deletes GL buffers from its GLModel members here, so this needs no context of its own.
|
|
||||||
virtual ~TriangleSelectorPatch() { release_geometry(); }
|
|
||||||
|
|
||||||
// Render current selection. Transformation matrices are supposed
|
// Render current selection. Transformation matrices are supposed
|
||||||
// to be already set.
|
// to be already set.
|
||||||
|
|||||||
@@ -361,7 +361,6 @@ ImGuiWrapper::~ImGuiWrapper()
|
|||||||
{
|
{
|
||||||
//destroy_fonts_texture();
|
//destroy_fonts_texture();
|
||||||
destroy_font();
|
destroy_font();
|
||||||
destroy_svg_textures();
|
|
||||||
ImGui::DestroyContext();
|
ImGui::DestroyContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -543,12 +542,6 @@ bool ImGuiWrapper::update_key_data(wxKeyEvent &evt)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SVG icons rasterized into GL textures, keyed on file name, size and recolor. Cleared as a whole
|
|
||||||
// from new_frame() once it grows past MAX_SVG_TEXTURES, which is safe there: the previous frame has
|
|
||||||
// been rendered and the frame about to be recorded asks for every icon it draws again.
|
|
||||||
static std::map<std::string, ImTextureID> s_svg_textures;
|
|
||||||
static const size_t MAX_SVG_TEXTURES = 256;
|
|
||||||
|
|
||||||
void ImGuiWrapper::new_frame()
|
void ImGuiWrapper::new_frame()
|
||||||
{
|
{
|
||||||
if (m_new_frame_open) {
|
if (m_new_frame_open) {
|
||||||
@@ -559,11 +552,6 @@ void ImGuiWrapper::new_frame()
|
|||||||
init_font(true);
|
init_font(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recolored icons accumulate one texture per color the session has shown; drop them before
|
|
||||||
// anything references them again. This frame recreates the handful it actually draws.
|
|
||||||
if (s_svg_textures.size() > MAX_SVG_TEXTURES)
|
|
||||||
destroy_svg_textures();
|
|
||||||
|
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
|
||||||
ImGui::NewFrame();
|
ImGui::NewFrame();
|
||||||
@@ -1816,7 +1804,8 @@ bool menu_item_with_icon(const char *label, const char *shortcut, ImVec2 icon_si
|
|||||||
if (icon_color != 0)
|
if (icon_color != 0)
|
||||||
ImGui::RenderFrame(icon_pos, icon_pos + icon_size, icon_color);
|
ImGui::RenderFrame(icon_pos, icon_pos + icon_size, icon_color);
|
||||||
else {
|
else {
|
||||||
ImTextureID transparent = ImGuiWrapper::svg_texture(Slic3r::resources_dir() + "/images/transparent.svg", icon_size.x, icon_size.y);
|
static ImTextureID transparent;
|
||||||
|
IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/transparent.svg", icon_size.x, icon_size.y, transparent);
|
||||||
window->DrawList->AddImage(transparent, icon_pos, icon_pos + icon_size, { 0,0 }, { 1,1 }, ImGui::GetColorU32(ImVec4(1.f, 1.f, 1.f, 1.f)));
|
window->DrawList->AddImage(transparent, icon_pos, icon_pos + icon_size, { 0,0 }, { 1,1 }, ImGui::GetColorU32(ImVec4(1.f, 1.f, 1.f, 1.f)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2642,7 +2631,11 @@ void ImGuiWrapper::push_toolbar_style(const float scale)
|
|||||||
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(238 / 255.0f, 238 / 255.0f, 238 / 255.0f, 1.00f)); // 10
|
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(238 / 255.0f, 238 / 255.0f, 238 / 255.0f, 1.00f)); // 10
|
||||||
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(238 / 255.0f, 238 / 255.0f, 238 / 255.0f, 0.00f)); // 11
|
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(238 / 255.0f, 238 / 255.0f, 238 / 255.0f, 0.00f)); // 11
|
||||||
ImGui::PushStyleColor(ImGuiCol_TextSelectedBg, COL_GREEN_LIGHT); // 12
|
ImGui::PushStyleColor(ImGuiCol_TextSelectedBg, COL_GREEN_LIGHT); // 12
|
||||||
ImGui::PushStyleColor(ImGuiCol_CheckMark, ImVec4(1.00f, 1.00f, 1.00f, 1.00f));//13
|
// The checkbox/radio frame behind this is drawn fully transparent (see FrameBg above,
|
||||||
|
// alpha 0), showing the light window background through it - a white check mark there is
|
||||||
|
// invisible. Dark mode doesn't have this problem (its window background is dark), so only
|
||||||
|
// this branch needs a check mark color with real contrast against a light background.
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_CheckMark, ImVec4(0.f, 156 / 255.f, 136 / 255.f, 1.00f));//13
|
||||||
ImGui::PushStyleColor(ImGuiCol_ScrollbarGrab, ImVec4(0.42f, 0.42f, 0.42f, 1.00f));
|
ImGui::PushStyleColor(ImGuiCol_ScrollbarGrab, ImVec4(0.42f, 0.42f, 0.42f, 1.00f));
|
||||||
ImGui::PushStyleColor(ImGuiCol_ScrollbarGrabHovered, ImVec4(0.93f, 0.93f, 0.93f, 1.00f));
|
ImGui::PushStyleColor(ImGuiCol_ScrollbarGrabHovered, ImVec4(0.93f, 0.93f, 0.93f, 1.00f));
|
||||||
ImGui::PushStyleColor(ImGuiCol_ScrollbarGrabActive, ImVec4(0.93f, 0.93f, 0.93f, 1.00f));
|
ImGui::PushStyleColor(ImGuiCol_ScrollbarGrabActive, ImVec4(0.93f, 0.93f, 0.93f, 1.00f));
|
||||||
@@ -3396,36 +3389,6 @@ bool ImGuiWrapper::display_initialized() const
|
|||||||
return io.DisplaySize.x >= 0.0f && io.DisplaySize.y >= 0.0f;
|
return io.DisplaySize.x >= 0.0f && io.DisplaySize.y >= 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImTextureID ImGuiWrapper::svg_texture(const std::string& filename, unsigned width, unsigned height, const char* hex_color)
|
|
||||||
{
|
|
||||||
std::string key = filename + "|" + std::to_string(width) + "x" + std::to_string(height);
|
|
||||||
if (hex_color != nullptr)
|
|
||||||
key += std::string("|") + hex_color;
|
|
||||||
|
|
||||||
const auto it = s_svg_textures.find(key);
|
|
||||||
if (it != s_svg_textures.end())
|
|
||||||
return it->second;
|
|
||||||
|
|
||||||
ImTextureID texture_id = nullptr;
|
|
||||||
const bool loaded = (hex_color != nullptr) ?
|
|
||||||
BitmapCache::load_from_svg_file_change_color(filename, width, height, texture_id, hex_color) :
|
|
||||||
IMTexture::load_from_svg_file(filename, width, height, texture_id);
|
|
||||||
if (!loaded)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
s_svg_textures.emplace(std::move(key), texture_id);
|
|
||||||
return texture_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ImGuiWrapper::destroy_svg_textures()
|
|
||||||
{
|
|
||||||
for (const auto& texture : s_svg_textures) {
|
|
||||||
GLuint texture_id = (GLuint)(intptr_t)texture.second;
|
|
||||||
glsafe(::glDeleteTextures(1, &texture_id));
|
|
||||||
}
|
|
||||||
s_svg_textures.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ImGuiWrapper::destroy_font()
|
void ImGuiWrapper::destroy_font()
|
||||||
{
|
{
|
||||||
if (m_font_texture != 0) {
|
if (m_font_texture != 0) {
|
||||||
@@ -3505,6 +3468,7 @@ void ImGuiWrapper::filament_group(const std::string& filament_type, const char*
|
|||||||
//ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
|
//ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
|
||||||
std::string id = std::to_string(static_cast<unsigned int> (filament_id + 1));
|
std::string id = std::to_string(static_cast<unsigned int> (filament_id + 1));
|
||||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||||
|
static ImTextureID transparent;
|
||||||
ImVec2 text_size = ImGui::CalcTextSize(filament_type.c_str());
|
ImVec2 text_size = ImGui::CalcTextSize(filament_type.c_str());
|
||||||
// BBS image sizing based on text width (DPI scaling)
|
// BBS image sizing based on text width (DPI scaling)
|
||||||
float img_width = ImGui::CalcTextSize("ABC").x;
|
float img_width = ImGui::CalcTextSize("ABC").x;
|
||||||
@@ -3517,7 +3481,7 @@ void ImGuiWrapper::filament_group(const std::string& filament_type, const char*
|
|||||||
if (rgba[3] == 0x00) {
|
if (rgba[3] == 0x00) {
|
||||||
svg_path = "/images/outlined_rect_transparent.svg";
|
svg_path = "/images/outlined_rect_transparent.svg";
|
||||||
}
|
}
|
||||||
ImTextureID transparent = svg_texture(Slic3r::resources_dir() + svg_path, img_size.x, img_size.y, hex_color);
|
BitmapCache::load_from_svg_file_change_color(Slic3r::resources_dir() + svg_path, img_size.x, img_size.y, transparent, hex_color);
|
||||||
ImGui::BeginGroup();
|
ImGui::BeginGroup();
|
||||||
{
|
{
|
||||||
ImVec2 cursor_pos = ImGui::GetCursorScreenPos();
|
ImVec2 cursor_pos = ImGui::GetCursorScreenPos();
|
||||||
|
|||||||
@@ -104,14 +104,6 @@ public:
|
|||||||
// Hash of every draw list's vertices, indices and commands.
|
// Hash of every draw list's vertices, indices and commands.
|
||||||
static ImGuiID draw_data_signature(const ImDrawData* draw_data);
|
static ImGuiID draw_data_signature(const ImDrawData* draw_data);
|
||||||
|
|
||||||
// A GL texture holding an SVG icon rasterized at width x height, optionally recolored.
|
|
||||||
// Rasterizing an SVG is far too expensive to redo for every frame that draws the icon, and the
|
|
||||||
// texture the previous frame generated would leak, so the result is kept until the frame that
|
|
||||||
// finds the cache overgrown drops it (and rebuilds only what it still draws).
|
|
||||||
static ImTextureID svg_texture(const std::string& filename, unsigned width, unsigned height, const char* hex_color = nullptr);
|
|
||||||
// Deletes every texture svg_texture() handed out. Requires a current GL context.
|
|
||||||
static void destroy_svg_textures();
|
|
||||||
|
|
||||||
float scaled(float x) const { return x * m_font_size; }
|
float scaled(float x) const { return x * m_font_size; }
|
||||||
ImVec2 scaled(float x, float y) const { return ImVec2(x * m_font_size, y * m_font_size); }
|
ImVec2 scaled(float x, float y) const { return ImVec2(x * m_font_size, y * m_font_size); }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -2225,12 +2225,11 @@ void NotificationManager::close_and_delete_self(PopNotification * self)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void NotificationManager::remove_notification_of_type(const NotificationType type) {
|
void NotificationManager::remove_notification_of_type(const NotificationType type) {
|
||||||
// Seven notification types may have several instances alive at once, so erase every match:
|
|
||||||
// stopping at the first one leaves the rest (and the ObjectIDs they hold) behind.
|
|
||||||
for (auto it = m_pop_notifications.begin(); it != m_pop_notifications.end();) {
|
for (auto it = m_pop_notifications.begin(); it != m_pop_notifications.end();) {
|
||||||
std::unique_ptr<PopNotification> ¬ification = *it;
|
std::unique_ptr<PopNotification> ¬ification = *it;
|
||||||
if (notification->get_type() == type) {
|
if (notification->get_type() == type) {
|
||||||
it = m_pop_notifications.erase(it);
|
it = m_pop_notifications.erase(it);
|
||||||
|
break;
|
||||||
} else
|
} else
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -819,7 +819,9 @@ int OrcaCloudServiceAgent::user_logout(bool request)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clear_session();
|
// An explicit logout also wipes the backend the token storage option is not using, so a token
|
||||||
|
// stranded by switching that option cannot sign the account back in later.
|
||||||
|
clear_session(/*all_backends=*/request);
|
||||||
return BAMBU_NETWORK_SUCCESS;
|
return BAMBU_NETWORK_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1604,7 +1606,9 @@ void OrcaCloudServiceAgent::persist_user_secret(const std::string& secret)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(void) stored;
|
if (stored) {
|
||||||
|
secret_stored = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OrcaCloudServiceAgent::load_user_secret(std::string& out_secret)
|
bool OrcaCloudServiceAgent::load_user_secret(std::string& out_secret)
|
||||||
@@ -1644,6 +1648,7 @@ bool OrcaCloudServiceAgent::load_user_secret(std::string& out_secret)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (integrity_ok && aes256gcm_decrypt(encoded_payload, key, plain) && !plain.empty()) {
|
if (integrity_ok && aes256gcm_decrypt(encoded_payload, key, plain) && !plain.empty()) {
|
||||||
|
secret_stored = true;
|
||||||
out_secret = plain;
|
out_secret = plain;
|
||||||
// Upgrade legacy payloads to signed format
|
// Upgrade legacy payloads to signed format
|
||||||
if (payload.rfind("v2:", 0) != 0) {
|
if (payload.rfind("v2:", 0) != 0) {
|
||||||
@@ -1661,6 +1666,7 @@ bool OrcaCloudServiceAgent::load_user_secret(std::string& out_secret)
|
|||||||
if (store.Load(SECRET_STORE_SERVICE, username, secret) && secret.IsOk()) {
|
if (store.Load(SECRET_STORE_SERVICE, username, secret) && secret.IsOk()) {
|
||||||
out_secret.assign(static_cast<const char*>(secret.GetData()), secret.GetSize());
|
out_secret.assign(static_cast<const char*>(secret.GetData()), secret.GetSize());
|
||||||
if (!out_secret.empty()) {
|
if (!out_secret.empty()) {
|
||||||
|
secret_stored = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1670,11 +1676,20 @@ bool OrcaCloudServiceAgent::load_user_secret(std::string& out_secret)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OrcaCloudServiceAgent::clear_user_secret()
|
void OrcaCloudServiceAgent::clear_user_secret(bool all_backends)
|
||||||
{
|
{
|
||||||
wxSecretStore store = wxSecretStore::GetDefault();
|
// Nothing this process loaded or saved: leave the store alone. Deleting would only cost a
|
||||||
if (store.IsOk()) {
|
// keychain round trip (or a hang while the keychain is unresponsive) and could remove a
|
||||||
store.Delete(SECRET_STORE_SERVICE);
|
// login another instance just saved.
|
||||||
|
if (!secret_stored.exchange(false) && !all_backends) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (all_backends || !m_use_encrypted_token_file) {
|
||||||
|
wxSecretStore store = wxSecretStore::GetDefault();
|
||||||
|
if (store.IsOk()) {
|
||||||
|
store.Delete(SECRET_STORE_SERVICE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
compute_fallback_path();
|
compute_fallback_path();
|
||||||
@@ -2023,13 +2038,13 @@ bool OrcaCloudServiceAgent::set_user_session(const json& session_json, bool noti
|
|||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OrcaCloudServiceAgent::clear_session()
|
void OrcaCloudServiceAgent::clear_session(bool all_backends)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(session_mutex);
|
std::lock_guard<std::mutex> lock(session_mutex);
|
||||||
session = SessionInfo{};
|
session = SessionInfo{};
|
||||||
}
|
}
|
||||||
clear_user_secret();
|
clear_user_secret(all_backends);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|||||||
@@ -326,7 +326,7 @@ public:
|
|||||||
|
|
||||||
void persist_user_secret(const std::string& secret);
|
void persist_user_secret(const std::string& secret);
|
||||||
bool load_user_secret(std::string& out_secret);
|
bool load_user_secret(std::string& out_secret);
|
||||||
void clear_user_secret();
|
void clear_user_secret(bool all_backends = false);
|
||||||
|
|
||||||
// Token refresh helpers
|
// Token refresh helpers
|
||||||
bool refresh_if_expiring(std::chrono::seconds skew, const std::string& reason);
|
bool refresh_if_expiring(std::chrono::seconds skew, const std::string& reason);
|
||||||
@@ -344,7 +344,7 @@ public:
|
|||||||
bool persist = true);
|
bool persist = true);
|
||||||
// Accepts either nested Orca cloud / GoTrue session JSON or flat WebView token JSON.
|
// Accepts either nested Orca cloud / GoTrue session JSON or flat WebView token JSON.
|
||||||
bool set_user_session(const nlohmann::json& session_json, bool notify_login = true);
|
bool set_user_session(const nlohmann::json& session_json, bool notify_login = true);
|
||||||
void clear_session();
|
void clear_session(bool all_backends = false);
|
||||||
|
|
||||||
static std::string generate_uuid_for_setting_id(const std::string& name, const std::string& user_id = "");
|
static std::string generate_uuid_for_setting_id(const std::string& name, const std::string& user_id = "");
|
||||||
|
|
||||||
@@ -413,6 +413,11 @@ private:
|
|||||||
// Member variables - auth state
|
// Member variables - auth state
|
||||||
PkceBundle pkce_bundle;
|
PkceBundle pkce_bundle;
|
||||||
std::string secret_fallback_path;
|
std::string secret_fallback_path;
|
||||||
|
// Set once this process has read a secret from the store or written one. Unless the user logs
|
||||||
|
// out explicitly, clear_user_secret() only touches the store while it is set, so a logged-out
|
||||||
|
// instance (the GUI polls the login status every 2 s) makes no keychain calls and cannot wipe
|
||||||
|
// a login another instance saved.
|
||||||
|
std::atomic_bool secret_stored{false};
|
||||||
SessionHandler session_handler;
|
SessionHandler session_handler;
|
||||||
OnLoginCompleteHandler on_login_complete_handler;
|
OnLoginCompleteHandler on_login_complete_handler;
|
||||||
SessionInfo session;
|
SessionInfo session;
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ add_executable(${_TEST_NAME}_tests
|
|||||||
test_prebuild_queue.cpp
|
test_prebuild_queue.cpp
|
||||||
test_staged_build.cpp
|
test_staged_build.cpp
|
||||||
test_network_versions.cpp
|
test_network_versions.cpp
|
||||||
|
test_orca_cloud_agent.cpp
|
||||||
test_action_source.cpp
|
test_action_source.cpp
|
||||||
test_plugin_host_api.cpp
|
test_plugin_host_api.cpp
|
||||||
# Exercise seam enums and predicates through the embedded Python host API.
|
# Exercise seam enums and predicates through the embedded Python host API.
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
#include <memory.h>
|
#include <memory.h>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
#include <pybind11/embed.h>
|
#include <pybind11/embed.h>
|
||||||
#include <pybind11/pybind11.h>
|
#include <pybind11/pybind11.h>
|
||||||
|
|
||||||
@@ -25,8 +26,15 @@ void ensure_python_initialized()
|
|||||||
config.parse_argv = 0;
|
config.parse_argv = 0;
|
||||||
|
|
||||||
const auto python_home = boost::dll::program_location().parent_path() / "python";
|
const auto python_home = boost::dll::program_location().parent_path() / "python";
|
||||||
|
#ifdef _WIN32
|
||||||
|
const auto stdlib = python_home / "Lib";
|
||||||
|
#else
|
||||||
|
const auto stdlib = python_home / "lib" /
|
||||||
|
("python" + std::to_string(PY_MAJOR_VERSION) + "." + std::to_string(PY_MINOR_VERSION));
|
||||||
|
#endif
|
||||||
|
|
||||||
if (boost::filesystem::exists(python_home)) {
|
// Only a real runtime: a stray python/ folder (packages a test left behind) is not a home.
|
||||||
|
if (boost::filesystem::exists(stdlib / "encodings")) {
|
||||||
const std::string home = python_home.string();
|
const std::string home = python_home.string();
|
||||||
const PyStatus status = PyConfig_SetBytesString(&config, &config.home, home.c_str());
|
const PyStatus status = PyConfig_SetBytesString(&config, &config.home, home.c_str());
|
||||||
|
|
||||||
|
|||||||
@@ -1,39 +1,6 @@
|
|||||||
#include <catch2/catch_all.hpp>
|
#include <catch2/catch_all.hpp>
|
||||||
|
|
||||||
#include "slic3r/Utils/Http.hpp"
|
#include "slic3r/Utils/Http.hpp"
|
||||||
#include "slic3r/Utils/OrcaCloudServiceAgent.hpp"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
nlohmann::json flat_session_json(const nlohmann::json& fields)
|
|
||||||
{
|
|
||||||
nlohmann::json session = {
|
|
||||||
{"access_token", "test-token"},
|
|
||||||
{"user_id", "test-user-id"}
|
|
||||||
};
|
|
||||||
session.update(fields);
|
|
||||||
return session;
|
|
||||||
}
|
|
||||||
|
|
||||||
nlohmann::json nested_session_json(const nlohmann::json& metadata)
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
{"access_token", "test-token"},
|
|
||||||
{"user", {
|
|
||||||
{"id", "test-user-id"},
|
|
||||||
{"user_metadata", metadata}
|
|
||||||
}}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string resolved_display_name(const nlohmann::json& session)
|
|
||||||
{
|
|
||||||
Slic3r::OrcaCloudServiceAgent agent("");
|
|
||||||
REQUIRE(agent.set_user_session(session, false));
|
|
||||||
return agent.get_user_nickname();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
TEST_CASE("Check SSL certificates paths", "[Http][NotWorking]") {
|
TEST_CASE("Check SSL certificates paths", "[Http][NotWorking]") {
|
||||||
|
|
||||||
@@ -53,62 +20,6 @@ TEST_CASE("Check SSL certificates paths", "[Http][NotWorking]") {
|
|||||||
REQUIRE(status == 200);
|
REQUIRE(status == 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Orca cloud flat session resolves display name consistently", "[OrcaCloudServiceAgent]")
|
|
||||||
{
|
|
||||||
CHECK(resolved_display_name(flat_session_json({
|
|
||||||
{"username", "orca_username"},
|
|
||||||
{"display_name", "Display Name"},
|
|
||||||
{"nickname", "Nickname"}
|
|
||||||
})) == "Display Name");
|
|
||||||
|
|
||||||
CHECK(resolved_display_name(flat_session_json({
|
|
||||||
{"username", "orca_username"},
|
|
||||||
{"nickname", "Nickname"}
|
|
||||||
})) == "Nickname");
|
|
||||||
|
|
||||||
CHECK(resolved_display_name(flat_session_json({
|
|
||||||
{"username", "orca_username"},
|
|
||||||
{"full_name", "Full Name"}
|
|
||||||
})) == "Full Name");
|
|
||||||
|
|
||||||
CHECK(resolved_display_name(flat_session_json({
|
|
||||||
{"username", "orca_username"},
|
|
||||||
{"name", "Provider Name"}
|
|
||||||
})) == "Provider Name");
|
|
||||||
|
|
||||||
CHECK(resolved_display_name(flat_session_json({
|
|
||||||
{"username", "orca_username"}
|
|
||||||
})) == "orca_username");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Orca cloud nested session resolves display name consistently", "[OrcaCloudServiceAgent]")
|
|
||||||
{
|
|
||||||
CHECK(resolved_display_name(nested_session_json({
|
|
||||||
{"username", "orca_username"},
|
|
||||||
{"display_name", "Display Name"},
|
|
||||||
{"nickname", "Nickname"}
|
|
||||||
})) == "Display Name");
|
|
||||||
|
|
||||||
CHECK(resolved_display_name(nested_session_json({
|
|
||||||
{"username", "orca_username"},
|
|
||||||
{"nickname", "Nickname"}
|
|
||||||
})) == "Nickname");
|
|
||||||
|
|
||||||
CHECK(resolved_display_name(nested_session_json({
|
|
||||||
{"username", "orca_username"},
|
|
||||||
{"full_name", "Full Name"}
|
|
||||||
})) == "Full Name");
|
|
||||||
|
|
||||||
CHECK(resolved_display_name(nested_session_json({
|
|
||||||
{"username", "orca_username"},
|
|
||||||
{"name", "Provider Name"}
|
|
||||||
})) == "Provider Name");
|
|
||||||
|
|
||||||
CHECK(resolved_display_name(nested_session_json({
|
|
||||||
{"username", "orca_username"}
|
|
||||||
})) == "orca_username");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Http digest authentication", "[Http][NotWorking]") {
|
TEST_CASE("Http digest authentication", "[Http][NotWorking]") {
|
||||||
Slic3r::Http g = Slic3r::Http::get("https://httpbingo.org/digest-auth/auth/guest/guest");
|
Slic3r::Http g = Slic3r::Http::get("https://httpbingo.org/digest-auth/auth/guest/guest");
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,173 @@
|
|||||||
|
#include <catch2/catch_all.hpp>
|
||||||
|
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
#include <boost/filesystem/fstream.hpp>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "slic3r/Utils/OrcaCloudServiceAgent.hpp"
|
||||||
|
#include "test_utils.hpp"
|
||||||
|
|
||||||
|
using namespace Slic3r;
|
||||||
|
namespace fs = boost::filesystem;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// The encrypted token file is the one secret backend a test can observe without a system
|
||||||
|
// keychain. Every agent pointed at the same directory shares it, like separate app instances
|
||||||
|
// share the keychain entry.
|
||||||
|
std::unique_ptr<OrcaCloudServiceAgent> make_file_backed_agent(const fs::path& dir)
|
||||||
|
{
|
||||||
|
auto agent = std::make_unique<OrcaCloudServiceAgent>(dir.string());
|
||||||
|
agent->set_use_encrypted_token_file(true);
|
||||||
|
agent->set_config_dir(dir.string());
|
||||||
|
return agent;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::path secret_file(const fs::path& dir) { return dir / secret_constants::USER_SECRET_FILENAME; }
|
||||||
|
|
||||||
|
nlohmann::json flat_session_json(const nlohmann::json& fields)
|
||||||
|
{
|
||||||
|
nlohmann::json session = {
|
||||||
|
{"access_token", "test-token"},
|
||||||
|
{"user_id", "test-user-id"}
|
||||||
|
};
|
||||||
|
session.update(fields);
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
nlohmann::json nested_session_json(const nlohmann::json& metadata)
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
{"access_token", "test-token"},
|
||||||
|
{"user", {
|
||||||
|
{"id", "test-user-id"},
|
||||||
|
{"user_metadata", metadata}
|
||||||
|
}}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// set_user_session() persists the session, so it goes to a throwaway token file rather than the
|
||||||
|
// system keychain of whoever runs the tests.
|
||||||
|
std::string resolved_display_name(const nlohmann::json& session)
|
||||||
|
{
|
||||||
|
ScopedTemporaryDir dir("orca-secret");
|
||||||
|
auto agent = make_file_backed_agent(dir.path());
|
||||||
|
REQUIRE(agent->set_user_session(session, false));
|
||||||
|
return agent->get_user_nickname();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_CASE("Logging out removes the secret this instance saved", "[OrcaCloudServiceAgent]")
|
||||||
|
{
|
||||||
|
ScopedTemporaryDir dir("orca-secret");
|
||||||
|
auto agent = make_file_backed_agent(dir.path());
|
||||||
|
|
||||||
|
agent->persist_user_secret("refresh-token");
|
||||||
|
REQUIRE(fs::exists(secret_file(dir.path())));
|
||||||
|
|
||||||
|
agent->user_logout(false);
|
||||||
|
CHECK_FALSE(fs::exists(secret_file(dir.path())));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Logging out removes a secret this instance loaded from the store", "[OrcaCloudServiceAgent]")
|
||||||
|
{
|
||||||
|
ScopedTemporaryDir dir("orca-secret");
|
||||||
|
make_file_backed_agent(dir.path())->persist_user_secret("refresh-token");
|
||||||
|
|
||||||
|
auto agent = make_file_backed_agent(dir.path());
|
||||||
|
std::string secret;
|
||||||
|
REQUIRE(agent->load_user_secret(secret));
|
||||||
|
CHECK(secret == "refresh-token");
|
||||||
|
|
||||||
|
agent->user_logout(false);
|
||||||
|
CHECK_FALSE(fs::exists(secret_file(dir.path())));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Logging out leaves a secret this instance never loaded or saved alone", "[OrcaCloudServiceAgent]")
|
||||||
|
{
|
||||||
|
ScopedTemporaryDir dir("orca-secret");
|
||||||
|
make_file_backed_agent(dir.path())->persist_user_secret("refresh-token");
|
||||||
|
|
||||||
|
// A logged-out instance is asked to log out on every login-status poll.
|
||||||
|
auto other = make_file_backed_agent(dir.path());
|
||||||
|
other->user_logout(false);
|
||||||
|
other->user_logout(false);
|
||||||
|
CHECK(fs::exists(secret_file(dir.path())));
|
||||||
|
|
||||||
|
std::string secret;
|
||||||
|
REQUIRE(make_file_backed_agent(dir.path())->load_user_secret(secret));
|
||||||
|
CHECK(secret == "refresh-token");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Logging out leaves a secret this instance could not read alone", "[OrcaCloudServiceAgent]")
|
||||||
|
{
|
||||||
|
ScopedTemporaryDir dir("orca-secret");
|
||||||
|
// Written under another encryption key, e.g. by another OS user sharing the data directory.
|
||||||
|
fs::ofstream(secret_file(dir.path())) << "v2:0000:not-a-payload-this-user-can-decrypt";
|
||||||
|
|
||||||
|
auto agent = make_file_backed_agent(dir.path());
|
||||||
|
std::string secret;
|
||||||
|
REQUIRE_FALSE(agent->load_user_secret(secret));
|
||||||
|
|
||||||
|
agent->user_logout(false);
|
||||||
|
CHECK(fs::exists(secret_file(dir.path())));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Orca cloud flat session resolves display name consistently", "[OrcaCloudServiceAgent]")
|
||||||
|
{
|
||||||
|
CHECK(resolved_display_name(flat_session_json({
|
||||||
|
{"username", "orca_username"},
|
||||||
|
{"display_name", "Display Name"},
|
||||||
|
{"nickname", "Nickname"}
|
||||||
|
})) == "Display Name");
|
||||||
|
|
||||||
|
CHECK(resolved_display_name(flat_session_json({
|
||||||
|
{"username", "orca_username"},
|
||||||
|
{"nickname", "Nickname"}
|
||||||
|
})) == "Nickname");
|
||||||
|
|
||||||
|
CHECK(resolved_display_name(flat_session_json({
|
||||||
|
{"username", "orca_username"},
|
||||||
|
{"full_name", "Full Name"}
|
||||||
|
})) == "Full Name");
|
||||||
|
|
||||||
|
CHECK(resolved_display_name(flat_session_json({
|
||||||
|
{"username", "orca_username"},
|
||||||
|
{"name", "Provider Name"}
|
||||||
|
})) == "Provider Name");
|
||||||
|
|
||||||
|
CHECK(resolved_display_name(flat_session_json({
|
||||||
|
{"username", "orca_username"}
|
||||||
|
})) == "orca_username");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Orca cloud nested session resolves display name consistently", "[OrcaCloudServiceAgent]")
|
||||||
|
{
|
||||||
|
CHECK(resolved_display_name(nested_session_json({
|
||||||
|
{"username", "orca_username"},
|
||||||
|
{"display_name", "Display Name"},
|
||||||
|
{"nickname", "Nickname"}
|
||||||
|
})) == "Display Name");
|
||||||
|
|
||||||
|
CHECK(resolved_display_name(nested_session_json({
|
||||||
|
{"username", "orca_username"},
|
||||||
|
{"nickname", "Nickname"}
|
||||||
|
})) == "Nickname");
|
||||||
|
|
||||||
|
CHECK(resolved_display_name(nested_session_json({
|
||||||
|
{"username", "orca_username"},
|
||||||
|
{"full_name", "Full Name"}
|
||||||
|
})) == "Full Name");
|
||||||
|
|
||||||
|
CHECK(resolved_display_name(nested_session_json({
|
||||||
|
{"username", "orca_username"},
|
||||||
|
{"name", "Provider Name"}
|
||||||
|
})) == "Provider Name");
|
||||||
|
|
||||||
|
CHECK(resolved_display_name(nested_session_json({
|
||||||
|
{"username", "orca_username"}
|
||||||
|
})) == "orca_username");
|
||||||
|
}
|
||||||
@@ -38,6 +38,9 @@ namespace {
|
|||||||
// before this destructor's shutdown() runs.
|
// before this destructor's shutdown() runs.
|
||||||
struct ScopedPluginManager
|
struct ScopedPluginManager
|
||||||
{
|
{
|
||||||
|
// Before initialize(): the interpreter creates {data_dir}/python/packages and {data_dir}/log,
|
||||||
|
// which would otherwise land in the working directory.
|
||||||
|
ScopedDataDir python_data_dir{"plugin-python"};
|
||||||
bool initialized = PluginManager::instance().initialize();
|
bool initialized = PluginManager::instance().initialize();
|
||||||
|
|
||||||
~ScopedPluginManager()
|
~ScopedPluginManager()
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ namespace {
|
|||||||
// same as any other plugin.
|
// same as any other plugin.
|
||||||
struct ScopedManagerShutdown
|
struct ScopedManagerShutdown
|
||||||
{
|
{
|
||||||
|
// Before initialize(): the interpreter creates {data_dir}/python/packages and {data_dir}/log,
|
||||||
|
// which would otherwise land in the working directory.
|
||||||
|
ScopedDataDir python_data_dir{"plugin-python"};
|
||||||
bool initialized = PluginManager::instance().initialize();
|
bool initialized = PluginManager::instance().initialize();
|
||||||
|
|
||||||
~ScopedManagerShutdown()
|
~ScopedManagerShutdown()
|
||||||
|
|||||||
@@ -42,6 +42,9 @@ namespace {
|
|||||||
// Declare this FIRST in a test so it is destroyed last.
|
// Declare this FIRST in a test so it is destroyed last.
|
||||||
struct ScopedPluginManager
|
struct ScopedPluginManager
|
||||||
{
|
{
|
||||||
|
// Before initialize(): the interpreter creates {data_dir}/python/packages and {data_dir}/log,
|
||||||
|
// which would otherwise land in the working directory.
|
||||||
|
ScopedDataDir python_data_dir{"plugin-python"};
|
||||||
bool initialized = false;
|
bool initialized = false;
|
||||||
|
|
||||||
ScopedPluginManager() { initialized = PluginManager::instance().initialize(); }
|
ScopedPluginManager() { initialized = PluginManager::instance().initialize(); }
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "plugin_test_utils.hpp"
|
||||||
|
|
||||||
namespace py = pybind11;
|
namespace py = pybind11;
|
||||||
using namespace Slic3r;
|
using namespace Slic3r;
|
||||||
|
|
||||||
@@ -21,6 +23,9 @@ namespace {
|
|||||||
// into Python unless PythonInterpreter::instance() reports initialized.
|
// into Python unless PythonInterpreter::instance() reports initialized.
|
||||||
struct ScopedPluginManager
|
struct ScopedPluginManager
|
||||||
{
|
{
|
||||||
|
// Before initialize(): the interpreter creates {data_dir}/python/packages and {data_dir}/log,
|
||||||
|
// which would otherwise land in the working directory.
|
||||||
|
ScopedDataDir python_data_dir{"plugin-python"};
|
||||||
bool initialized = PluginManager::instance().initialize();
|
bool initialized = PluginManager::instance().initialize();
|
||||||
|
|
||||||
~ScopedPluginManager()
|
~ScopedPluginManager()
|
||||||
|
|||||||
@@ -35,6 +35,9 @@ namespace {
|
|||||||
|
|
||||||
struct ScopedPluginManager
|
struct ScopedPluginManager
|
||||||
{
|
{
|
||||||
|
// Before initialize(): the interpreter creates {data_dir}/python/packages and {data_dir}/log,
|
||||||
|
// which would otherwise land in the working directory.
|
||||||
|
ScopedDataDir python_data_dir{"plugin-python"};
|
||||||
bool initialized = false;
|
bool initialized = false;
|
||||||
|
|
||||||
ScopedPluginManager() { initialized = PluginManager::instance().initialize(); }
|
ScopedPluginManager() { initialized = PluginManager::instance().initialize(); }
|
||||||
|
|||||||
Reference in New Issue
Block a user