mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-24 09:23:27 +00:00
feat: faster startup by lazy-loading main window panels on idle or first use (#15811)
This commit is contained in:
@@ -0,0 +1,220 @@
|
||||
# Deferred Page Construction: High Level Design
|
||||
|
||||
## Why it exists
|
||||
|
||||
The main window is a notebook of tabs. When the first frame appears, one tab is on
|
||||
screen and the others are not; some of them are opened later in the session, some
|
||||
never, and some only exist for certain printers. Building a tab before the first frame
|
||||
adds its cost to every startup, whether or not the tab is used.
|
||||
|
||||
This subsystem builds a tab's panel the first time the tab is shown. It also builds the
|
||||
remaining tabs while the user is idle after startup, in units of tens of milliseconds,
|
||||
so a click that lands in the middle of one waits for that unit and no longer. Startup
|
||||
pays only for what the first frame shows, and the other tabs are usually built before
|
||||
anyone opens them.
|
||||
|
||||
## The parts
|
||||
|
||||
`src/slic3r/GUI/Lazy.hpp`, `LazyPage.hpp`, `StagedBuild.hpp` and `IdleScheduler.hpp/.cpp`
|
||||
(with its wx-free `PrebuildQueue.hpp`) are independent. A holder can hold anything a
|
||||
factory makes, a page is a holder with a placeholder widget, a staged object can live
|
||||
outside a holder, and the scheduler knows none of them; it runs tasks, which `MainFrame`
|
||||
makes from the holders.
|
||||
|
||||
### Lazy<T>: the holder
|
||||
|
||||
Holds an object that a factory makes on first use, under a name for the log and a place
|
||||
in the idle queue, both given by the owner. The header has no wx dependency; the busy
|
||||
cursor for an on-demand build lives in `Lazy.cpp` and is skipped when there is no app,
|
||||
so the holder is unit-tested.
|
||||
|
||||
- `get()` is null until the object is completely built; `built()` says the same.
|
||||
- `ensure()` builds whatever is left now, under a busy cursor, and returns the object, or
|
||||
null in the two cases below. A click on an unbuilt tab or a first open of a dialog goes
|
||||
through this, and it logs the units and time it took.
|
||||
- `build_step()` runs one unit of construction and returns true while more remain. The
|
||||
first unit is the factory call, and each later unit is one `StagedBuild` step if the
|
||||
type has them.
|
||||
- `when_built(fn)` runs `fn(object)` now if it exists, otherwise once it is built.
|
||||
- `pending()` says whether the idle prebuild has work here: not built, and the factory
|
||||
has not returned null. A null factory result is logged and the holder stays unbuilt.
|
||||
- A unit that pumps the event loop cannot re-enter the holder; a nested `build_step()`
|
||||
does nothing and a nested `ensure()` returns null.
|
||||
- After a unit throws, the holder and the scheduler still run the next one.
|
||||
|
||||
The holder does not own the object; its wx parent does, as for any window. A type with
|
||||
one instance in the app derives from `LazyInstance<T>`, which points at that instance's
|
||||
holder (the holder registers itself, and a recreated `MainFrame`'s holder replaces the
|
||||
old frame's) and gives the type the static entry points the rest of the app uses,
|
||||
`T::if_built()`, `T::ensure()` and `T::when_built(fn)`. They return null, or do
|
||||
nothing, while no holder exists, so a caller needs no `mainframe` check.
|
||||
|
||||
`built()` is an atomic flag, since a job worker reads it through the statics
|
||||
(`MainFrame::get_calibration_curr_tab()`); it is set after the object is complete.
|
||||
|
||||
`LazyBase` is the holder's type-free interface (`name()`, `built()`, `pending()`,
|
||||
`build_step()`, `prebuild_order()`) and is what the scheduler side sees.
|
||||
|
||||
### LazyPage<Panel>: the placeholder
|
||||
|
||||
A `wxPanel` placed in the parent in place of the real panel, and a `Lazy<Panel>` whose
|
||||
factory makes the panel inside it (by default `new Panel(parent)`). The notebook needs a
|
||||
page object for the tab to exist and for `show_device()` to insert and remove tabs by
|
||||
pointer, and the placeholder is that object. `MainFrame` creates every page once, named
|
||||
after its `TAB_ID_*`, and keeps it for the frame's life; `show_device()` only moves
|
||||
pages in and out of the book.
|
||||
|
||||
- `Show()` is forwarded to the panel, so a panel's own `Show()` override stays its
|
||||
activation hook (refresh timers, machine sync) and `SelectPageByName()` works
|
||||
unchanged. A show builds the panel unless the frame itself is still hidden, because
|
||||
the book selects its first page as it is inserted; `MainFrame::Show()` shows the
|
||||
current page again when the frame becomes visible, which builds it.
|
||||
- `in_book()` says whether the parent notebook currently lists the page, and
|
||||
`pending()` is that and not built, so a tab `show_device()` has taken out of the book
|
||||
is not prebuilt.
|
||||
- A panel built while its page is hidden stays hidden, and a completed panel gets the
|
||||
dark-mode pass the frame ran before it existed.
|
||||
|
||||
The Compare presets dialog is a holder without a page. `MainFrame` keeps a
|
||||
`Lazy<DiffPresetDialog>` whose factory constructs the dialog and binds its events, the
|
||||
dialog derives from `LazyInstance`, and its callers use
|
||||
`DiffPresetDialog::ensure()->show()` and `DiffPresetDialog::if_built()` like a tab's
|
||||
callers do. Saving a preset refreshes the dialog only while it is shown, since `show()`
|
||||
reloads the presets.
|
||||
|
||||
### StagedBuild: construction in units
|
||||
|
||||
A mixin for a panel whose constructor is too big to be one unit. The constructor builds
|
||||
the skeleton (sizers, and the parts other code may touch) and queues the rest with
|
||||
`add_build_step()`. `build_step()` runs one step, and `add_build_steps_of(child)`
|
||||
forwards a child's steps so a nested panel is spread the same way; the parent is built
|
||||
only once the child is, including steps the child queues later. Steps run in order, on
|
||||
the main thread. A `Lazy<T>` recognises a staged type at compile time and runs its steps
|
||||
one per unit.
|
||||
|
||||
Nothing may touch what a step builds before the last step has run. In practice:
|
||||
|
||||
- nothing paints the panel before it is complete, since a panel built at idle is hidden
|
||||
with its page and one built on demand finishes inside `ensure()` before the event loop
|
||||
runs again;
|
||||
- members created in steps are initialised to null in the header, so a partially built
|
||||
panel can be destroyed;
|
||||
- timers and event handlers that use step content check `built()` first
|
||||
(`MonitorPanel::update_all()`, `CalibrationPanel::update_all()`), and a child's steps
|
||||
are queued before anything in the constructor can fire such a handler;
|
||||
- a destructor that disconnects from step content checks `built()` first;
|
||||
- a constructor or step does not take focus while the panel is off screen
|
||||
(`IsShownOnScreen()` before `SetFocus()`), since it may run while the user is typing
|
||||
elsewhere;
|
||||
- where a step's widgets must keep their place in a sizer that later steps also fill, the
|
||||
constructor adds an empty slot sizer in that position and the step fills the slot
|
||||
(`StatusBasePanel`).
|
||||
|
||||
### IdleScheduler: when to build
|
||||
|
||||
A task is any `LazyBase`: a name for the log, `pending()`, `build_step()` and
|
||||
`prebuild_order()`. `PrebuildQueue` holds the tasks by order (equal order in the order
|
||||
added) and runs a slice, the units of the first pending task until it completes, the
|
||||
budget is spent on the clock it is given, or the interrupt predicate says input arrived.
|
||||
It has no wx dependency and is unit-tested with a fake clock. A task whose work is gone
|
||||
is passed over and stays in the queue, so a tab that `show_device()` removes and later
|
||||
re-inserts is pending again.
|
||||
|
||||
`IdleScheduler` drives the queue with the real clock, the app's input timestamp and the
|
||||
Windows queue check, and logs each unit and slice. Each slice is a timer message: a
|
||||
period of 250 ms while waiting for the user to go quiet, and a one-shot of zero after a
|
||||
slice that left work, so the event loop dispatches whatever it has queued (paint,
|
||||
timers, input) before the next slice runs. Chaining slices with `CallAfter` would not do
|
||||
this: wx drains pending events fully before the next native message, on every platform.
|
||||
On GTK the one-shot is 5 ms, because a due GLib timeout runs ahead of the redraw and
|
||||
idle sources that paint and deliver posted events.
|
||||
A slice runs only once the user has been idle for the quiet time, and the timer stops
|
||||
itself once no task is pending. The tick period, quiet time and slice length are
|
||||
constants in `IdleScheduler.cpp`. A unit cannot be interrupted once started, so the
|
||||
largest unit bounds click latency on Windows; on the other platforms a click also waits
|
||||
for the rest of the slice. A unit that pumps the event loop lets the timer fire inside
|
||||
its own slice, and that tick does nothing.
|
||||
|
||||
The tasks are made by their owners. `MainFrame::prebuild_pages_when_idle()` registers
|
||||
every `LazyPage` the frame created, in or out of the book (`pending()` is false for a
|
||||
page out of the book), the Compare presets holder, and the Prepare sidebar's settings
|
||||
page from `ParamsPanel::settings_page_prebuild()`, whose first unit selects the default
|
||||
tab if none is selected yet and whose later units build one option group each.
|
||||
`show_device()` only restarts the timer. `MainFrame` owns the scheduler because it owns
|
||||
everything the tasks build, and clearing the queue with the frame is what keeps a task
|
||||
from outliving its object.
|
||||
|
||||
Idle time comes from `GUI_App::FilterEvent`, which timestamps mouse and keyboard events
|
||||
(`wxEVT_CATEGORY_USER_INPUT` minus command events, which all claim that category), and
|
||||
`GUI_App::input_idle_ms()` reports it. On Windows a slice additionally refuses to start
|
||||
when the message queue holds keyboard, button, touch or pen input. Mouse moves are
|
||||
excluded because Windows synthesises one whenever a window appears under the cursor,
|
||||
which every unit does. Other platforms use the timestamp alone.
|
||||
|
||||
Once every registered page is built the timer is stopped and the subsystem costs
|
||||
nothing.
|
||||
|
||||
## Rules
|
||||
|
||||
**What builds before the first frame.** The Prepare tab's plater, because `post_init()`
|
||||
needs its GL canvas on screen to initialise OpenGL in every startup state, and the start
|
||||
page the user configured. Home is built by the first `MainFrame::Show()`, Prepare's
|
||||
settings page by selecting the tab. `post_init()` passes through the Prepare tab for GL
|
||||
init under `Freeze()` with `MainFrame::select_prepare_for_gl_init()`, which changes the
|
||||
selection without the page-changed event, so nothing else is built for that pass.
|
||||
|
||||
**Reaching a lazy object.** A caller uses the type's own statics. `T::if_built()` may
|
||||
return null and is for telling the object something it can live without (a rescale, a
|
||||
colour change, a status update). `T::ensure()` builds the object and is for navigating
|
||||
to it or for a caller that is about to show it. A caller that tells the object something
|
||||
it would not fetch for itself on construction uses `T::when_built()`, which keeps the
|
||||
message until the object exists. A panel that pulls its state when constructed (the Home
|
||||
page requests the recent list on load, the Device tab reads the device manager on show)
|
||||
is reached with `if_built()`; one that cannot pull gets `when_built()`.
|
||||
|
||||
**Unit size.** A unit cannot be interrupted, so it should stay within the slice length on
|
||||
a fast machine. A constructor above that is staged. A single widget above it is the
|
||||
floor unless the widget itself is split.
|
||||
|
||||
**Order.** Cheapest and most likely to be opened first, given where `MainFrame` creates
|
||||
each holder. The settings page is 0 (the Prepare tab's own content), Home 10, Device 20
|
||||
(a Bambu user's usual second stop), Calibration 30, Multi-device 40, the web Device
|
||||
view 50, Project 60 (a second WebView2 instance) and the Compare presets dialog 100;
|
||||
steps of ten so a new tab takes a value between its neighbours without renumbering
|
||||
them. `MainFrame::prebuild_pages_when_idle()` registers the tasks once, from
|
||||
`post_init()`.
|
||||
|
||||
**Never prebuilt.** A holder with a negative order, for a tab that few sessions open
|
||||
and that costs more to build unasked than it saves (the Design tab), and plugin-provided
|
||||
tabs, which are Python-side and not lazy pages.
|
||||
|
||||
## Adopting it
|
||||
|
||||
A lazy tab needs:
|
||||
|
||||
1. The panel derived from `LazyInstance<Panel>`, since a tab's panel has one instance.
|
||||
2. A `LazyPage<Panel>*` member in `MainFrame`, created once in `init_tabpanel()` with
|
||||
its `TAB_ID_*` as the name, its place by the order rule (and a factory if
|
||||
`new Panel(parent)` is not enough), added to `m_lazy_pages`, and used wherever the
|
||||
tab is added to or looked up in the notebook.
|
||||
3. Every use of the panel outside `MainFrame` going through one of the panel's statics,
|
||||
chosen by the rule above. When converting an existing member, `grep` for it; the
|
||||
compiler finds the rest.
|
||||
4. A constructor that copes with the frame already existing and the user being busy
|
||||
elsewhere, since `wxGetApp().mainframe` is set, the frame may be shown, and the user
|
||||
may be typing when a lazy panel is built: no `SetFocus()` while off screen, and
|
||||
whatever the constructor did through a `MainFrame` accessor before (a mode update, a
|
||||
deferred URL) done in the constructor itself.
|
||||
|
||||
To stage a heavy constructor, inherit `StagedBuild`, keep the skeleton in the constructor,
|
||||
move the rest into `add_build_step()` lambdas in the original order, and follow the
|
||||
staged-panel rules above. Measure the units. A step that is still one big widget has to
|
||||
be split inside that widget or accepted as the floor.
|
||||
|
||||
Verification is by log. `MainFrame::prebuild_pages_when_idle` lists the queue it
|
||||
registered, `IdleScheduler::tick` reports each completed task by name at info level and
|
||||
each slice and unit at debug level, and `Lazy::ensure` reports an object a user built
|
||||
on demand with the units and time it took. A run from the configured start
|
||||
page shows every registered page complete, in order, with no unit longer than intended.
|
||||
A click on a tab mid-prebuild shows the finished panel with an `ensure` line for what was
|
||||
left, and the slices resume for the remaining tasks once the user is idle again.
|
||||
@@ -286,6 +286,8 @@ set(SLIC3R_GUI_SOURCES
|
||||
GUI/FilamentMapDialog.hpp
|
||||
GUI/IconManager.cpp
|
||||
GUI/IconManager.hpp
|
||||
GUI/IdleScheduler.cpp
|
||||
GUI/IdleScheduler.hpp
|
||||
GUI/ImageGrid.cpp
|
||||
GUI/ImageGrid.h
|
||||
GUI/ImGuiWrapper.cpp
|
||||
@@ -347,6 +349,10 @@ set(SLIC3R_GUI_SOURCES
|
||||
GUI/KBShortcutsDialog.hpp
|
||||
GUI/KeyChord.cpp
|
||||
GUI/KeyChord.hpp
|
||||
GUI/Lazy.cpp
|
||||
GUI/Lazy.hpp
|
||||
GUI/LazyPage.cpp
|
||||
GUI/LazyPage.hpp
|
||||
GUI/LibVGCode/LibVGCodeWrapper.hpp
|
||||
GUI/LibVGCode/LibVGCodeWrapper.cpp
|
||||
GUI/LinuxDisplayBackend.cpp
|
||||
@@ -433,6 +439,7 @@ set(SLIC3R_GUI_SOURCES
|
||||
GUI/Plater.hpp
|
||||
GUI/PlateSettingsDialog.cpp
|
||||
GUI/PlateSettingsDialog.hpp
|
||||
GUI/PrebuildQueue.hpp
|
||||
GUI/Preferences.cpp
|
||||
GUI/Preferences.hpp
|
||||
GUI/PresetBundleDialog.cpp
|
||||
@@ -512,6 +519,7 @@ set(SLIC3R_GUI_SOURCES
|
||||
GUI/SliceInfoPanel.hpp
|
||||
GUI/SlicingProgressNotification.cpp
|
||||
GUI/SlicingProgressNotification.hpp
|
||||
GUI/StagedBuild.hpp
|
||||
GUI/StatusPanel.cpp
|
||||
GUI/StatusPanel.hpp
|
||||
GUI/StepMeshDialog.cpp
|
||||
|
||||
@@ -863,17 +863,27 @@ void AuxiliaryPanel::init_tabpanel()
|
||||
m_tabpanel->SetBackgroundColour(wxColour("#FEFFFF"));
|
||||
m_tabpanel->Bind(wxEVT_BOOKCTRL_PAGE_CHANGED, [](wxBookCtrlEvent &e) { /* Event handling */ });
|
||||
|
||||
m_designer_panel = new DesignerPanel(m_tabpanel, AuxiliaryFolderType::DESIGNER);
|
||||
m_pictures_panel = new AuFolderPanel(m_tabpanel, AuxiliaryFolderType::MODEL_PICTURE);
|
||||
m_bill_of_materials_panel = new AuFolderPanel(m_tabpanel, AuxiliaryFolderType::BILL_OF_MATERIALS);
|
||||
m_assembly_panel = new AuFolderPanel(m_tabpanel, AuxiliaryFolderType::ASSEMBLY_GUIDE);
|
||||
m_others_panel = new AuFolderPanel(m_tabpanel, AuxiliaryFolderType::OTHERS);
|
||||
|
||||
m_tabpanel->AddPage(m_designer_panel, _L("Basic Info"), true);
|
||||
m_tabpanel->AddPage(m_pictures_panel, _L("Pictures"), false);
|
||||
m_tabpanel->AddPage(m_bill_of_materials_panel, _L("Bill of Materials"), false);
|
||||
m_tabpanel->AddPage(m_assembly_panel, _L("Assembly Guide"), false);
|
||||
m_tabpanel->AddPage(m_others_panel, _L("Others"), false);
|
||||
add_build_step([this] {
|
||||
m_designer_panel = new DesignerPanel(m_tabpanel, AuxiliaryFolderType::DESIGNER);
|
||||
m_tabpanel->AddPage(m_designer_panel, _L("Basic Info"), true);
|
||||
});
|
||||
add_build_step([this] {
|
||||
m_pictures_panel = new AuFolderPanel(m_tabpanel, AuxiliaryFolderType::MODEL_PICTURE);
|
||||
m_tabpanel->AddPage(m_pictures_panel, _L("Pictures"), false);
|
||||
});
|
||||
add_build_step([this] {
|
||||
m_bill_of_materials_panel = new AuFolderPanel(m_tabpanel, AuxiliaryFolderType::BILL_OF_MATERIALS);
|
||||
m_tabpanel->AddPage(m_bill_of_materials_panel, _L("Bill of Materials"), false);
|
||||
});
|
||||
add_build_step([this] {
|
||||
m_assembly_panel = new AuFolderPanel(m_tabpanel, AuxiliaryFolderType::ASSEMBLY_GUIDE);
|
||||
m_tabpanel->AddPage(m_assembly_panel, _L("Assembly Guide"), false);
|
||||
});
|
||||
add_build_step([this] {
|
||||
m_others_panel = new AuFolderPanel(m_tabpanel, AuxiliaryFolderType::OTHERS);
|
||||
m_tabpanel->AddPage(m_others_panel, _L("Others"), false);
|
||||
Layout();
|
||||
});
|
||||
}
|
||||
|
||||
wxWindow *AuxiliaryPanel::create_side_tools()
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#include "slic3r/GUI/UpgradePanel.hpp"
|
||||
#include "slic3r/GUI/AmsWidgets.hpp"
|
||||
#include "Widgets/SideTools.hpp"
|
||||
#include "StagedBuild.hpp"
|
||||
|
||||
#define AUFILE_GREY700 wxColour(107, 107, 107)
|
||||
#define AUFILE_GREY500 wxColour(158, 158, 158)
|
||||
@@ -194,7 +195,7 @@ public:
|
||||
};
|
||||
|
||||
|
||||
class AuxiliaryPanel : public wxPanel
|
||||
class AuxiliaryPanel : public wxPanel, public StagedBuild
|
||||
{
|
||||
private:
|
||||
Tabbook *m_tabpanel = {nullptr};
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#include "libslic3r/CAD/CadDocument.hpp"
|
||||
#include "slic3r/GUI/CAD/DesignInteraction.hpp" // CadLevel: what one Esc press means
|
||||
#include "slic3r/GUI/Lazy.hpp"
|
||||
|
||||
class ComboBox; // Orca dropdown (Widgets/ComboBox.hpp) — replaces wxChoice everywhere here
|
||||
class StaticBox; // Orca rounded card frame (Widgets/StaticBox.hpp)
|
||||
@@ -46,7 +47,7 @@ class DesignCanvas;
|
||||
// Design (CAD) tab: a sketch-first, Onshape-style form-driven CAD panel.
|
||||
// Sketch and Extrude are independent tools: the user creates a Sketch first,
|
||||
// then selects it and Extrudes to produce a solid.
|
||||
class DesignPanel : public wxPanel
|
||||
class DesignPanel : public wxPanel, public LazyInstance<DesignPanel>
|
||||
{
|
||||
public:
|
||||
explicit DesignPanel(wxWindow* parent);
|
||||
|
||||
@@ -1989,11 +1989,10 @@ json action_set_feature_expr(DesignPanel* panel, const json& params)
|
||||
// Dispatch one parsed request ON THE MAIN THREAD. Returns a JSON-RPC reply string.
|
||||
std::string handle_on_main(const std::string& method, const json& params, const json& id)
|
||||
{
|
||||
MainFrame* mf = wxGetApp().mainframe;
|
||||
// The panel is built on first use, and in a headless session nobody clicks the tab that
|
||||
// would build it -- so build it here rather than refusing. Safe: this runs on the main
|
||||
// thread (see the CallAfter that dispatches us).
|
||||
DesignPanel* panel = mf ? mf->ensure_design_panel() : nullptr;
|
||||
DesignPanel* panel = DesignPanel::ensure();
|
||||
if (!panel)
|
||||
return rpc_error(id, -32001, "Design panel not ready");
|
||||
|
||||
|
||||
@@ -491,14 +491,15 @@ void CalibrationPanel::init_tabpanel() {
|
||||
selected);
|
||||
}
|
||||
|
||||
for (int i = 0; i < (int)CALI_MODE_COUNT; i++)
|
||||
add_build_steps_of(*m_cali_panels[i]);
|
||||
|
||||
// ORCA use standard paddings and keep arrow icon for consistent look between sidebars
|
||||
//for (int i = 0; i < (int)CALI_MODE_COUNT; i++)
|
||||
// m_tabpanel->SetPageImage(i, "");
|
||||
|
||||
//auto padding_size = m_tabpanel->GetBtnsListCtrl()->GetPaddingSize(0);
|
||||
//m_tabpanel->GetBtnsListCtrl()->SetPaddingSize({ FromDIP(15), padding_size.y });
|
||||
|
||||
m_initialized = true;
|
||||
}
|
||||
|
||||
void CalibrationPanel::init_timer()
|
||||
@@ -534,6 +535,8 @@ void CalibrationPanel::update_print_error_info(int code, std::string msg, std::s
|
||||
}
|
||||
|
||||
void CalibrationPanel::update_all() {
|
||||
// Every wizard's pages exist once the last build step has run.
|
||||
if (!built()) return;
|
||||
|
||||
NetworkAgent* m_agent = wxGetApp().getAgent();
|
||||
Slic3r::DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager();
|
||||
@@ -597,7 +600,7 @@ void CalibrationPanel::update_all() {
|
||||
|
||||
void CalibrationPanel::show_status(int status)
|
||||
{
|
||||
if (!m_initialized) return;
|
||||
if (!built()) return;
|
||||
if (last_status == status)return;
|
||||
last_status = status;
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "CalibrationWizard.hpp"
|
||||
#include "Tabbook.hpp"
|
||||
#include "Lazy.hpp"
|
||||
//#include "Widgets/SideTools.hpp"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
@@ -88,7 +89,7 @@ private:
|
||||
};
|
||||
|
||||
|
||||
class CalibrationPanel : public wxPanel
|
||||
class CalibrationPanel : public wxPanel, public StagedBuild, public LazyInstance<CalibrationPanel>
|
||||
{
|
||||
public:
|
||||
CalibrationPanel(wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxTAB_TRAVERSAL);
|
||||
@@ -109,7 +110,6 @@ protected:
|
||||
|
||||
|
||||
int last_status;
|
||||
bool m_initialized { false };
|
||||
std::string last_conn_type = "undedefined";
|
||||
MachineObject* obj{ nullptr };
|
||||
MachineObject* last_obj { nullptr };
|
||||
|
||||
@@ -130,6 +130,15 @@ CalibrationWizard::~CalibrationWizard()
|
||||
;
|
||||
}
|
||||
|
||||
void CalibrationWizard::add_page_step(CalibrationWizardPageStep*& step, std::function<CalibrationWizardPage*()> make)
|
||||
{
|
||||
add_build_step([this, &step, make = std::move(make)] {
|
||||
step = new CalibrationWizardPageStep(make());
|
||||
m_all_pages_sizer->Add(step->page, 1, wxEXPAND | wxALL, FromDIP(25));
|
||||
step->page->Hide();
|
||||
});
|
||||
}
|
||||
|
||||
void CalibrationWizard::on_cali_job_finished(wxCommandEvent& event)
|
||||
{
|
||||
this->on_cali_job_finished(event.GetString());
|
||||
@@ -520,33 +529,28 @@ void PressureAdvanceWizard::on_cali_job_finished(wxString evt_data)
|
||||
|
||||
void PressureAdvanceWizard::create_pages()
|
||||
{
|
||||
start_step = new CalibrationWizardPageStep(new CalibrationPAStartPage(m_scrolledWindow));
|
||||
preset_step = new CalibrationWizardPageStep(new CalibrationPresetPage(m_scrolledWindow, m_mode, false));
|
||||
cali_step = new CalibrationWizardPageStep(new CalibrationCaliPage(m_scrolledWindow, m_mode));
|
||||
save_step = new CalibrationWizardPageStep(new CalibrationPASavePage(m_scrolledWindow));
|
||||
add_page_step(start_step, [this] { return new CalibrationPAStartPage(m_scrolledWindow); });
|
||||
add_page_step(preset_step, [this] { return new CalibrationPresetPage(m_scrolledWindow, m_mode, false); });
|
||||
add_page_step(cali_step, [this] { return new CalibrationCaliPage(m_scrolledWindow, m_mode); });
|
||||
add_page_step(save_step, [this] { return new CalibrationPASavePage(m_scrolledWindow); });
|
||||
|
||||
m_all_pages_sizer->Add(start_step->page, 1, wxEXPAND | wxALL, FromDIP(25));
|
||||
m_all_pages_sizer->Add(preset_step->page, 1, wxEXPAND | wxALL, FromDIP(25));
|
||||
m_all_pages_sizer->Add(cali_step->page, 1, wxEXPAND | wxALL, FromDIP(25));
|
||||
m_all_pages_sizer->Add(save_step->page, 1, wxEXPAND | wxALL, FromDIP(25));
|
||||
add_build_step([this] {
|
||||
m_page_steps.push_back(start_step);
|
||||
m_page_steps.push_back(preset_step);
|
||||
m_page_steps.push_back(cali_step);
|
||||
m_page_steps.push_back(save_step);
|
||||
|
||||
for (int i = 0; i < m_page_steps.size() -1; i++) {
|
||||
m_page_steps[i]->chain(m_page_steps[i+1]);
|
||||
}
|
||||
|
||||
m_page_steps.push_back(start_step);
|
||||
m_page_steps.push_back(preset_step);
|
||||
m_page_steps.push_back(cali_step);
|
||||
m_page_steps.push_back(save_step);
|
||||
for (int i = 0; i < m_page_steps.size(); i++) {
|
||||
m_page_steps[i]->page->Bind(EVT_CALI_ACTION, &PressureAdvanceWizard::on_cali_action, this);
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_page_steps.size() -1; i++) {
|
||||
m_page_steps[i]->chain(m_page_steps[i+1]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_page_steps.size(); i++) {
|
||||
m_page_steps[i]->page->Hide();
|
||||
m_page_steps[i]->page->Bind(EVT_CALI_ACTION, &PressureAdvanceWizard::on_cali_action, this);
|
||||
}
|
||||
|
||||
if (!m_page_steps.empty())
|
||||
show_step(m_page_steps.front());
|
||||
if (!m_page_steps.empty())
|
||||
show_step(m_page_steps.front());
|
||||
});
|
||||
}
|
||||
|
||||
void PressureAdvanceWizard::on_cali_action(wxCommandEvent& evt)
|
||||
@@ -1053,59 +1057,46 @@ FlowRateWizard::FlowRateWizard(wxWindow* parent, wxWindowID id, const wxPoint& p
|
||||
|
||||
void FlowRateWizard::create_pages()
|
||||
{
|
||||
start_step = new CalibrationWizardPageStep(new CalibrationFlowRateStartPage(m_scrolledWindow));
|
||||
preset_step = new CalibrationWizardPageStep(new CalibrationPresetPage(m_scrolledWindow, m_mode, false));
|
||||
add_page_step(start_step, [this] { return new CalibrationFlowRateStartPage(m_scrolledWindow); });
|
||||
add_page_step(preset_step, [this] { return new CalibrationPresetPage(m_scrolledWindow, m_mode, false); });
|
||||
|
||||
// manual
|
||||
cali_coarse_step = new CalibrationWizardPageStep(new CalibrationCaliPage(m_scrolledWindow, m_mode, CaliPageType::CALI_PAGE_CALI));
|
||||
coarse_save_step = new CalibrationWizardPageStep(new CalibrationFlowCoarseSavePage(m_scrolledWindow));
|
||||
cali_fine_step = new CalibrationWizardPageStep(new CalibrationCaliPage(m_scrolledWindow, m_mode, CaliPageType::CALI_PAGE_FINE_CALI));
|
||||
fine_save_step = new CalibrationWizardPageStep(new CalibrationFlowFineSavePage(m_scrolledWindow));
|
||||
add_page_step(cali_coarse_step, [this] { return new CalibrationCaliPage(m_scrolledWindow, m_mode, CaliPageType::CALI_PAGE_CALI); });
|
||||
add_page_step(coarse_save_step, [this] { return new CalibrationFlowCoarseSavePage(m_scrolledWindow); });
|
||||
add_page_step(cali_fine_step, [this] { return new CalibrationCaliPage(m_scrolledWindow, m_mode, CaliPageType::CALI_PAGE_FINE_CALI); });
|
||||
add_page_step(fine_save_step, [this] { return new CalibrationFlowFineSavePage(m_scrolledWindow); });
|
||||
|
||||
// auto
|
||||
cali_step = new CalibrationWizardPageStep(new CalibrationCaliPage(m_scrolledWindow, m_mode));
|
||||
save_step = new CalibrationWizardPageStep(new CalibrationFlowX1SavePage(m_scrolledWindow));
|
||||
add_page_step(cali_step, [this] { return new CalibrationCaliPage(m_scrolledWindow, m_mode); });
|
||||
add_page_step(save_step, [this] { return new CalibrationFlowX1SavePage(m_scrolledWindow); });
|
||||
|
||||
m_all_pages_sizer->Add(start_step->page, 1, wxEXPAND | wxALL, FromDIP(25));
|
||||
m_all_pages_sizer->Add(preset_step->page, 1, wxEXPAND | wxALL, FromDIP(25));
|
||||
m_all_pages_sizer->Add(cali_coarse_step->page, 1, wxEXPAND | wxALL, FromDIP(25));
|
||||
m_all_pages_sizer->Add(coarse_save_step->page, 1, wxEXPAND | wxALL, FromDIP(25));
|
||||
m_all_pages_sizer->Add(cali_fine_step->page, 1, wxEXPAND | wxALL, FromDIP(25));
|
||||
m_all_pages_sizer->Add(fine_save_step->page, 1, wxEXPAND | wxALL, FromDIP(25));
|
||||
add_build_step([this] {
|
||||
m_page_steps.push_back(start_step);
|
||||
m_page_steps.push_back(preset_step);
|
||||
m_page_steps.push_back(cali_coarse_step);
|
||||
m_page_steps.push_back(coarse_save_step);
|
||||
m_page_steps.push_back(cali_fine_step);
|
||||
m_page_steps.push_back(fine_save_step);
|
||||
|
||||
m_all_pages_sizer->Add(cali_step->page, 1, wxEXPAND | wxALL, FromDIP(25));
|
||||
m_all_pages_sizer->Add(save_step->page, 1, wxEXPAND | wxALL, FromDIP(25));
|
||||
//m_page_steps.push_back(cali_step);
|
||||
//m_page_steps.push_back(save_step);
|
||||
|
||||
m_page_steps.push_back(start_step);
|
||||
m_page_steps.push_back(preset_step);
|
||||
m_page_steps.push_back(cali_coarse_step);
|
||||
m_page_steps.push_back(coarse_save_step);
|
||||
m_page_steps.push_back(cali_fine_step);
|
||||
m_page_steps.push_back(fine_save_step);
|
||||
for (int i = 0; i < m_page_steps.size() - 1; i++) {
|
||||
m_page_steps[i]->chain(m_page_steps[i + 1]);
|
||||
}
|
||||
|
||||
//m_page_steps.push_back(cali_step);
|
||||
//m_page_steps.push_back(save_step);
|
||||
for (int i = 0; i < m_page_steps.size(); i++) {
|
||||
m_page_steps[i]->page->Bind(EVT_CALI_ACTION, &FlowRateWizard::on_cali_action, this);
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_page_steps.size() - 1; i++) {
|
||||
m_page_steps[i]->chain(m_page_steps[i + 1]);
|
||||
}
|
||||
cali_step->page->Bind(EVT_CALI_ACTION, &FlowRateWizard::on_cali_action, this);
|
||||
save_step->page->Bind(EVT_CALI_ACTION, &FlowRateWizard::on_cali_action, this);
|
||||
|
||||
// hide all pages
|
||||
cali_step->page->Hide();
|
||||
save_step->page->Hide();
|
||||
for (int i = 0; i < m_page_steps.size(); i++) {
|
||||
m_page_steps[i]->page->Hide();
|
||||
m_page_steps[i]->page->Bind(EVT_CALI_ACTION, &FlowRateWizard::on_cali_action, this);
|
||||
}
|
||||
if (!m_page_steps.empty())
|
||||
show_step(m_page_steps.front());
|
||||
|
||||
|
||||
cali_step->page->Bind(EVT_CALI_ACTION, &FlowRateWizard::on_cali_action, this);
|
||||
save_step->page->Bind(EVT_CALI_ACTION, &FlowRateWizard::on_cali_action, this);
|
||||
|
||||
if (!m_page_steps.empty())
|
||||
show_step(m_page_steps.front());
|
||||
|
||||
set_cali_method(CalibrationMethod::CALI_METHOD_MANUAL);
|
||||
set_cali_method(CalibrationMethod::CALI_METHOD_MANUAL);
|
||||
});
|
||||
}
|
||||
|
||||
void FlowRateWizard::on_cali_action(wxCommandEvent& evt)
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "CalibrationWizardPresetPage.hpp"
|
||||
#include "CalibrationWizardCaliPage.hpp"
|
||||
#include "CalibrationWizardSavePage.hpp"
|
||||
#include "StagedBuild.hpp"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
@@ -36,7 +37,7 @@ struct ConfigIndexValue
|
||||
int index{0};
|
||||
};
|
||||
|
||||
class CalibrationWizard : public wxPanel {
|
||||
class CalibrationWizard : public wxPanel, public StagedBuild {
|
||||
public:
|
||||
CalibrationWizard(wxWindow* parent, CalibMode mode,
|
||||
wxWindowID id = wxID_ANY,
|
||||
@@ -79,6 +80,9 @@ public:
|
||||
protected:
|
||||
void on_cali_go_home();
|
||||
|
||||
// Queues a page as a build step, created hidden and added to the pages sizer.
|
||||
void add_page_step(CalibrationWizardPageStep*& step, std::function<CalibrationWizardPage*()> make);
|
||||
|
||||
protected:
|
||||
/* wx widgets*/
|
||||
wxScrolledWindow* m_scrolledWindow;
|
||||
|
||||
@@ -192,8 +192,10 @@ void CalibrationCaliPage::update(MachineObject* obj)
|
||||
set_cali_img();
|
||||
}
|
||||
|
||||
// A calibration can run before the Device tab is ever opened, and only its status
|
||||
// panel shows a print error.
|
||||
if (obj->print_error > 0) {
|
||||
StatusPanel* status_panel = Slic3r::GUI::wxGetApp().mainframe->m_monitor->get_status_panel();
|
||||
StatusPanel* status_panel = MonitorPanel::ensure()->get_status_panel();
|
||||
status_panel->obj = obj;
|
||||
status_panel->update_error_message();
|
||||
}
|
||||
|
||||
@@ -538,7 +538,8 @@ void DeviceErrorDialog::on_button_click(ActionButton btn_id)
|
||||
break;
|
||||
}
|
||||
case DeviceErrorDialog::CHECK_ASSISTANT: {
|
||||
wxGetApp().mainframe->m_monitor->jump_to_HMS(); // go to assistant page
|
||||
if (MonitorPanel* monitor = MonitorPanel::if_built())
|
||||
monitor->jump_to_HMS(); // go to assistant page
|
||||
break;
|
||||
}
|
||||
case DeviceErrorDialog::FILAMENT_EXTRUDED: {
|
||||
@@ -568,7 +569,8 @@ void DeviceErrorDialog::on_button_click(ActionButton btn_id)
|
||||
}
|
||||
case DeviceErrorDialog::JUMP_TO_LIVEVIEW: {
|
||||
Slic3r::GUI::wxGetApp().mainframe->jump_to_monitor();
|
||||
Slic3r::GUI::wxGetApp().mainframe->m_monitor->jump_to_LiveView();
|
||||
if (MonitorPanel* monitor = MonitorPanel::if_built())
|
||||
monitor->jump_to_LiveView();
|
||||
break;
|
||||
}
|
||||
case DeviceErrorDialog::NO_REMINDER_NEXT_TIME: {
|
||||
@@ -618,7 +620,8 @@ void DeviceErrorDialog::on_button_click(ActionButton btn_id)
|
||||
}
|
||||
case DeviceErrorDialog::OK_JUMP_RACK: {
|
||||
Slic3r::GUI::wxGetApp().mainframe->jump_to_monitor();
|
||||
Slic3r::GUI::wxGetApp().mainframe->m_monitor->jump_to_Rack();
|
||||
if (MonitorPanel* monitor = MonitorPanel::if_built())
|
||||
monitor->jump_to_Rack();
|
||||
break;
|
||||
}
|
||||
case DeviceErrorDialog::ABORT: {
|
||||
|
||||
@@ -934,7 +934,8 @@ void wgtDeviceNozzleRackNozzleItem::OnBtnNozzleStatus(wxMouseEvent& evt)
|
||||
dlg.AddButton(wxID_OK,_L("Jump to the upgrade page"), true);
|
||||
|
||||
if (dlg.ShowModal() == wxID_OK) {
|
||||
wxGetApp().mainframe->m_monitor->jump_to_Upgrade();
|
||||
if (MonitorPanel* monitor = MonitorPanel::if_built())
|
||||
monitor->jump_to_Upgrade();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,7 +344,8 @@ void wgtDeviceNozzleRackHotendUpdate::OnStatusIconClick(wxMouseEvent& event)
|
||||
|
||||
if (dlg.ShowModal() == wxID_OK)
|
||||
{
|
||||
wxGetApp().mainframe->m_monitor->jump_to_Upgrade();
|
||||
if (MonitorPanel* monitor = MonitorPanel::if_built())
|
||||
monitor->jump_to_Upgrade();
|
||||
|
||||
wxCommandEvent evt(wxEVT_NOZZLE_JUMP_UPGRADE, GetId());
|
||||
evt.SetEventObject(this);
|
||||
|
||||
+57
-32
@@ -863,7 +863,7 @@ void GUI_App::post_init()
|
||||
mainframe->Freeze();
|
||||
#endif
|
||||
plater_->canvas3D()->enable_render(false);
|
||||
mainframe->select_tab(TAB_ID_PREPARE);
|
||||
mainframe->select_prepare_for_gl_init();
|
||||
plater_->select_view_3D("3D");
|
||||
// The first render happens before the queued new_project() sets the same view.
|
||||
plater_->get_camera().select_view("topfront");
|
||||
@@ -903,10 +903,10 @@ void GUI_App::post_init()
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", finished rendering a first frame for test";
|
||||
}
|
||||
}
|
||||
if (is_editor())
|
||||
mainframe->select_tab(TAB_ID_HOME);
|
||||
if (app_config->get("default_page") == "1")
|
||||
if (starts_on_prepare())
|
||||
mainframe->select_tab(TAB_ID_PREPARE);
|
||||
else if (is_editor())
|
||||
mainframe->select_tab(TAB_ID_HOME);
|
||||
#ifndef __linux__
|
||||
mainframe->Thaw();
|
||||
#endif
|
||||
@@ -915,6 +915,7 @@ void GUI_App::post_init()
|
||||
|
||||
plater_->trigger_restore_project(1);
|
||||
//#endif
|
||||
mainframe->prebuild_pages_when_idle();
|
||||
|
||||
//BBS: remove GCodeViewer as seperate APP logic
|
||||
/*if (this->init_params->start_as_gcodeviewer) {
|
||||
@@ -1929,9 +1930,9 @@ bool GUI_App::hot_reload_network_plugin()
|
||||
m_device_manager->add_user_subscribe();
|
||||
}
|
||||
|
||||
if (mainframe && mainframe->m_monitor) {
|
||||
mainframe->m_monitor->update_network_version_footer();
|
||||
mainframe->m_monitor->set_default();
|
||||
if (MonitorPanel* monitor = MonitorPanel::if_built()) {
|
||||
monitor->update_network_version_footer();
|
||||
monitor->set_default();
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": reset monitor panel";
|
||||
}
|
||||
|
||||
@@ -3422,14 +3423,16 @@ bool GUI_App::on_init_inner()
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(info) << "create the main window";
|
||||
mainframe = new MainFrame();
|
||||
// hide settings tabs after first Layout
|
||||
if (is_editor()) {
|
||||
mainframe->select_tab(TAB_ID_HOME);
|
||||
if (starts_on_prepare()) {
|
||||
mainframe->select_tab(TAB_ID_PREPARE);
|
||||
} else {
|
||||
mainframe->select_tab(TAB_ID_HOME);
|
||||
}
|
||||
}
|
||||
|
||||
sidebar().obj_list()->init();
|
||||
//sidebar().aux_list()->init_auxiliary();
|
||||
mainframe->m_project->init_auxiliary();
|
||||
|
||||
// update_mode(); // !!! do that later
|
||||
SetTopWindow(mainframe);
|
||||
@@ -4140,13 +4143,13 @@ void GUI_App::select_machine(const std::string& agent_id)
|
||||
|
||||
// Use MonitorPanel::select_machine() to trigger full selection flow
|
||||
// This reuses existing logic for machine switching (UI updates, callbacks, etc.)
|
||||
if (mainframe && mainframe->m_monitor) {
|
||||
mainframe->m_monitor->select_machine(dev_id);
|
||||
if (MonitorPanel* monitor = MonitorPanel::if_built()) {
|
||||
monitor->select_machine(dev_id);
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": triggered select_machine for dev_id=" << dev_id;
|
||||
} else {
|
||||
// Fallback if MonitorPanel not available
|
||||
m_device_manager->set_selected_machine(dev_id);
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": fallback set_selected_machine dev_id=" << dev_id;
|
||||
} else if (m_device_manager->set_selected_machine(dev_id)) {
|
||||
// The Device tab's own state is set when the tab is built.
|
||||
MonitorPanel::on_machine_selected(m_device_manager->get_selected_machine());
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": set_selected_machine dev_id=" << dev_id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4670,6 +4673,7 @@ void GUI_App::recreate_GUI(const wxString &msg_name)
|
||||
|
||||
//BBS: trigger restore project logic here, and skip confirm
|
||||
plater_->trigger_restore_project(1);
|
||||
mainframe->prebuild_pages_when_idle();
|
||||
|
||||
// #ys_FIXME_delete_after_testing Do we still need this ?
|
||||
// CallAfter([]() {
|
||||
@@ -5011,7 +5015,8 @@ void GUI_App::get_login_info(const std::string& provider/* = ORCA_CLOUD_PROVIDER
|
||||
wxString strJS = wxString::Format("window.postMessage(%s)", from_u8(logout_cmd));
|
||||
GUI::wxGetApp().run_script(strJS);
|
||||
}
|
||||
mainframe->m_webview->SetLoginPanelVisibility(true);
|
||||
if (WebViewPanel* home = WebViewPanel::if_built())
|
||||
home->SetLoginPanelVisibility(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5143,9 +5148,9 @@ std::string GUI_App::handle_web_request(std::string cmd)
|
||||
"homepage_bambu_login_or_register",
|
||||
};
|
||||
if (app_config->get_stealth_mode() && stealth_blocked_info_commands.count(command_str)) {
|
||||
CallAfter([this] {
|
||||
if (mainframe && mainframe->m_webview)
|
||||
mainframe->m_webview->SendCloudProvidersInfo();
|
||||
CallAfter([] {
|
||||
if (WebViewPanel* home = WebViewPanel::if_built())
|
||||
home->SendCloudProvidersInfo();
|
||||
});
|
||||
return "";
|
||||
}
|
||||
@@ -5159,8 +5164,8 @@ std::string GUI_App::handle_web_request(std::string cmd)
|
||||
if (dlg.ShowModal() == wxID_OK) {
|
||||
app_config->set_bool("stealth_mode", false);
|
||||
app_config->save();
|
||||
if (mainframe && mainframe->m_webview)
|
||||
mainframe->m_webview->SendCloudProvidersInfo();
|
||||
if (WebViewPanel* home = WebViewPanel::if_built())
|
||||
home->SendCloudProvidersInfo();
|
||||
// Continue with login
|
||||
if (command_str == "homepage_login_or_register")
|
||||
this->request_login(true);
|
||||
@@ -5241,8 +5246,8 @@ std::string GUI_App::handle_web_request(std::string cmd)
|
||||
}
|
||||
else if (command_str.compare("get_recent_projects") == 0) {
|
||||
if (mainframe) {
|
||||
if (mainframe->m_webview) {
|
||||
mainframe->m_webview->SendRecentList(INT_MAX);
|
||||
if (WebViewPanel* home = WebViewPanel::if_built()) {
|
||||
home->SendRecentList(INT_MAX);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7775,8 +7780,8 @@ void GUI_App::on_stealth_mode_enter()
|
||||
BOOST_LOG_TRIVIAL(info) << "logout: on_stealth_mode_enter";
|
||||
request_user_logout(ORCA_CLOUD_PROVIDER);
|
||||
request_user_logout(BBL_CLOUD_PROVIDER);
|
||||
if (mainframe && mainframe->m_webview) {
|
||||
mainframe->m_webview->SendCloudProvidersInfo();
|
||||
if (WebViewPanel* home = WebViewPanel::if_built()) {
|
||||
home->SendCloudProvidersInfo();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8186,6 +8191,24 @@ ConfigOptionMode GUI_App::get_saved_mode()
|
||||
return saved_mode_from_string(app_config->get("user_mode"));
|
||||
}
|
||||
|
||||
bool GUI_App::starts_on_prepare() const
|
||||
{
|
||||
return app_config->get("default_page") == "1";
|
||||
}
|
||||
|
||||
int GUI_App::input_idle_ms() const
|
||||
{
|
||||
return int(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - m_last_input).count());
|
||||
}
|
||||
|
||||
// Every wxCommandEvent claims the user-input category, so only real mouse and key events count.
|
||||
int GUI_App::FilterEvent(wxEvent& event)
|
||||
{
|
||||
if (!event.IsCommandEvent() && (event.GetEventCategory() & wxEVT_CATEGORY_USER_INPUT))
|
||||
m_last_input = std::chrono::steady_clock::now();
|
||||
return Event_Skip;
|
||||
}
|
||||
|
||||
ConfigOptionMode GUI_App::get_mode()
|
||||
{
|
||||
return app_config->get_bool("developer_mode") ? comDevelop : get_saved_mode();
|
||||
@@ -8237,9 +8260,10 @@ void GUI_App::update_mode()
|
||||
mainframe->m_param_panel->update_mode();
|
||||
if (mainframe->m_param_dialog)
|
||||
mainframe->m_param_dialog->panel()->update_mode();
|
||||
if (mainframe->m_printer_view)
|
||||
mainframe->m_printer_view->update_mode();
|
||||
mainframe->m_webview->update_mode();
|
||||
if (PrinterWebView* view = PrinterWebView::if_built())
|
||||
view->update_mode();
|
||||
if (WebViewPanel* home = WebViewPanel::if_built())
|
||||
home->update_mode();
|
||||
|
||||
#ifdef _MSW_DARK_MODE
|
||||
if (!wxGetApp().tabs_as_menu())
|
||||
@@ -8257,9 +8281,10 @@ void GUI_App::update_mode()
|
||||
}
|
||||
|
||||
void GUI_App::update_internal_development() {
|
||||
mainframe->m_webview->update_mode();
|
||||
if (mainframe->m_printer_view)
|
||||
mainframe->m_printer_view->update_mode();
|
||||
if (WebViewPanel* home = WebViewPanel::if_built())
|
||||
home->update_mode();
|
||||
if (PrinterWebView* view = PrinterWebView::if_built())
|
||||
view->update_mode();
|
||||
}
|
||||
|
||||
void GUI_App::show_ip_address_enter_dialog(wxString title)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef slic3r_GUI_App_hpp_
|
||||
#define slic3r_GUI_App_hpp_
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -248,6 +249,7 @@ private:
|
||||
bool m_app_conf_exists{ false };
|
||||
EAppMode m_app_mode{ EAppMode::Editor };
|
||||
bool m_is_recreating_gui{ false };
|
||||
std::chrono::steady_clock::time_point m_last_input{ std::chrono::steady_clock::now() };
|
||||
#ifdef __linux__
|
||||
bool m_opengl_initialized{ false };
|
||||
#endif
|
||||
@@ -387,6 +389,11 @@ public:
|
||||
bool is_editor() const { return m_app_mode == EAppMode::Editor; }
|
||||
bool is_gcode_viewer() const { return m_app_mode == EAppMode::GCodeViewer; }
|
||||
bool is_recreating_gui() const { return m_is_recreating_gui; }
|
||||
// Milliseconds since the last mouse or keyboard event the app processed.
|
||||
int input_idle_ms() const;
|
||||
int FilterEvent(wxEvent& event) override;
|
||||
// The Preferences "Default page" choice, stored as its index: 0 Home, 1 Prepare.
|
||||
bool starts_on_prepare() const;
|
||||
std::string logo_name() const { return is_editor() ? "OrcaSlicer" : "OrcaSlicer-gcodeviewer"; }
|
||||
|
||||
bool is_closing() const { return m_is_closing.load(std::memory_order_acquire); }
|
||||
|
||||
@@ -122,7 +122,8 @@ HMSNotifyItem::HMSNotifyItem(const std::string& dev_id, wxWindow *parent, DevHMS
|
||||
m_hms_content->Bind(wxEVT_LEFT_UP, [this](wxMouseEvent& e) {
|
||||
wxCommandEvent evt(EVT_ALREADY_READ_HMS);
|
||||
evt.SetString(long_error_code);
|
||||
wxPostEvent(wxGetApp().mainframe->m_monitor, evt);
|
||||
if (MonitorPanel* monitor = MonitorPanel::if_built())
|
||||
wxPostEvent(monitor, evt);
|
||||
|
||||
if (!m_url.empty()) wxLaunchDefaultBrowser(m_url);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
#include "IdleScheduler.hpp"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include "libslic3r/Utils.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
namespace {
|
||||
|
||||
// The timer's period while waiting for the user to go quiet.
|
||||
constexpr int tick_ms = 250;
|
||||
// Input-free time before a slice may start, about a double-click interval, so a user
|
||||
// mid-gesture is left alone.
|
||||
constexpr int quiet_ms = 500;
|
||||
// Budget of one slice. The next slice is a timer message, so paint, timers and input
|
||||
// queued meanwhile are handled first; a click waits at most a slice plus the unit that
|
||||
// overran it.
|
||||
constexpr int slice_ms = 40;
|
||||
// On GTK a due timer runs ahead of repaints and posted events, so the next slice waits a few ms.
|
||||
#ifdef __WXGTK__
|
||||
constexpr int next_slice_ms = 5;
|
||||
#else
|
||||
constexpr int next_slice_ms = 0;
|
||||
#endif
|
||||
|
||||
// True when unhandled keyboard, button, touch or pen input is queued; only Windows can ask.
|
||||
bool input_pending()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// Windows synthesizes a mouse move whenever a window appears under the cursor, so those
|
||||
// do not count.
|
||||
return (GetQueueStatus(QS_INPUT & ~QS_MOUSEMOVE) >> 16) != 0;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
IdleScheduler::IdleScheduler(std::function<int()> input_idle_ms) : m_input_idle_ms(std::move(input_idle_ms))
|
||||
{
|
||||
m_timer.Bind(wxEVT_TIMER, [this](wxTimerEvent&) { tick(); });
|
||||
}
|
||||
|
||||
void IdleScheduler::start()
|
||||
{
|
||||
if (!m_timer.IsRunning())
|
||||
m_timer.Start(tick_ms);
|
||||
}
|
||||
|
||||
void IdleScheduler::stop()
|
||||
{
|
||||
m_timer.Stop();
|
||||
}
|
||||
|
||||
void IdleScheduler::tick()
|
||||
{
|
||||
// A unit that pumps the event loop lets the timer fire inside its own slice.
|
||||
if (m_in_slice)
|
||||
return;
|
||||
if (!m_queue.pending()) {
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
if (m_input_idle_ms() < quiet_ms || input_pending()) {
|
||||
start();
|
||||
return;
|
||||
}
|
||||
const auto now_ms = [] {
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
|
||||
};
|
||||
const char* const fn = __FUNCTION__;
|
||||
m_in_slice = true;
|
||||
PrebuildQueue::Slice slice;
|
||||
{
|
||||
ScopeGuard in_slice([this] { m_in_slice = false; });
|
||||
slice = m_queue.run_slice(slice_ms, now_ms, input_pending, [fn](const std::string& name, long long ms) {
|
||||
BOOST_LOG_TRIVIAL(debug) << fn << ": " << name << ": unit took " << ms << " ms";
|
||||
});
|
||||
}
|
||||
if (slice.completed)
|
||||
BOOST_LOG_TRIVIAL(info) << fn << ": task complete: " << slice.name << ", " << slice.units << " unit(s) in " << slice.ms << " ms this slice";
|
||||
else
|
||||
BOOST_LOG_TRIVIAL(debug) << fn << ": " << slice.name << ": " << slice.units << " unit(s) in " << slice.ms << " ms, yielding";
|
||||
if (!slice.remaining)
|
||||
stop();
|
||||
else if (input_pending())
|
||||
m_timer.Start(tick_ms);
|
||||
else
|
||||
m_timer.StartOnce(next_slice_ms);
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#include <wx/event.h>
|
||||
#include <wx/timer.h>
|
||||
|
||||
#include "PrebuildQueue.hpp"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
// Runs the queue's units while the user is idle. Once the user has been idle long enough,
|
||||
// slices run one per timer message until the work is done or input arrives, so the event
|
||||
// loop handles what it has between slices. Main thread only.
|
||||
class IdleScheduler
|
||||
{
|
||||
public:
|
||||
// input_idle_ms reports how long ago the user last touched the mouse or keyboard.
|
||||
explicit IdleScheduler(std::function<int()> input_idle_ms);
|
||||
|
||||
void add(LazyBase& task) { m_queue.add(task); }
|
||||
void clear() { m_queue.clear(); }
|
||||
// The task names in queue order, for a log line.
|
||||
std::string names() const { return m_queue.names(); }
|
||||
|
||||
// Arms the timer; it stops itself once no task is pending, so call again when a task
|
||||
// appears.
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
private:
|
||||
void tick();
|
||||
|
||||
PrebuildQueue m_queue;
|
||||
wxTimer m_timer;
|
||||
std::function<int()> m_input_idle_ms;
|
||||
bool m_in_slice{ false };
|
||||
};
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
@@ -0,0 +1,27 @@
|
||||
#include "Lazy.hpp"
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <wx/app.h>
|
||||
#include <wx/utils.h>
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
LazyBase::OnDemandBuild::OnDemandBuild(const LazyBase& lazy) : m_lazy(lazy), m_started(std::chrono::steady_clock::now())
|
||||
{
|
||||
// The unit tests have no app.
|
||||
if (wxTheApp != nullptr)
|
||||
m_busy = std::make_unique<wxBusyCursor>();
|
||||
}
|
||||
|
||||
LazyBase::OnDemandBuild::~OnDemandBuild()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "Lazy::ensure: built " << m_lazy.name() << " on demand, " << m_units << " unit(s) in "
|
||||
<< std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - m_started).count() << " ms";
|
||||
}
|
||||
|
||||
void LazyBase::log_null_factory(const std::string& name)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error) << "Lazy::build_step: the factory for " << name << " returned null";
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
@@ -0,0 +1,198 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "StagedBuild.hpp"
|
||||
|
||||
class wxBusyCursor;
|
||||
|
||||
// Deferred construction of one object. Lazy<T> holds the factory and builds the object on
|
||||
// demand or, unit by unit, from an IdleScheduler; LazyBase is what the scheduler sees of
|
||||
// it; LazyInstance<Self> gives a type with one such object app-wide static access to it.
|
||||
// docs/HLSD/deferred-page-construction.md covers the design.
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
template <class Self> class LazyInstance;
|
||||
|
||||
// A prebuild task: what PrebuildQueue sees of a Lazy<T>. A type with its own unit sequence
|
||||
// implements it directly.
|
||||
class LazyBase
|
||||
{
|
||||
public:
|
||||
virtual ~LazyBase() = default;
|
||||
|
||||
// Labels the object in logs.
|
||||
virtual const std::string& name() const = 0;
|
||||
virtual bool built() const = 0;
|
||||
// Whether the idle prebuild has work here.
|
||||
virtual bool pending() const { return !built(); }
|
||||
// Runs one unit and returns true while more remain.
|
||||
virtual bool build_step() = 0;
|
||||
// Position in the idle queue: lower builds first, negative is never prebuilt.
|
||||
virtual int prebuild_order() const = 0;
|
||||
|
||||
protected:
|
||||
// Busy cursor around a build the user is waiting on, and a log line after it.
|
||||
class OnDemandBuild
|
||||
{
|
||||
public:
|
||||
explicit OnDemandBuild(const LazyBase& lazy);
|
||||
~OnDemandBuild();
|
||||
void unit() { ++m_units; }
|
||||
|
||||
private:
|
||||
const LazyBase& m_lazy;
|
||||
std::unique_ptr<wxBusyCursor> m_busy;
|
||||
std::chrono::steady_clock::time_point m_started;
|
||||
int m_units{ 0 };
|
||||
};
|
||||
|
||||
static void log_null_factory(const std::string& name);
|
||||
};
|
||||
|
||||
// Holds an object a factory makes on the first build_step() or ensure(), followed by one
|
||||
// StagedBuild step per unit if the type has them, and keeps callbacks until the object is
|
||||
// complete. The object's parent owns it, not the holder. A LazyInstance type is registered
|
||||
// for its statics; any other type is reached only through the holder.
|
||||
template <class T>
|
||||
class Lazy : public LazyBase
|
||||
{
|
||||
public:
|
||||
using Factory = std::function<T*()>;
|
||||
|
||||
Lazy(std::string name, int order, Factory make) : m_name(std::move(name)), m_order(order), m_make(std::move(make))
|
||||
{
|
||||
if constexpr (registered())
|
||||
LazyInstance<T>::s_lazy = this;
|
||||
}
|
||||
~Lazy() override
|
||||
{
|
||||
if constexpr (registered())
|
||||
if (LazyInstance<T>::s_lazy == this)
|
||||
LazyInstance<T>::s_lazy = nullptr;
|
||||
}
|
||||
Lazy(const Lazy&) = delete;
|
||||
Lazy& operator=(const Lazy&) = delete;
|
||||
|
||||
// Null until completely built.
|
||||
T* get() const { return built() ? m_object : nullptr; }
|
||||
|
||||
// Builds whatever is left now and returns the object, null if the factory returned null
|
||||
// or a nested call finds the object mid-build.
|
||||
T* ensure()
|
||||
{
|
||||
if (!built() && !m_building) {
|
||||
OnDemandBuild build(*this);
|
||||
do
|
||||
build.unit();
|
||||
while (build_step());
|
||||
}
|
||||
return get();
|
||||
}
|
||||
|
||||
// Runs fn on the object now if it is built, otherwise once it is.
|
||||
void when_built(std::function<void(T&)> fn)
|
||||
{
|
||||
if (built())
|
||||
fn(*m_object);
|
||||
else
|
||||
m_deferred.push_back(std::move(fn));
|
||||
}
|
||||
|
||||
const std::string& name() const override { return m_name; }
|
||||
// Read by a worker thread through the statics; the last write in build_step() releases it.
|
||||
bool built() const override { return m_complete.load(std::memory_order_acquire); }
|
||||
bool pending() const override { return !built() && !m_failed; }
|
||||
int prebuild_order() const override { return m_order; }
|
||||
|
||||
// The factory is the first unit, then one StagedBuild step each. A unit that pumps the
|
||||
// event loop cannot re-enter; a nested call does nothing.
|
||||
bool build_step() override
|
||||
{
|
||||
if (built() || m_building || m_failed)
|
||||
return false;
|
||||
m_building = true;
|
||||
try {
|
||||
if (m_object == nullptr)
|
||||
m_object = m_make();
|
||||
else
|
||||
step();
|
||||
} catch (...) {
|
||||
m_building = false;
|
||||
throw;
|
||||
}
|
||||
m_building = false;
|
||||
if (m_object == nullptr) {
|
||||
m_failed = true;
|
||||
log_null_factory(m_name);
|
||||
return false;
|
||||
}
|
||||
if (steps_remain())
|
||||
return true;
|
||||
m_complete.store(true, std::memory_order_release);
|
||||
for (auto& fn : m_deferred)
|
||||
fn(*m_object);
|
||||
m_deferred.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr bool registered() { return std::is_base_of_v<LazyInstance<T>, T>; }
|
||||
static constexpr bool staged() { return std::is_base_of_v<StagedBuild, T>; }
|
||||
|
||||
bool steps_remain() const
|
||||
{
|
||||
if constexpr (staged())
|
||||
return !m_object->built();
|
||||
else
|
||||
return false;
|
||||
}
|
||||
void step()
|
||||
{
|
||||
if constexpr (staged())
|
||||
m_object->build_step();
|
||||
}
|
||||
|
||||
std::string m_name;
|
||||
int m_order;
|
||||
Factory m_make;
|
||||
T* m_object{ nullptr };
|
||||
std::atomic<bool> m_complete{ false };
|
||||
bool m_building{ false };
|
||||
bool m_failed{ false };
|
||||
std::vector<std::function<void(T&)>> m_deferred;
|
||||
};
|
||||
|
||||
// Mixin for a type with one lazily built instance in the app. The statics reach that
|
||||
// instance through its Lazy holder (a recreated MainFrame's holder replaces the old
|
||||
// frame's).
|
||||
template <class Self>
|
||||
class LazyInstance
|
||||
{
|
||||
public:
|
||||
// Null until completely built.
|
||||
static Self* if_built() { return s_lazy ? s_lazy->get() : nullptr; }
|
||||
// Builds the object if needed; null while no holder exists or the holder has no
|
||||
// object.
|
||||
static Self* ensure() { return s_lazy ? s_lazy->ensure() : nullptr; }
|
||||
// Runs fn on the object now if it is built, otherwise once it is.
|
||||
static void when_built(std::function<void(Self&)> fn)
|
||||
{
|
||||
if (s_lazy)
|
||||
s_lazy->when_built(std::move(fn));
|
||||
}
|
||||
|
||||
private:
|
||||
friend class Lazy<Self>;
|
||||
inline static Lazy<Self>* s_lazy{ nullptr };
|
||||
};
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
@@ -0,0 +1,14 @@
|
||||
#include "LazyPage.hpp"
|
||||
|
||||
#include "GUI_App.hpp"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
void apply_dark_ui_to_lazy_panel(wxWindow* panel)
|
||||
{
|
||||
#ifdef _MSW_DARK_MODE
|
||||
wxGetApp().UpdateDarkUIWin(panel);
|
||||
#endif
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
@@ -0,0 +1,77 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <wx/bookctrl.h>
|
||||
#include <wx/panel.h>
|
||||
#include <wx/sizer.h>
|
||||
|
||||
#include "Lazy.hpp"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
// Applies the app's dark-mode pass to a panel built after the frame's own pass ran.
|
||||
void apply_dark_ui_to_lazy_panel(wxWindow* panel);
|
||||
|
||||
// A page whose panel is built the first time the page is shown, or earlier by an
|
||||
// IdleScheduler; nothing builds while the frame is still hidden. The Lazy side holds the
|
||||
// panel and, for a LazyInstance panel type, registers it for the type's statics.
|
||||
template <class Panel>
|
||||
class LazyPage : public wxPanel, public Lazy<Panel>
|
||||
{
|
||||
public:
|
||||
using Factory = std::function<Panel*(wxWindow* parent)>;
|
||||
|
||||
LazyPage(wxWindow* parent, std::string name, int order, Factory make = [](wxWindow* parent) { return new Panel(parent); })
|
||||
: wxPanel(parent), Lazy<Panel>(std::move(name), order, [this, make = std::move(make)] {
|
||||
Panel* panel = make(this);
|
||||
GetSizer()->Add(panel, 1, wxEXPAND);
|
||||
// Hidden while its page is, as an inserted page would be.
|
||||
if (!IsShown())
|
||||
panel->Hide();
|
||||
return panel;
|
||||
})
|
||||
{
|
||||
SetSizer(new wxBoxSizer(wxVERTICAL));
|
||||
// Shown by the book when its tab is selected.
|
||||
Hide();
|
||||
this->when_built([this](Panel& panel) {
|
||||
apply_dark_ui_to_lazy_panel(&panel);
|
||||
Layout();
|
||||
});
|
||||
}
|
||||
|
||||
// The parent book currently lists this page.
|
||||
bool in_book() const
|
||||
{
|
||||
auto* book = dynamic_cast<wxBookCtrlBase*>(GetParent());
|
||||
return book != nullptr && book->FindPage(this) != wxNOT_FOUND;
|
||||
}
|
||||
|
||||
// Pending only while the tab is in the book.
|
||||
bool pending() const override { return in_book() && Lazy<Panel>::pending(); }
|
||||
|
||||
// Forwarded so the panel's own Show() override stays its activation hook; wx calls this
|
||||
// virtual from the book's ShowWithEffect() only for wxSHOW_EFFECT_NONE, the default.
|
||||
bool Show(bool show = true) override
|
||||
{
|
||||
const bool changed = wxPanel::Show(show);
|
||||
if (show) {
|
||||
// The book shows its first page as it is inserted, before startup has chosen the
|
||||
// start page, so a hidden frame builds nothing; MainFrame::Show() completes it.
|
||||
if (this->built() || wxGetTopLevelParent(this)->IsShown()) {
|
||||
if (Panel* panel = this->ensure())
|
||||
panel->Show(true);
|
||||
// The sizer skipped the panel while the book kept it hidden.
|
||||
Layout();
|
||||
}
|
||||
} else if (Panel* panel = this->get()) {
|
||||
panel->Show(false);
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
};
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
+157
-170
@@ -317,7 +317,8 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, BORDERLESS_FRAME_
|
||||
// BBS
|
||||
, m_recent_projects(18)
|
||||
, m_settings_dialog(this)
|
||||
, diff_dialog(this)
|
||||
, m_idle([] { return wxGetApp().input_idle_ms(); })
|
||||
, m_diff_dialog("compare_presets", 100, [this] { return make_diff_dialog(); })
|
||||
{
|
||||
#ifdef __WXOSX__
|
||||
set_miniaturizable(GetHandle());
|
||||
@@ -737,9 +738,6 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, BORDERLESS_FRAME_
|
||||
|
||||
wxGetApp().persist_window_geometry(this, true);
|
||||
wxGetApp().persist_window_geometry(&m_settings_dialog, true);
|
||||
// bind events from DiffDlg
|
||||
|
||||
bind_diff_dialog();
|
||||
}
|
||||
|
||||
bool MainFrame::handle_global_shortcut(const KeyChord& chord)
|
||||
@@ -832,8 +830,10 @@ bool MainFrame::handle_global_shortcut(const KeyChord& chord)
|
||||
return true;
|
||||
}
|
||||
|
||||
void MainFrame::bind_diff_dialog()
|
||||
DiffPresetDialog* MainFrame::make_diff_dialog()
|
||||
{
|
||||
auto* dialog = new DiffPresetDialog(this);
|
||||
|
||||
auto get_tab = [](Preset::Type type) {
|
||||
Tab* null_tab = nullptr;
|
||||
for (Tab* tab : wxGetApp().tabs_list)
|
||||
@@ -842,23 +842,24 @@ void MainFrame::bind_diff_dialog()
|
||||
return null_tab;
|
||||
};
|
||||
|
||||
auto transfer = [this, get_tab](Preset::Type type) {
|
||||
get_tab(type)->transfer_options(diff_dialog.get_left_preset_name(type),
|
||||
diff_dialog.get_right_preset_name(type),
|
||||
diff_dialog.get_selected_options(type));
|
||||
auto transfer = [dialog, get_tab](Preset::Type type) {
|
||||
get_tab(type)->transfer_options(dialog->get_left_preset_name(type),
|
||||
dialog->get_right_preset_name(type),
|
||||
dialog->get_selected_options(type));
|
||||
};
|
||||
|
||||
auto process_options = [this](std::function<void(Preset::Type)> process) {
|
||||
const Preset::Type diff_dlg_type = diff_dialog.view_type();
|
||||
auto process_options = [dialog](std::function<void(Preset::Type)> process) {
|
||||
const Preset::Type diff_dlg_type = dialog->view_type();
|
||||
if (diff_dlg_type == Preset::TYPE_INVALID) {
|
||||
for (const Preset::Type& type : diff_dialog.types_list() )
|
||||
for (const Preset::Type& type : dialog->types_list() )
|
||||
process(type);
|
||||
}
|
||||
else
|
||||
process(diff_dlg_type);
|
||||
};
|
||||
|
||||
diff_dialog.Bind(EVT_DIFF_DIALOG_TRANSFER, [process_options, transfer](SimpleEvent&) { process_options(transfer); });
|
||||
dialog->Bind(EVT_DIFF_DIALOG_TRANSFER, [process_options, transfer](SimpleEvent&) { process_options(transfer); });
|
||||
return dialog;
|
||||
}
|
||||
|
||||
|
||||
@@ -1186,8 +1187,9 @@ void MainFrame::update_edge_panels()
|
||||
void MainFrame::shutdown()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "MainFrame::shutdown enter";
|
||||
if (m_project != nullptr)
|
||||
m_project->shutdown();
|
||||
m_idle.stop();
|
||||
if (ProjectPanel* project = ProjectPanel::if_built())
|
||||
project->shutdown();
|
||||
m_plugin_pages.shutdown();
|
||||
if (m_plater != nullptr)
|
||||
m_plater->remove_dock_panes();
|
||||
@@ -1314,19 +1316,6 @@ void MainFrame::show_option(bool show)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SLIC3R_CAD
|
||||
DesignPanel* MainFrame::ensure_design_panel()
|
||||
{
|
||||
if (m_design_panel == nullptr && m_design_page != nullptr) {
|
||||
wxBusyCursor busy;
|
||||
m_design_panel = new DesignPanel(m_design_page);
|
||||
m_design_page->GetSizer()->Add(m_design_panel, 1, wxEXPAND);
|
||||
m_design_page->Layout();
|
||||
}
|
||||
return m_design_panel;
|
||||
}
|
||||
#endif
|
||||
|
||||
void MainFrame::init_tabpanel() {
|
||||
// wxNB_NOPAGETHEME: Disable Windows Vista theme for the Notebook background. The theme performance is terrible on
|
||||
// Windows 10 with multiple high resolution displays connected.
|
||||
@@ -1369,23 +1358,20 @@ void MainFrame::init_tabpanel() {
|
||||
// m_param_panel->OnActivate();
|
||||
#ifdef SLIC3R_CAD
|
||||
else if (m_design_page != nullptr && panel == m_design_page) {
|
||||
// Built on first activation, never at startup: the panel creates several hundred
|
||||
// controls and its own GL canvas, which a user who does not open the tab should
|
||||
// not pay for.
|
||||
ensure_design_panel();
|
||||
// Re-sync the Design bed to the active printer: the panel is built before the
|
||||
// printer profile is fully applied, so its bed must refresh on activation or the
|
||||
// grid (true bed) spills past the stale default bed quad.
|
||||
m_design_panel->on_tab_shown();
|
||||
DesignPanel::ensure()->on_tab_shown();
|
||||
}
|
||||
#endif
|
||||
else if (panel == m_monitor) {
|
||||
else if (panel == m_monitor_page) {
|
||||
//monitor
|
||||
}
|
||||
#ifdef SLIC3R_CAD
|
||||
// Any page that is not Design takes the Design status line down with it — see
|
||||
// DesignPanel::on_tab_hidden for why the popup does not follow the page on its own.
|
||||
if (m_design_panel != nullptr && panel != m_design_page) m_design_panel->on_tab_hidden();
|
||||
if (DesignPanel* design = DesignPanel::if_built(); design != nullptr && panel != m_design_page)
|
||||
design->on_tab_hidden();
|
||||
#endif
|
||||
#ifndef __APPLE__
|
||||
if (m_last_selected_tab == TAB_ID_PREPARE) {
|
||||
@@ -1401,13 +1387,14 @@ void MainFrame::init_tabpanel() {
|
||||
});
|
||||
|
||||
if (wxGetApp().is_editor()) {
|
||||
m_webview = new WebViewPanel(m_tabpanel);
|
||||
m_home_page = new LazyPage<WebViewPanel>(m_tabpanel, TAB_ID_HOME, 10);
|
||||
m_lazy_pages.push_back(m_home_page);
|
||||
Bind(EVT_LOAD_URL, [this](wxCommandEvent &evt) {
|
||||
wxString url = evt.GetString();
|
||||
select_tab(TAB_ID_HOME);
|
||||
m_webview->load_url(url);
|
||||
WebViewPanel::ensure()->load_url(url);
|
||||
});
|
||||
m_tabpanel->AddPage(TAB_ID_HOME, m_webview, "", "tab_home_active");
|
||||
m_tabpanel->AddPage(TAB_ID_HOME, m_home_page, "", "tab_home_active");
|
||||
m_param_panel = new ParamsPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBK_LEFT | wxTAB_TRAVERSAL);
|
||||
}
|
||||
|
||||
@@ -1418,15 +1405,13 @@ void MainFrame::init_tabpanel() {
|
||||
wxGetApp().plater_ = m_plater;
|
||||
|
||||
#ifdef SLIC3R_CAD
|
||||
// Stand-in page for the Design tab. The real DesignPanel is built into it the first time
|
||||
// the tab is selected (see the page-changed handler above), so nothing it constructs sits
|
||||
// on the startup path. The experimental feature is off by default, and when it is off the
|
||||
// page is never created, so the tab does not appear at all (the preference takes effect on
|
||||
// the next start, like the other feature toggles).
|
||||
// The experimental feature is off by default, and when it is off the page is never
|
||||
// created, so the tab does not appear at all (the preference takes effect on the next
|
||||
// start, like the other feature toggles).
|
||||
if (wxGetApp().is_enable_cad_feature()) {
|
||||
m_design_page = new wxPanel(this);
|
||||
m_design_page->SetSizer(new wxBoxSizer(wxVERTICAL));
|
||||
m_design_page->Hide();
|
||||
// Experimental and heavy enough that building it unasked would cost more than it saves.
|
||||
m_design_page = new LazyPage<DesignPanel>(this, TAB_ID_DESIGN, -1);
|
||||
m_lazy_pages.push_back(m_design_page);
|
||||
start_mcp_control_if_enabled(); // opens the MCP socket iff ORCA_CAD_MCP is set
|
||||
}
|
||||
#endif
|
||||
@@ -1434,33 +1419,41 @@ void MainFrame::init_tabpanel() {
|
||||
create_preset_tabs();
|
||||
|
||||
//BBS add pages
|
||||
m_monitor = new MonitorPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
|
||||
m_monitor->SetBackgroundColour(*wxWHITE);
|
||||
m_tabpanel->AddPage(TAB_ID_MONITOR, m_monitor, _L("Device"), "tab_monitor_active");
|
||||
m_monitor_page = new LazyPage<MonitorPanel>(m_tabpanel, TAB_ID_MONITOR, 20);
|
||||
m_lazy_pages.push_back(m_monitor_page);
|
||||
m_tabpanel->AddPage(TAB_ID_MONITOR, m_monitor_page, _L("Device"), "tab_monitor_active");
|
||||
|
||||
m_printer_view = new PrinterWebView(m_tabpanel);
|
||||
m_printer_view_page = new LazyPage<PrinterWebView>(m_tabpanel, TAB_ID_MONITOR_WEB, 50, [this](wxWindow* parent) {
|
||||
auto* view = new PrinterWebView(parent);
|
||||
if (!m_printer_url.empty())
|
||||
view->load_url(m_printer_url, m_printer_api_key);
|
||||
return view;
|
||||
});
|
||||
m_lazy_pages.push_back(m_printer_view_page);
|
||||
Bind(EVT_LOAD_PRINTER_URL, [this](LoadPrinterViewEvent &evt) {
|
||||
wxString url = evt.GetString();
|
||||
wxString key = evt.GetAPIkey();
|
||||
//select_tab(MainFrame::tpMonitor);
|
||||
m_printer_view->load_url(url, key);
|
||||
m_printer_url = evt.GetString();
|
||||
m_printer_api_key = evt.GetAPIkey();
|
||||
if (PrinterWebView* view = PrinterWebView::if_built())
|
||||
view->load_url(m_printer_url, m_printer_api_key);
|
||||
});
|
||||
m_printer_view->Hide();
|
||||
|
||||
m_multi_machine_page = new LazyPage<MultiMachinePage>(m_tabpanel, TAB_ID_MULTI_DEVICE, 40);
|
||||
m_lazy_pages.push_back(m_multi_machine_page);
|
||||
if (wxGetApp().is_enable_multi_machine()) {
|
||||
m_multi_machine = new MultiMachinePage(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
|
||||
m_multi_machine->SetBackgroundColour(*wxWHITE);
|
||||
// TODO: change the bitmap
|
||||
m_tabpanel->AddPage(TAB_ID_MULTI_DEVICE, m_multi_machine, _L("Multi-device"), "tab_multi_active");
|
||||
m_tabpanel->AddPage(TAB_ID_MULTI_DEVICE, m_multi_machine_page, _L("Multi-device"), "tab_multi_active");
|
||||
}
|
||||
|
||||
m_project = new ProjectPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
|
||||
m_project->SetBackgroundColour(*wxWHITE);
|
||||
m_tabpanel->AddPage(TAB_ID_PROJECT, m_project, _L("Project"), "tab_auxiliary_active");
|
||||
m_project_page = new LazyPage<ProjectPanel>(m_tabpanel, TAB_ID_PROJECT, 60);
|
||||
m_lazy_pages.push_back(m_project_page);
|
||||
m_tabpanel->AddPage(TAB_ID_PROJECT, m_project_page, _L("Project"), "tab_auxiliary_active");
|
||||
|
||||
m_calibration = new CalibrationPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
|
||||
m_calibration->SetBackgroundColour(*wxWHITE);
|
||||
m_tabpanel->AddPage(TAB_ID_CALIBRATION, m_calibration, _L("Calibration"), "tab_calibration_active");
|
||||
// show_device() removes this tab for printers without the Bambu device tab, and the page
|
||||
// then never builds its panel.
|
||||
m_calibration_page = new LazyPage<CalibrationPanel>(m_tabpanel, TAB_ID_CALIBRATION, 30);
|
||||
m_lazy_pages.push_back(m_calibration_page);
|
||||
m_tabpanel->AddPage(TAB_ID_CALIBRATION, m_calibration_page, _L("Calibration"), "tab_calibration_active");
|
||||
|
||||
// Plugin pages are appended after the built-in tabs; their ids are namespaced
|
||||
// (plugin.<plugin_key>.<name>) so they can't collide with the built-in TAB_ID_* constants.
|
||||
@@ -1495,67 +1488,44 @@ void MainFrame::show_device(bool should_use_native) {
|
||||
// Remove the extra page before switching to any layout that shouldn't have it.
|
||||
if (!want_web_device_tab) {
|
||||
if ((idx = m_tabpanel->FindPageByName(TAB_ID_MONITOR_WEB)) != wxNOT_FOUND) {
|
||||
m_printer_view->Show(false);
|
||||
m_printer_view_page->Show(false);
|
||||
m_tabpanel->RemovePage(idx);
|
||||
}
|
||||
}
|
||||
|
||||
if (use_printer_agents) {
|
||||
if (!m_monitor) {
|
||||
m_monitor = new MonitorPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
|
||||
m_monitor->SetBackgroundColour(*wxWHITE);
|
||||
}
|
||||
|
||||
if (m_tabpanel->FindPage(m_monitor) == wxNOT_FOUND) {
|
||||
if ((idx = m_tabpanel->FindPage(m_printer_view)) != wxNOT_FOUND) {
|
||||
m_printer_view->Show(false);
|
||||
if (!m_monitor_page->in_book()) {
|
||||
if ((idx = m_tabpanel->FindPage(m_printer_view_page)) != wxNOT_FOUND) {
|
||||
m_printer_view_page->Show(false);
|
||||
m_tabpanel->RemovePage(idx);
|
||||
}
|
||||
m_monitor->Show(false);
|
||||
m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_PREVIEW}), TAB_ID_MONITOR, m_monitor,
|
||||
m_monitor_page->Show(false);
|
||||
m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_PREVIEW}), TAB_ID_MONITOR, m_monitor_page,
|
||||
_L("Device"), "tab_monitor_active");
|
||||
}
|
||||
|
||||
if (m_printer_view == nullptr) {
|
||||
m_printer_view = new PrinterWebView(m_tabpanel);
|
||||
Bind(EVT_LOAD_PRINTER_URL, [this](LoadPrinterViewEvent& evt) {
|
||||
wxString url = evt.GetString();
|
||||
wxString key = evt.GetAPIkey();
|
||||
// select_tab(MainFrame::tpMonitor);
|
||||
m_printer_view->load_url(url, key);
|
||||
});
|
||||
}
|
||||
|
||||
if (wxGetApp().is_enable_multi_machine()) {
|
||||
if (!m_multi_machine) {
|
||||
m_multi_machine = new MultiMachinePage(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
|
||||
m_multi_machine->SetBackgroundColour(*wxWHITE);
|
||||
}
|
||||
// TODO: change the bitmap
|
||||
if (m_tabpanel->FindPage(m_multi_machine) == wxNOT_FOUND) {
|
||||
m_multi_machine->Show(false);
|
||||
if (!m_multi_machine_page->in_book()) {
|
||||
m_multi_machine_page->Show(false);
|
||||
// Past the web Device tab when it is already there, so enabling multi-machine
|
||||
// later can't wedge this page between the two Device tabs.
|
||||
m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_MONITOR_WEB, TAB_ID_MONITOR}),
|
||||
TAB_ID_MULTI_DEVICE, m_multi_machine, _L("Multi-device"), "tab_multi_active");
|
||||
TAB_ID_MULTI_DEVICE, m_multi_machine_page, _L("Multi-device"), "tab_multi_active");
|
||||
}
|
||||
}
|
||||
if (!m_calibration) {
|
||||
m_calibration = new CalibrationPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
|
||||
m_calibration->SetBackgroundColour(*wxWHITE);
|
||||
}
|
||||
if (m_tabpanel->FindPage(m_calibration) == wxNOT_FOUND) {
|
||||
m_calibration->Show(false);
|
||||
m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_PROJECT}), TAB_ID_CALIBRATION, m_calibration,
|
||||
if (!m_calibration_page->in_book()) {
|
||||
m_calibration_page->Show(false);
|
||||
m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_PROJECT}), TAB_ID_CALIBRATION, m_calibration_page,
|
||||
_L("Calibration"), "tab_calibration_active");
|
||||
}
|
||||
|
||||
if (want_web_device_tab) {
|
||||
if ((idx = m_tabpanel->FindPage(m_printer_view)) == wxNOT_FOUND) {
|
||||
m_printer_view->Show(false);
|
||||
if ((idx = m_tabpanel->FindPage(m_printer_view_page)) == wxNOT_FOUND) {
|
||||
m_printer_view_page->Show(false);
|
||||
// Immediately right of the native Device tab, not at the end of the tab bar.
|
||||
m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_MONITOR}), TAB_ID_MONITOR_WEB,
|
||||
m_printer_view, _L("Device (Web)"), "tab_monitor_active");
|
||||
m_printer_view_page, _L("Device (Web)"), "tab_monitor_active");
|
||||
} else {
|
||||
m_tabpanel->SetPageText(idx, _L("Device (Web)"));
|
||||
}
|
||||
@@ -1567,48 +1537,37 @@ void MainFrame::show_device(bool should_use_native) {
|
||||
|
||||
fit_tab_labels(); // ORCA on printer change
|
||||
m_plugin_pages.relayout(); // re-sync plugin tabs against the native tabs just mutated above
|
||||
|
||||
if (m_prebuild_started)
|
||||
m_idle.start();
|
||||
return;
|
||||
}
|
||||
|
||||
if (should_use_native) {
|
||||
if (m_tabpanel->FindPage(m_monitor) != wxNOT_FOUND) {
|
||||
if (m_monitor_page->in_book()) {
|
||||
fit_tab_labels(); // ORCA on printer change - same button layout
|
||||
return;
|
||||
}
|
||||
// Remove printer view
|
||||
if ((idx = m_tabpanel->FindPage(m_printer_view)) != wxNOT_FOUND) {
|
||||
m_printer_view->Show(false);
|
||||
if ((idx = m_tabpanel->FindPage(m_printer_view_page)) != wxNOT_FOUND) {
|
||||
m_printer_view_page->Show(false);
|
||||
m_tabpanel->RemovePage(idx);
|
||||
}
|
||||
|
||||
// Create/insert monitor page
|
||||
if (!m_monitor) {
|
||||
m_monitor = new MonitorPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
|
||||
m_monitor->SetBackgroundColour(*wxWHITE);
|
||||
}
|
||||
m_monitor->Show(false);
|
||||
m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_PREVIEW}), TAB_ID_MONITOR, m_monitor,
|
||||
// Insert monitor page
|
||||
m_monitor_page->Show(false);
|
||||
m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_PREVIEW}), TAB_ID_MONITOR, m_monitor_page,
|
||||
_L("Device"), "tab_monitor_active");
|
||||
|
||||
if (wxGetApp().is_enable_multi_machine()) {
|
||||
if (!m_multi_machine) {
|
||||
m_multi_machine = new MultiMachinePage(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
|
||||
m_multi_machine->SetBackgroundColour(*wxWHITE);
|
||||
}
|
||||
// TODO: change the bitmap
|
||||
m_multi_machine->Show(false);
|
||||
m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_MONITOR}), TAB_ID_MULTI_DEVICE, m_multi_machine,
|
||||
m_multi_machine_page->Show(false);
|
||||
m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_MONITOR}), TAB_ID_MULTI_DEVICE, m_multi_machine_page,
|
||||
_L("Multi-device"), "tab_multi_active");
|
||||
}
|
||||
if (!m_calibration) {
|
||||
m_calibration = new CalibrationPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
|
||||
m_calibration->SetBackgroundColour(*wxWHITE);
|
||||
}
|
||||
m_calibration->Show(false);
|
||||
m_calibration_page->Show(false);
|
||||
// Last of the built-in tabs, but plugin tabs already sit past it — anchor rather than
|
||||
// append, so its position doesn't depend on the relayout() below running afterwards.
|
||||
m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_PROJECT}), TAB_ID_CALIBRATION, m_calibration,
|
||||
m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_PROJECT}), TAB_ID_CALIBRATION, m_calibration_page,
|
||||
_L("Calibration"), "tab_calibration_active");
|
||||
|
||||
#ifdef _MSW_DARK_MODE
|
||||
@@ -1616,37 +1575,30 @@ void MainFrame::show_device(bool should_use_native) {
|
||||
#endif // _MSW_DARK_MODE
|
||||
|
||||
} else {
|
||||
if (m_tabpanel->FindPage(m_printer_view) != wxNOT_FOUND) {
|
||||
if (m_printer_view_page->in_book()) {
|
||||
fit_tab_labels(); // ORCA on printer change - same button layout
|
||||
return;
|
||||
}
|
||||
if ((idx = m_tabpanel->FindPage(m_calibration)) != wxNOT_FOUND) {
|
||||
m_calibration->Show(false);
|
||||
if ((idx = m_tabpanel->FindPage(m_calibration_page)) != wxNOT_FOUND) {
|
||||
m_calibration_page->Show(false);
|
||||
m_tabpanel->RemovePage(idx);
|
||||
}
|
||||
if ((idx = m_tabpanel->FindPage(m_multi_machine)) != wxNOT_FOUND) {
|
||||
m_multi_machine->Show(false);
|
||||
if ((idx = m_tabpanel->FindPage(m_multi_machine_page)) != wxNOT_FOUND) {
|
||||
m_multi_machine_page->Show(false);
|
||||
m_tabpanel->RemovePage(idx);
|
||||
}
|
||||
if ((idx = m_tabpanel->FindPage(m_monitor)) != wxNOT_FOUND) {
|
||||
m_monitor->Show(false);
|
||||
if ((idx = m_tabpanel->FindPage(m_monitor_page)) != wxNOT_FOUND) {
|
||||
m_monitor_page->Show(false);
|
||||
m_tabpanel->RemovePage(idx);
|
||||
}
|
||||
if (m_printer_view == nullptr) {
|
||||
m_printer_view = new PrinterWebView(m_tabpanel);
|
||||
Bind(EVT_LOAD_PRINTER_URL, [this](LoadPrinterViewEvent& evt) {
|
||||
wxString url = evt.GetString();
|
||||
wxString key = evt.GetAPIkey();
|
||||
// select_tab(MainFrame::tpMonitor);
|
||||
m_printer_view->load_url(url, key);
|
||||
});
|
||||
}
|
||||
m_printer_view->Show(false);
|
||||
m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_PREVIEW}), TAB_ID_MONITOR, m_printer_view,
|
||||
m_printer_view_page->Show(false);
|
||||
m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_PREVIEW}), TAB_ID_MONITOR, m_printer_view_page,
|
||||
_L("Device"), "tab_monitor_active");
|
||||
}
|
||||
fit_tab_labels(); // ORCA on printer change
|
||||
m_plugin_pages.relayout(); // re-sync plugin tabs against the native tabs just mutated above
|
||||
if (m_prebuild_started)
|
||||
m_idle.start();
|
||||
}
|
||||
|
||||
bool MainFrame::is_prepare_or_preview_tab() const
|
||||
@@ -2692,13 +2644,11 @@ void MainFrame::on_dpi_changed(const wxRect& suggested_rect)
|
||||
//BBS GUI refactor: remove unused layout new/dlg
|
||||
//if (m_layout != ESettingsLayout::Dlg) // Do not update tabs if the Settings are in the separated dialog
|
||||
m_param_panel->msw_rescale();
|
||||
m_project->msw_rescale();
|
||||
if(m_monitor)
|
||||
m_monitor->msw_rescale();
|
||||
if(m_multi_machine)
|
||||
m_multi_machine->msw_rescale();
|
||||
if(m_calibration)
|
||||
m_calibration->msw_rescale();
|
||||
// A panel mid-build gets the pass once it is complete.
|
||||
ProjectPanel::when_built([](ProjectPanel& project) { project.msw_rescale(); });
|
||||
MonitorPanel::when_built([](MonitorPanel& monitor) { monitor.msw_rescale(); });
|
||||
MultiMachinePage::when_built([](MultiMachinePage& multi_machine) { multi_machine.msw_rescale(); });
|
||||
CalibrationPanel::when_built([](CalibrationPanel& calibration) { calibration.msw_rescale(); });
|
||||
|
||||
// BBS
|
||||
#if 0
|
||||
@@ -2752,7 +2702,8 @@ void MainFrame::on_sys_color_changed()
|
||||
#endif
|
||||
#endif
|
||||
|
||||
diff_dialog.on_sys_color_changed();
|
||||
if (DiffPresetDialog* dialog = DiffPresetDialog::if_built())
|
||||
dialog->on_sys_color_changed();
|
||||
|
||||
// BBS
|
||||
m_tabpanel->Rescale();
|
||||
@@ -2760,10 +2711,8 @@ void MainFrame::on_sys_color_changed()
|
||||
|
||||
// update Plater
|
||||
wxGetApp().plater()->sys_color_changed();
|
||||
if(m_monitor)
|
||||
m_monitor->on_sys_color_changed();
|
||||
if(m_calibration)
|
||||
m_calibration->on_sys_color_changed();
|
||||
MonitorPanel::when_built([](MonitorPanel& monitor) { monitor.on_sys_color_changed(); });
|
||||
CalibrationPanel::when_built([](CalibrationPanel& calibration) { calibration.on_sys_color_changed(); });
|
||||
// update Tabs
|
||||
for (auto tab : wxGetApp().tabs_list)
|
||||
tab->sys_color_changed();
|
||||
@@ -3696,7 +3645,8 @@ void MainFrame::set_max_recent_count(int max)
|
||||
}
|
||||
wxGetApp().app_config->set_recent_projects(recent_projects);
|
||||
wxGetApp().app_config->save();
|
||||
m_webview->SendRecentList(-1);
|
||||
if (WebViewPanel* home = WebViewPanel::if_built())
|
||||
home->SendRecentList(-1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4040,23 +3990,56 @@ void MainFrame::select_tab(wxPanel* panel)
|
||||
select_tab(page_name);
|
||||
}
|
||||
|
||||
// Selects the Prepare page without the page-changed event, so the GL canvas is on screen
|
||||
// and nothing else is built for the pass.
|
||||
void MainFrame::select_prepare_for_gl_init()
|
||||
{
|
||||
m_tabpanel->ChangeSelection(m_tabpanel->FindPageByName(TAB_ID_PREPARE));
|
||||
}
|
||||
|
||||
// The book shows its first page as it is inserted, while the frame is hidden and nothing
|
||||
// may build; the first show completes that page.
|
||||
bool MainFrame::Show(bool show)
|
||||
{
|
||||
const bool changed = DPIFrame::Show(show);
|
||||
if (show && changed && m_tabpanel != nullptr)
|
||||
if (wxWindow* page = m_tabpanel->GetCurrentPage())
|
||||
page->Show(true);
|
||||
return changed;
|
||||
}
|
||||
|
||||
// A page out of the book stays registered and is passed over; a negative order is never
|
||||
// registered.
|
||||
void MainFrame::prebuild_pages_when_idle()
|
||||
{
|
||||
m_idle.clear();
|
||||
if (m_param_panel)
|
||||
m_idle.add(m_param_panel->settings_page_prebuild());
|
||||
for (LazyBase* page : m_lazy_pages)
|
||||
if (page->prebuild_order() >= 0)
|
||||
m_idle.add(*page);
|
||||
m_idle.add(m_diff_dialog);
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": prebuild queue: " << m_idle.names();
|
||||
m_idle.start();
|
||||
m_prebuild_started = true;
|
||||
}
|
||||
|
||||
//BBS
|
||||
void MainFrame::jump_to_monitor(std::string dev_id)
|
||||
{
|
||||
if(!m_monitor)
|
||||
return;
|
||||
m_tabpanel->SelectPageByName(TAB_ID_MONITOR);
|
||||
if (!dev_id.empty()) {
|
||||
((MonitorPanel*)m_monitor)->select_machine(dev_id);
|
||||
MonitorPanel::ensure()->select_machine(dev_id);
|
||||
}
|
||||
}
|
||||
|
||||
void MainFrame::jump_to_multipage()
|
||||
{
|
||||
if(!m_multi_machine)
|
||||
if (!m_multi_machine_page->in_book())
|
||||
return;
|
||||
m_tabpanel->SelectPageByName(TAB_ID_MULTI_DEVICE);
|
||||
((MultiMachinePage*)m_multi_machine)->jump_to_send_page();
|
||||
if (MultiMachinePage* page = m_multi_machine_page->ensure())
|
||||
page->jump_to_send_page();
|
||||
}
|
||||
|
||||
|
||||
@@ -4105,8 +4088,8 @@ void MainFrame::request_select_tab(const wxString& id)
|
||||
}
|
||||
|
||||
int MainFrame::get_calibration_curr_tab() {
|
||||
if (m_calibration)
|
||||
return m_calibration->get_tabpanel()->GetSelection();
|
||||
if (CalibrationPanel* calibration = CalibrationPanel::if_built())
|
||||
return calibration->get_tabpanel()->GetSelection();
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -4215,7 +4198,8 @@ void MainFrame::add_to_recent_projects(const wxString& filename)
|
||||
recent_projects.push_back(into_u8(m_recent_projects.GetHistoryFile(i)));
|
||||
}
|
||||
wxGetApp().app_config->set_recent_projects(recent_projects);
|
||||
m_webview->SendRecentList(0);
|
||||
if (WebViewPanel* home = WebViewPanel::if_built())
|
||||
home->SendRecentList(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4331,7 +4315,8 @@ void MainFrame::open_recent_project(size_t file_id, wxString const & filename)
|
||||
recent_projects.push_back(into_u8(m_recent_projects.GetHistoryFile(i)));
|
||||
}
|
||||
wxGetApp().app_config->set_recent_projects(recent_projects);
|
||||
m_webview->SendRecentList(-1);
|
||||
if (WebViewPanel* home = WebViewPanel::if_built())
|
||||
home->SendRecentList(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4354,7 +4339,8 @@ void MainFrame::remove_recent_project(size_t file_id, wxString const &filename)
|
||||
recent_projects.push_back(into_u8(m_recent_projects.GetHistoryFile(i)));
|
||||
}
|
||||
wxGetApp().app_config->set_recent_projects(recent_projects);
|
||||
m_webview->SendRecentList(-1);
|
||||
if (WebViewPanel* home = WebViewPanel::if_built())
|
||||
home->SendRecentList(-1);
|
||||
}
|
||||
|
||||
void MainFrame::load_url(wxString url)
|
||||
@@ -4408,14 +4394,14 @@ bool MainFrame::is_printer_view() const { return m_tabpanel->GetSelectedPageName
|
||||
|
||||
void MainFrame::refresh_plugin_tips()
|
||||
{
|
||||
if (m_webview != nullptr)
|
||||
m_webview->ShowNetpluginTip();
|
||||
if (WebViewPanel* home = WebViewPanel::if_built())
|
||||
home->ShowNetpluginTip();
|
||||
}
|
||||
|
||||
void MainFrame::RunScript(wxString js)
|
||||
{
|
||||
if (m_webview != nullptr)
|
||||
m_webview->RunScript(js);
|
||||
if (WebViewPanel* home = WebViewPanel::if_built())
|
||||
home->RunScript(js);
|
||||
}
|
||||
|
||||
void MainFrame::technology_changed()
|
||||
@@ -4524,7 +4510,8 @@ void MainFrame::update_side_preset_ui()
|
||||
|
||||
|
||||
//take off multi machine
|
||||
if(m_multi_machine){m_multi_machine->clear_page();}
|
||||
if (MultiMachinePage* multi_machine = MultiMachinePage::if_built())
|
||||
multi_machine->clear_page();
|
||||
}
|
||||
|
||||
void MainFrame::on_select_default_preset(SimpleEvent& evt)
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
#include "Widgets/SideButton.hpp"
|
||||
#include "Widgets/SideMenuPopup.hpp"
|
||||
#include "FilamentGroupPopup.hpp"
|
||||
#include "LazyPage.hpp"
|
||||
#include "IdleScheduler.hpp"
|
||||
|
||||
|
||||
#include <boost/property_tree/ptree_fwd.hpp>
|
||||
@@ -136,6 +138,13 @@ class MainFrame : public DPIFrame
|
||||
#endif
|
||||
bool m_loaded {false};
|
||||
wxTimer* m_reset_title_text_colour_timer{ nullptr };
|
||||
IdleScheduler m_idle;
|
||||
bool m_prebuild_started{ false };
|
||||
// Every LazyPage, in and out of the book; prebuild_pages_when_idle() registers them.
|
||||
std::vector<LazyBase*> m_lazy_pages;
|
||||
// The latest EVT_LOAD_PRINTER_URL, applied when the web Device view is built.
|
||||
wxString m_printer_url;
|
||||
wxString m_printer_api_key;
|
||||
|
||||
wxString m_qs_last_input_file = wxEmptyString;
|
||||
wxString m_qs_last_output_file = wxEmptyString;
|
||||
@@ -180,7 +189,7 @@ class MainFrame : public DPIFrame
|
||||
bool can_delete() const;
|
||||
bool can_delete_all() const;
|
||||
bool can_reslice() const;
|
||||
void bind_diff_dialog();
|
||||
DiffPresetDialog* make_diff_dialog();
|
||||
|
||||
// BBS
|
||||
wxBoxSizer* create_side_tools();
|
||||
@@ -377,6 +386,12 @@ public:
|
||||
void select_tab(wxPanel* panel);
|
||||
void select_tab(const wxString& id = wxString());
|
||||
void request_select_tab(const wxString& id);
|
||||
// post_init() needs the plater's canvas on screen to initialize OpenGL; this pass does not
|
||||
// build the settings page.
|
||||
void select_prepare_for_gl_init();
|
||||
// Builds the lazy tab pages while the user is idle; post_init() calls it once.
|
||||
void prebuild_pages_when_idle();
|
||||
bool Show(bool show = true) override;
|
||||
int get_calibration_curr_tab();
|
||||
void select_view(const std::string& direction);
|
||||
void update_shortcut_labels();
|
||||
@@ -435,27 +450,21 @@ public:
|
||||
BBLTopbar* m_topbar{ nullptr };
|
||||
PrintHostQueueDialog* printhost_queue_dlg() { return m_printhost_queue_dlg; }
|
||||
Plater* m_plater { nullptr };
|
||||
// Lazy pages, created once and kept for the frame's life; their panels are reached
|
||||
// through LazyInstance's statics, and show_device() only moves pages in and out of the book.
|
||||
#ifdef SLIC3R_CAD
|
||||
// The tab page is the placeholder; m_design_panel stays null until the tab is first
|
||||
// selected, so everything the Design panel builds stays off the startup path.
|
||||
wxPanel* m_design_page { nullptr };
|
||||
DesignPanel* m_design_panel { nullptr };
|
||||
// Builds the Design panel if it does not exist yet and returns it (null only before the
|
||||
// placeholder page itself exists). Main thread only -- it creates wx controls. Both the
|
||||
// tab activation and the MCP socket go through this: the socket is driven headlessly,
|
||||
// with nobody to click the tab, and without this every verb would answer "not ready".
|
||||
DesignPanel* ensure_design_panel();
|
||||
LazyPage<DesignPanel>* m_design_page { nullptr };
|
||||
#endif
|
||||
//BBS: GUI refactor
|
||||
MonitorPanel* m_monitor{ nullptr };
|
||||
LazyPage<MonitorPanel>* m_monitor_page{ nullptr };
|
||||
|
||||
//AuxiliaryPanel* m_auxiliary{ nullptr };
|
||||
MultiMachinePage* m_multi_machine{ nullptr };
|
||||
ProjectPanel* m_project{ nullptr };
|
||||
LazyPage<MultiMachinePage>* m_multi_machine_page{ nullptr };
|
||||
LazyPage<ProjectPanel>* m_project_page{ nullptr };
|
||||
|
||||
CalibrationPanel* m_calibration{ nullptr };
|
||||
WebViewPanel* m_webview { nullptr };
|
||||
PrinterWebView* m_printer_view{nullptr};
|
||||
LazyPage<CalibrationPanel>* m_calibration_page{ nullptr };
|
||||
LazyPage<WebViewPanel>* m_home_page { nullptr };
|
||||
LazyPage<PrinterWebView>* m_printer_view_page{nullptr};
|
||||
PluginPages m_plugin_pages;
|
||||
wxLogWindow* m_log_window { nullptr };
|
||||
// BBS
|
||||
@@ -466,7 +475,8 @@ public:
|
||||
ParamsDialog* m_param_dialog{ nullptr };
|
||||
//BBS
|
||||
SettingsDialog m_settings_dialog;
|
||||
DiffPresetDialog diff_dialog;
|
||||
// The Compare presets dialog, built on first use or at idle through its holder.
|
||||
Lazy<DiffPresetDialog> m_diff_dialog;
|
||||
wxWindow* m_plater_page{ nullptr };
|
||||
PrintHostQueueDialog* m_printhost_queue_dlg;
|
||||
|
||||
|
||||
+44
-37
@@ -103,6 +103,7 @@ MonitorPanel::MonitorPanel(wxWindow* parent, wxWindowID id, const wxPoint& pos,
|
||||
: wxPanel(parent, id, pos, size, style),
|
||||
m_select_machine(SelectMachinePopup(this))
|
||||
{
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
#ifdef __WINDOWS__
|
||||
SetDoubleBuffered(true);
|
||||
#endif //__WINDOWS__
|
||||
@@ -180,31 +181,36 @@ void MonitorPanel::init_tabpanel()
|
||||
auto title = m_tabpanel->GetPageText(m_tabpanel->GetSelection());
|
||||
m_media_file_panel->SwitchStorage(title == _L("Storage"));
|
||||
}
|
||||
page->SetFocus();
|
||||
// The first page is selected while the panel is built off screen.
|
||||
if (page->IsShownOnScreen())
|
||||
page->SetFocus();
|
||||
update_all();
|
||||
}, m_tabpanel->GetId());
|
||||
|
||||
//m_status_add_machine_panel = new AddMachinePanel(m_tabpanel);
|
||||
m_status_info_panel = new StatusPanel(m_tabpanel);
|
||||
m_status_info_panel = new StatusPanel(m_tabpanel);
|
||||
// Queued before the page is added, since adding it selects it and runs the handler above,
|
||||
// where built() must already be false.
|
||||
add_build_steps_of(*m_status_info_panel);
|
||||
m_tabpanel->AddPage(m_status_info_panel, _L("Status"), true);
|
||||
add_build_step([this] {
|
||||
m_media_file_panel = new MediaFilePanel(m_tabpanel);
|
||||
m_tabpanel->AddPage(m_media_file_panel, _L("Storage"), false);
|
||||
});
|
||||
add_build_step([this] {
|
||||
m_upgrade_panel = new UpgradePanel(m_tabpanel);
|
||||
m_tabpanel->AddPage(m_upgrade_panel, _L_CONTEXT(L_CONTEXT("Update", "Firmware"), "Firmware"), false);
|
||||
});
|
||||
add_build_step([this] {
|
||||
m_hms_panel = new HMSPanel(m_tabpanel);
|
||||
m_tabpanel->AddPage(m_hms_panel, _L("Assistant(HMS)"), false);
|
||||
|
||||
m_media_file_panel = new MediaFilePanel(m_tabpanel);
|
||||
m_tabpanel->AddPage(m_media_file_panel, _L("Storage"), false);
|
||||
//m_tabpanel->AddPage(m_media_file_panel, _L("Internal Storage"), false);
|
||||
std::string network_ver = Slic3r::NetworkAgent::get_version();
|
||||
if (!network_ver.empty()) {
|
||||
m_tabpanel->SetFooterText(wxString::Format(_L("Network plug-in v%s"), network_ver));
|
||||
}
|
||||
|
||||
m_upgrade_panel = new UpgradePanel(m_tabpanel);
|
||||
m_tabpanel->AddPage(m_upgrade_panel, _L_CONTEXT(L_CONTEXT("Update", "Firmware"), "Firmware"), false);
|
||||
|
||||
m_hms_panel = new HMSPanel(m_tabpanel);
|
||||
m_tabpanel->AddPage(m_hms_panel, _L("Assistant(HMS)"), false);
|
||||
|
||||
std::string network_ver = Slic3r::NetworkAgent::get_version();
|
||||
if (!network_ver.empty()) {
|
||||
m_tabpanel->SetFooterText(wxString::Format(_L("Network plug-in v%s"), network_ver));
|
||||
}
|
||||
|
||||
m_initialized = true;
|
||||
show_status((int)MonitorStatus::MONITOR_NO_PRINTER);
|
||||
show_status((int)MonitorStatus::MONITOR_NO_PRINTER);
|
||||
});
|
||||
}
|
||||
|
||||
void MonitorPanel::set_default()
|
||||
@@ -288,19 +294,23 @@ void MonitorPanel::on_select_printer(wxCommandEvent& event)
|
||||
set_default();
|
||||
update_all();
|
||||
|
||||
MachineObject *obj_ = dev->get_selected_machine();
|
||||
if (obj_) {
|
||||
obj_->last_cali_version = -1;
|
||||
obj_->reset_pa_cali_history_result();
|
||||
obj_->reset_pa_cali_result();
|
||||
Sidebar &sidebar = GUI::wxGetApp().sidebar();
|
||||
sidebar.update_sync_status(obj_);
|
||||
sidebar.set_need_auto_sync_after_connect_printer(sidebar.need_auto_sync_extruder_list_after_connect_priner(obj_));
|
||||
}
|
||||
on_machine_selected(dev->get_selected_machine());
|
||||
|
||||
Layout();
|
||||
}
|
||||
|
||||
void MonitorPanel::on_machine_selected(MachineObject* obj)
|
||||
{
|
||||
if (obj == nullptr)
|
||||
return;
|
||||
obj->last_cali_version = -1;
|
||||
obj->reset_pa_cali_history_result();
|
||||
obj->reset_pa_cali_result();
|
||||
Sidebar& sidebar = GUI::wxGetApp().sidebar();
|
||||
sidebar.update_sync_status(obj);
|
||||
sidebar.set_need_auto_sync_after_connect_printer(sidebar.need_auto_sync_extruder_list_after_connect_priner(obj));
|
||||
}
|
||||
|
||||
void MonitorPanel::on_printer_clicked(wxMouseEvent &event)
|
||||
{
|
||||
auto mouse_pos = ClientToScreen(event.GetPosition());
|
||||
@@ -331,7 +341,8 @@ void MonitorPanel::on_size(wxSizeEvent &event)
|
||||
|
||||
void MonitorPanel::update_all()
|
||||
{
|
||||
if (!m_initialized)
|
||||
// Every page exists once the last build step has run.
|
||||
if (!built())
|
||||
return;
|
||||
|
||||
NetworkAgent* m_agent = wxGetApp().getAgent();
|
||||
@@ -412,16 +423,12 @@ void MonitorPanel::update_hms_tag()
|
||||
|
||||
bool MonitorPanel::Show(bool show)
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
// Notebook::InsertPage() hides every page it appends, so this also runs while MainFrame is
|
||||
// still constructing, before GUI_App::mainframe is assigned. Same guard as Plater::Show().
|
||||
if (wxGetApp().mainframe)
|
||||
wxGetApp().mainframe->SetMinSize(wxGetApp().plater()->GetMinSize());
|
||||
#endif
|
||||
|
||||
NetworkAgent* m_agent = wxGetApp().getAgent();
|
||||
DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager();
|
||||
if (show) {
|
||||
#ifdef __APPLE__
|
||||
wxGetApp().mainframe->SetMinSize(wxGetApp().plater()->GetMinSize());
|
||||
#endif
|
||||
start_update();
|
||||
update_network_version_footer();
|
||||
|
||||
@@ -448,7 +455,7 @@ bool MonitorPanel::Show(bool show)
|
||||
|
||||
void MonitorPanel::show_status(int status)
|
||||
{
|
||||
if (!m_initialized) return;
|
||||
if (!built()) return;
|
||||
if (last_status == status)return;
|
||||
if ((last_status & (int)MonitorStatus::MONITOR_CONNECTING) != 0) {
|
||||
NetworkAgent* agent = wxGetApp().getAgent();
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
#include "slic3r/GUI/AmsWidgets.hpp"
|
||||
#include "Widgets/SideTools.hpp"
|
||||
#include "SelectMachinePop.hpp"
|
||||
#include "Lazy.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
@@ -72,16 +73,16 @@ public:
|
||||
void msw_rescale();
|
||||
};
|
||||
|
||||
class MonitorPanel : public wxPanel
|
||||
class MonitorPanel : public wxPanel, public StagedBuild, public LazyInstance<MonitorPanel>
|
||||
{
|
||||
private:
|
||||
Tabbook* m_tabpanel{ nullptr };
|
||||
wxSizer* m_main_sizer{ nullptr };
|
||||
|
||||
StatusPanel* m_status_info_panel;
|
||||
MediaFilePanel* m_media_file_panel;
|
||||
UpgradePanel* m_upgrade_panel;
|
||||
HMSPanel* m_hms_panel;
|
||||
StatusPanel* m_status_info_panel{ nullptr };
|
||||
MediaFilePanel* m_media_file_panel{ nullptr };
|
||||
UpgradePanel* m_upgrade_panel{ nullptr };
|
||||
HMSPanel* m_hms_panel{ nullptr };
|
||||
|
||||
/* side tools */
|
||||
SideTools* m_side_tools{nullptr};
|
||||
@@ -96,7 +97,6 @@ private:
|
||||
wxBitmap m_arrow_img;
|
||||
|
||||
int last_status;
|
||||
bool m_initialized { false };
|
||||
bool update_flag{false};
|
||||
wxTimer* m_refresh_timer = nullptr;
|
||||
|
||||
@@ -125,6 +125,8 @@ public:
|
||||
|
||||
StatusPanel* get_status_panel() {return m_status_info_panel;};
|
||||
void select_machine(std::string machine_sn);
|
||||
// Resets the selected printer's calibration results and syncs the sidebar, without the Device tab.
|
||||
static void on_machine_selected(MachineObject* obj);
|
||||
void on_timer(wxTimerEvent& event);
|
||||
void on_select_printer(wxCommandEvent& event);
|
||||
void on_printer_clicked(wxMouseEvent &event);
|
||||
|
||||
@@ -21,8 +21,9 @@ MultiMachineItem::MultiMachineItem(wxWindow* parent, MachineObject* obj)
|
||||
Bind(wxEVT_MOTION, &MultiMachineItem::OnMove, this);
|
||||
Bind(EVT_MULTI_DEVICE_VIEW, [obj](auto& e) {
|
||||
wxGetApp().mainframe->jump_to_monitor(obj->get_dev_id());
|
||||
if (wxGetApp().mainframe->m_monitor->get_status_panel()->get_media_play_ctrl()) {
|
||||
wxGetApp().mainframe->m_monitor->get_status_panel()->get_media_play_ctrl()->jump_to_play();
|
||||
MonitorPanel* monitor = MonitorPanel::if_built();
|
||||
if (monitor && monitor->get_status_panel()->get_media_play_ctrl()) {
|
||||
monitor->get_status_panel()->get_media_play_ctrl()->jump_to_play();
|
||||
}
|
||||
});
|
||||
wxGetApp().UpdateDarkUIWin(this);
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace GUI {
|
||||
MultiMachinePage::MultiMachinePage(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style)
|
||||
: wxPanel(parent, id, pos, size, style)
|
||||
{
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
init_tabpanel();
|
||||
m_main_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
m_main_sizer->Add(m_tabpanel, 1, wxEXPAND | wxLEFT, 0);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "MultiTaskManagerPage.hpp"
|
||||
#include "MultiMachineManagerPage.hpp"
|
||||
#include "Tabbook.hpp"
|
||||
#include "Lazy.hpp"
|
||||
|
||||
#include "wx/button.h"
|
||||
|
||||
@@ -19,7 +20,7 @@ namespace GUI {
|
||||
#define PICK_LEFT_DEV_STATUS 250
|
||||
#define PICK_DEVICE_MAX 6
|
||||
|
||||
class MultiMachinePage : public wxPanel
|
||||
class MultiMachinePage : public wxPanel, public LazyInstance<MultiMachinePage>
|
||||
{
|
||||
private:
|
||||
wxTimer* m_refresh_timer = nullptr;
|
||||
|
||||
@@ -290,7 +290,7 @@ ParamsPanel::ParamsPanel( wxWindow* parent, wxWindowID id, const wxPoint& pos, c
|
||||
|
||||
m_compare_btn = new ScalableButton(m_top_panel, wxID_ANY, "compare", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, true);
|
||||
m_compare_btn->SetToolTip(_L("Compare presets"));
|
||||
m_compare_btn->Bind(wxEVT_BUTTON, ([](wxCommandEvent e) { wxGetApp().mainframe->diff_dialog.show(); }));
|
||||
m_compare_btn->Bind(wxEVT_BUTTON, ([](wxCommandEvent e) { DiffPresetDialog::ensure()->show(); }));
|
||||
|
||||
m_setting_btn = new ScalableButton(m_top_panel, wxID_ANY, "table", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, true);
|
||||
m_setting_btn->SetToolTip(_L("View all object's settings"));
|
||||
@@ -550,18 +550,36 @@ void ParamsPanel::clear_page()
|
||||
void ParamsPanel::OnActivate()
|
||||
{
|
||||
if (m_current_tab == NULL)
|
||||
{
|
||||
//the first time
|
||||
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": first time opened, set current tab to print");
|
||||
// BBS: open/close tab
|
||||
//m_current_tab = m_tab_print;
|
||||
set_active_tab(m_tab_print ? m_tab_print : m_tab_filament);
|
||||
}
|
||||
select_default_tab();
|
||||
Tab* cur_tab = dynamic_cast<Tab *> (m_current_tab);
|
||||
if (cur_tab)
|
||||
cur_tab->OnActivate();
|
||||
}
|
||||
|
||||
void ParamsPanel::select_default_tab()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": first time opened, set current tab to print");
|
||||
// BBS: open/close tab
|
||||
//m_current_tab = m_tab_print;
|
||||
set_active_tab(m_tab_print ? m_tab_print : m_tab_filament);
|
||||
}
|
||||
|
||||
bool ParamsPanel::SettingsPagePrebuild::built() const
|
||||
{
|
||||
Tab* tab = dynamic_cast<Tab*>(m_panel.m_current_tab);
|
||||
return tab != nullptr && !tab->page_build_pending();
|
||||
}
|
||||
|
||||
bool ParamsPanel::SettingsPagePrebuild::build_step()
|
||||
{
|
||||
if (m_panel.m_current_tab == nullptr) {
|
||||
m_panel.select_default_tab();
|
||||
return !built();
|
||||
}
|
||||
Tab* tab = dynamic_cast<Tab*>(m_panel.m_current_tab);
|
||||
return tab != nullptr && tab->page_build_step();
|
||||
}
|
||||
|
||||
void ParamsPanel::OnToggled(wxCommandEvent& event)
|
||||
{
|
||||
if (m_mode_region && m_mode_region->GetId() == event.GetId()) {
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "wxExtensions.hpp"
|
||||
#include "GUI_Utils.hpp"
|
||||
#include "Widgets/Button.hpp"
|
||||
#include "Lazy.hpp"
|
||||
|
||||
class ModeSwitchButton;
|
||||
class SwitchButton;
|
||||
@@ -121,6 +122,22 @@ class ParamsPanel : public wxPanel
|
||||
|
||||
wxPanel* m_current_tab { nullptr };
|
||||
|
||||
// Builds the selected page's option groups at idle; while no tab is selected yet,
|
||||
// its first unit selects the default one.
|
||||
class SettingsPagePrebuild : public LazyBase
|
||||
{
|
||||
public:
|
||||
explicit SettingsPagePrebuild(ParamsPanel& panel) : m_panel(panel) {}
|
||||
const std::string& name() const override { return m_name; }
|
||||
bool built() const override;
|
||||
bool build_step() override;
|
||||
int prebuild_order() const override { return 0; }
|
||||
|
||||
private:
|
||||
ParamsPanel& m_panel;
|
||||
std::string m_name{ "settings_page" };
|
||||
} m_settings_page_prebuild{ *this };
|
||||
|
||||
bool m_has_object_config { false };
|
||||
|
||||
struct Highlighter
|
||||
@@ -149,6 +166,10 @@ class ParamsPanel : public wxPanel
|
||||
//clear the right page
|
||||
void clear_page();
|
||||
void OnActivate();
|
||||
// The print tab, or the filament tab without one.
|
||||
void select_default_tab();
|
||||
// The settings page's prebuild task.
|
||||
LazyBase& settings_page_prebuild() { return m_settings_page_prebuild; }
|
||||
void set_active_tab(wxPanel*tab);
|
||||
bool is_active_and_shown_tab(wxPanel*tab);
|
||||
void update_mode();
|
||||
|
||||
+17
-16
@@ -10412,8 +10412,8 @@ void Plater::priv::reset(bool apply_presets_change, bool reload_presets)
|
||||
// Same reason, one level up: the Design tab keeps the editable document, not the Model, so
|
||||
// clearing the recipe alone leaves the tab showing the previous project's feature tree —
|
||||
// and its next edit syncs that tree straight back into the new project.
|
||||
if (wxGetApp().mainframe != nullptr && wxGetApp().mainframe->m_design_panel != nullptr)
|
||||
wxGetApp().mainframe->m_design_panel->clear_document();
|
||||
if (DesignPanel* design = DesignPanel::if_built())
|
||||
design->clear_document();
|
||||
#endif
|
||||
assemble_view->get_canvas3d()->reset_explosion_ratio();
|
||||
update();
|
||||
@@ -13184,17 +13184,17 @@ void Plater::priv::on_tab_selection_changing(wxBookCtrlEvent& e)
|
||||
// Pointer test, not a name lookup: in printer-agents mode this page is TAB_ID_MONITOR_WEB
|
||||
// while the native Device tab holds TAB_ID_MONITOR, and in legacy-web mode it holds
|
||||
// TAB_ID_MONITOR itself.
|
||||
const bool selecting_web_device_tab = main_frame->m_printer_view &&
|
||||
main_frame->m_tabpanel->GetPage(new_sel) == main_frame->m_printer_view;
|
||||
const bool selecting_web_device_tab = main_frame->m_printer_view_page &&
|
||||
main_frame->m_tabpanel->GetPage(new_sel) == main_frame->m_printer_view_page;
|
||||
if (selecting_web_device_tab) {
|
||||
// Use the selected discovered machine when the preset has no host.
|
||||
main_frame->load_printer_url();
|
||||
} else if (new_name == TAB_ID_MONITOR && wxGetApp().preset_bundle != nullptr) {
|
||||
auto cfg = wxGetApp().preset_bundle->printers.get_edited_preset().config;
|
||||
wxString url = from_u8(PrintHost::get_print_host_webui(&cfg));
|
||||
if (main_frame->m_printer_view && url.empty()) {
|
||||
if (PrinterWebView* view = PrinterWebView::if_built(); view != nullptr && url.empty()) {
|
||||
// It's missing_connection page, reload so that we can replay the gif image
|
||||
main_frame->m_printer_view->reload();
|
||||
view->reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13925,8 +13925,8 @@ void Plater::priv::unbind_canvas_event_handlers()
|
||||
// The Design tab's viewport is a fourth GLCanvas3D on the same shared GL context, owned by
|
||||
// MainFrame rather than by us — same reach as reset() uses for clear_document(). Null until
|
||||
// the tab has been opened once, so most sessions skip it.
|
||||
if (wxGetApp().mainframe != nullptr && wxGetApp().mainframe->m_design_panel != nullptr)
|
||||
wxGetApp().mainframe->m_design_panel->unbind_canvas_event_handlers();
|
||||
if (DesignPanel* design = DesignPanel::if_built())
|
||||
design->unbind_canvas_event_handlers();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -13939,8 +13939,8 @@ void Plater::priv::reset_canvas_volumes()
|
||||
preview->get_canvas3d()->reset_volumes();
|
||||
|
||||
#ifdef SLIC3R_CAD
|
||||
if (wxGetApp().mainframe != nullptr && wxGetApp().mainframe->m_design_panel != nullptr)
|
||||
wxGetApp().mainframe->m_design_panel->reset_canvas_volumes();
|
||||
if (DesignPanel* design = DesignPanel::if_built())
|
||||
design->reset_canvas_volumes();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -20002,7 +20002,7 @@ int Plater::export_config_3mf(int plate_idx, Export3mfProgressFn proFn)
|
||||
void Plater::send_calibration_job_finished(wxCommandEvent & evt)
|
||||
{
|
||||
p->main_frame->request_select_tab(TAB_ID_CALIBRATION);
|
||||
auto calibration_panel = p->main_frame->m_calibration;
|
||||
CalibrationPanel* calibration_panel = CalibrationPanel::ensure();
|
||||
if (calibration_panel) {
|
||||
auto curr_wizard = static_cast<CalibrationWizard*>(calibration_panel->get_tabpanel()->GetPage(evt.GetInt()));
|
||||
wxCommandEvent event(EVT_CALIBRATION_JOB_FINISHED);
|
||||
@@ -20034,8 +20034,8 @@ void Plater::print_job_finished(wxCommandEvent &evt)
|
||||
|
||||
dev->set_selected_machine(evt.GetString().ToStdString());
|
||||
p->main_frame->request_select_tab(TAB_ID_MONITOR);
|
||||
//jump to monitor and select device status panel
|
||||
MonitorPanel* curr_monitor = p->main_frame->m_monitor;
|
||||
// Selects the status page on a built Device tab; one built by the switch starts there.
|
||||
MonitorPanel* curr_monitor = MonitorPanel::if_built();
|
||||
if(curr_monitor)
|
||||
curr_monitor->get_tabpanel()->ChangeSelection(MonitorPanel::PrinterTab::PT_STATUS);
|
||||
}
|
||||
@@ -20775,8 +20775,8 @@ void Plater::update_print_error_info(int code, std::string msg, std::string extr
|
||||
if (p->m_send_to_sdcard_dlg) {
|
||||
p->m_send_to_sdcard_dlg->update_print_error_info(code, msg, extra);
|
||||
}
|
||||
if (p->main_frame->m_calibration)
|
||||
p->main_frame->m_calibration->update_print_error_info(code, msg, extra);
|
||||
if (CalibrationPanel* calibration = CalibrationPanel::if_built())
|
||||
calibration->update_print_error_info(code, msg, extra);
|
||||
}
|
||||
|
||||
wxString Plater::get_project_filename(const wxString& extension) const
|
||||
@@ -21069,7 +21069,8 @@ void Plater::pop_warning_and_go_to_device_page(wxString printer_name, PrinterWar
|
||||
{
|
||||
printer_name.Replace("Bambu Lab", "", false);
|
||||
wxString content;
|
||||
bool device_page = (wxGetApp().mainframe == nullptr) && (wxGetApp().mainframe->m_monitor->IsShown());
|
||||
MainFrame* frame = wxGetApp().mainframe;
|
||||
bool device_page = frame != nullptr && frame->m_monitor_page->in_book();
|
||||
if (type == PrinterWarningType::NOT_CONNECTED) {
|
||||
if (device_page) {
|
||||
content = wxString::Format(_L("Printer not connected. Please go to the device page to connect %s before syncing."),
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Lazy.hpp"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
// Holders to build at idle, ordered by prebuild_order(), run one slice at a time. The
|
||||
// holders outlive the queue. Main thread only.
|
||||
class PrebuildQueue
|
||||
{
|
||||
public:
|
||||
struct Slice
|
||||
{
|
||||
std::string name; // the task that ran; empty when nothing was pending
|
||||
int units{ 0 }; // units run
|
||||
long long ms{ 0 }; // time spent
|
||||
bool completed{ false }; // the task has no more units
|
||||
bool remaining{ false }; // some task is still pending
|
||||
};
|
||||
|
||||
// Lower order runs first, equal order in the order added.
|
||||
void add(LazyBase& task)
|
||||
{
|
||||
auto after = std::find_if(m_tasks.begin(), m_tasks.end(), [&](const LazyBase* t) { return t->prebuild_order() > task.prebuild_order(); });
|
||||
m_tasks.insert(after, &task);
|
||||
}
|
||||
|
||||
void clear() { m_tasks.clear(); }
|
||||
|
||||
bool pending() const
|
||||
{
|
||||
return std::any_of(m_tasks.begin(), m_tasks.end(), [](const LazyBase* t) { return t->pending(); });
|
||||
}
|
||||
|
||||
// The task names in queue order, comma separated.
|
||||
std::string names() const
|
||||
{
|
||||
std::string out;
|
||||
for (const LazyBase* t : m_tasks)
|
||||
out += (out.empty() ? "" : ", ") + t->name();
|
||||
return out;
|
||||
}
|
||||
|
||||
// Runs units of the first pending task until it completes, budget_ms of now_ms() have
|
||||
// passed, or interrupt() is true after a unit; on_unit gets the task name and each
|
||||
// unit's duration. A task with no work is passed over and stays in the queue, so one
|
||||
// whose work returns is pending again.
|
||||
Slice run_slice(int budget_ms,
|
||||
const std::function<long long()>& now_ms,
|
||||
const std::function<bool()>& interrupt,
|
||||
const std::function<void(const std::string&, long long)>& on_unit = {})
|
||||
{
|
||||
Slice slice;
|
||||
auto next = std::find_if(m_tasks.begin(), m_tasks.end(), [](const LazyBase* t) { return t->pending(); });
|
||||
if (next == m_tasks.end())
|
||||
return slice;
|
||||
// The pointer is copied out, since a unit may add tasks and reallocate m_tasks.
|
||||
LazyBase* const task = *next;
|
||||
slice.name = task->name();
|
||||
const long long started = now_ms();
|
||||
for (;;) {
|
||||
const long long unit_started = now_ms();
|
||||
const bool more = task->build_step();
|
||||
const long long now = now_ms();
|
||||
++slice.units;
|
||||
if (on_unit)
|
||||
on_unit(slice.name, now - unit_started);
|
||||
slice.ms = now - started;
|
||||
if (!more) {
|
||||
slice.completed = true;
|
||||
break;
|
||||
}
|
||||
if (slice.ms >= budget_ms || interrupt())
|
||||
break;
|
||||
}
|
||||
slice.remaining = pending();
|
||||
return slice;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<LazyBase*> m_tasks;
|
||||
};
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
@@ -1044,8 +1044,8 @@ wxBoxSizer *PreferencesDialog::create_item_checkbox(wxString title, wxString too
|
||||
if (m_bambu_cloud_checkbox) m_bambu_cloud_checkbox->Enable(!enabled);
|
||||
}
|
||||
else if (param == "hide_login_side_panel") {
|
||||
if (wxGetApp().mainframe && wxGetApp().mainframe->m_webview) {
|
||||
wxGetApp().mainframe->m_webview->SendCloudProvidersInfo();
|
||||
if (WebViewPanel* home = WebViewPanel::if_built()) {
|
||||
home->SendCloudProvidersInfo();
|
||||
}
|
||||
}
|
||||
// ORCA: apply the preview dimming change immediately to the currently loaded preview
|
||||
@@ -1254,9 +1254,8 @@ wxBoxSizer *PreferencesDialog::create_item_bambu_cloud(wxString title, wxString
|
||||
app_config->save();
|
||||
|
||||
// Update homepage visibility immediately
|
||||
auto *mainframe = wxGetApp().mainframe;
|
||||
if (mainframe && mainframe->m_webview)
|
||||
mainframe->m_webview->SendCloudProvidersInfo();
|
||||
if (WebViewPanel* home = WebViewPanel::if_built())
|
||||
home->SendCloudProvidersInfo();
|
||||
});
|
||||
|
||||
m_sizer->Add(cb, 0, wxALIGN_CENTER);
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "wx/textctrl.h"
|
||||
#include <wx/timer.h>
|
||||
#include <memory>
|
||||
#include "Lazy.hpp"
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
@@ -34,7 +35,7 @@ namespace GUI {
|
||||
class PrinterWebViewHandler;
|
||||
|
||||
|
||||
class PrinterWebView : public wxPanel {
|
||||
class PrinterWebView : public wxPanel, public LazyInstance<PrinterWebView> {
|
||||
public:
|
||||
PrinterWebView(wxWindow *parent);
|
||||
virtual ~PrinterWebView();
|
||||
|
||||
@@ -44,6 +44,7 @@ const std::vector<std::string> license_list = {
|
||||
|
||||
ProjectPanel::ProjectPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, long style) : wxPanel(parent, id, pos, size, style)
|
||||
{
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
m_project_home_url = wxString::Format("file://%s/web/model/index.html", from_u8(resources_dir()));
|
||||
wxString strlang = wxGetApp().current_language_code_safe();
|
||||
if (strlang != "")
|
||||
@@ -67,6 +68,7 @@ ProjectPanel::ProjectPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos,
|
||||
m_auxiliary = new AuxiliaryPanel(this);
|
||||
m_auxiliary->Hide();
|
||||
main_sizer->Add(m_auxiliary, wxSizerFlags().Expand().Proportion(1));
|
||||
add_build_steps_of(*m_auxiliary);
|
||||
Bind(EVT_AUXILIARY_DONE, [this](wxCommandEvent& e) { update_model_data();});
|
||||
|
||||
SetSizer(main_sizer);
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "libslic3r/ProjectTask.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
#include "Auxiliary.hpp"
|
||||
#include "Lazy.hpp"
|
||||
|
||||
#define AUFILE_GREY700 wxColour(107, 107, 107)
|
||||
#define AUFILE_GREY500 wxColour(158, 158, 158)
|
||||
@@ -59,7 +60,7 @@ struct project_file{
|
||||
std::string size;
|
||||
};
|
||||
|
||||
class ProjectPanel : public wxPanel
|
||||
class ProjectPanel : public wxPanel, public StagedBuild, public LazyInstance<ProjectPanel>
|
||||
{
|
||||
private:
|
||||
std::atomic<bool> m_web_init_completed{false};
|
||||
@@ -90,7 +91,6 @@ public:
|
||||
void msw_rescale();
|
||||
void update_model_data();
|
||||
void clear_model_info();
|
||||
void init_auxiliary() { m_auxiliary->init_auxiliary(); }
|
||||
|
||||
bool Show(bool show);
|
||||
void OnScriptMessage(wxWebViewEvent& evt);
|
||||
|
||||
@@ -3470,7 +3470,7 @@ void SelectMachineDialog::navigate_to_timelapse_page()
|
||||
main_frame->jump_to_monitor();
|
||||
|
||||
// then switch to Storage (Media) tab inside Monitor
|
||||
auto* monitor = dynamic_cast<MonitorPanel*>(main_frame->m_monitor);
|
||||
MonitorPanel* monitor = MonitorPanel::if_built();
|
||||
if (monitor) {
|
||||
auto* tabpanel = monitor->get_tabpanel();
|
||||
if (tabpanel) {
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
// A panel whose constructor builds the skeleton and queues the rest as steps, run in order
|
||||
// on the main thread, one at a time.
|
||||
class StagedBuild
|
||||
{
|
||||
public:
|
||||
virtual ~StagedBuild() = default;
|
||||
|
||||
bool built() const
|
||||
{
|
||||
return m_next_step == m_steps.size() &&
|
||||
std::all_of(m_children.begin(), m_children.end(), [](const StagedBuild* child) { return child->built(); });
|
||||
}
|
||||
|
||||
// Runs one step and returns true while more remain. Own steps first, then whatever a
|
||||
// child queued after its steps were forwarded.
|
||||
bool build_step()
|
||||
{
|
||||
if (m_next_step < m_steps.size()) {
|
||||
// Copied out, since a step may queue more steps and reallocate m_steps.
|
||||
auto step = std::move(m_steps[m_next_step++]);
|
||||
step();
|
||||
} else if (StagedBuild* child = unfinished_child()) {
|
||||
child->build_step();
|
||||
}
|
||||
return !built();
|
||||
}
|
||||
|
||||
protected:
|
||||
void add_build_step(std::function<void()> step) { m_steps.push_back(std::move(step)); }
|
||||
|
||||
// Queues one step per step the child has now, and keeps the child so that built() waits
|
||||
// for any it queues later.
|
||||
void add_build_steps_of(StagedBuild& child)
|
||||
{
|
||||
m_children.push_back(&child);
|
||||
for (size_t i = child.m_next_step; i < child.m_steps.size(); ++i)
|
||||
add_build_step([&child] { child.build_step(); });
|
||||
}
|
||||
|
||||
private:
|
||||
StagedBuild* unfinished_child() const
|
||||
{
|
||||
auto it = std::find_if(m_children.begin(), m_children.end(), [](const StagedBuild* child) { return !child->built(); });
|
||||
return it == m_children.end() ? nullptr : *it;
|
||||
}
|
||||
|
||||
std::vector<std::function<void()>> m_steps;
|
||||
std::vector<StagedBuild*> m_children;
|
||||
size_t m_next_step{ 0 };
|
||||
};
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
@@ -1328,18 +1328,21 @@ StatusBasePanel::StatusBasePanel(wxWindow *parent, wxWindowID id, const wxPoint
|
||||
|
||||
wxBoxSizer *bSizer_left = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
auto m_monitoring_sizer = create_monitoring_page();
|
||||
bSizer_left->Add(m_monitoring_sizer, 1, wxEXPAND | wxALL, 0);
|
||||
// The sizers are nested here so each step only appends to its own.
|
||||
add_build_step([this, bSizer_left] {
|
||||
auto m_monitoring_sizer = create_monitoring_page();
|
||||
bSizer_left->Add(m_monitoring_sizer, 1, wxEXPAND | wxALL, 0);
|
||||
|
||||
auto m_panel_separotor1 = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
||||
m_panel_separotor1->SetBackgroundColour(STATUS_PANEL_BG);
|
||||
m_panel_separotor1->SetMinSize(wxSize(-1, PAGE_SPACING));
|
||||
m_panel_separotor1->SetMaxSize(wxSize(-1, PAGE_SPACING));
|
||||
m_monitoring_sizer->Add(m_panel_separotor1, 0, wxEXPAND, 0);
|
||||
auto m_panel_separotor1 = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
||||
m_panel_separotor1->SetBackgroundColour(STATUS_PANEL_BG);
|
||||
m_panel_separotor1->SetMinSize(wxSize(-1, PAGE_SPACING));
|
||||
m_panel_separotor1->SetMaxSize(wxSize(-1, PAGE_SPACING));
|
||||
m_monitoring_sizer->Add(m_panel_separotor1, 0, wxEXPAND, 0);
|
||||
|
||||
m_project_task_panel = new PrintingTaskPanel(this, PrintingTaskType::PRINGINT);
|
||||
m_project_task_panel->init_bitmaps();
|
||||
m_monitoring_sizer->Add(m_project_task_panel, 0, wxALL | wxEXPAND , 0);
|
||||
m_project_task_panel = new PrintingTaskPanel(this, PrintingTaskType::PRINGINT);
|
||||
m_project_task_panel->init_bitmaps();
|
||||
m_monitoring_sizer->Add(m_project_task_panel, 0, wxALL | wxEXPAND , 0);
|
||||
});
|
||||
|
||||
// auto m_panel_separotor2 = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
||||
// m_panel_separotor2->SetBackgroundColour(STATUS_PANEL_BG);
|
||||
@@ -1359,8 +1362,10 @@ StatusBasePanel::StatusBasePanel(wxWindow *parent, wxWindowID id, const wxPoint
|
||||
m_machine_ctrl_panel->SetDoubleBuffered(true);
|
||||
auto m_machine_control = create_machine_control_page(m_machine_ctrl_panel);
|
||||
m_machine_ctrl_panel->SetSizer(m_machine_control);
|
||||
m_machine_ctrl_panel->Layout();
|
||||
m_machine_control->Fit(m_machine_ctrl_panel);
|
||||
add_build_step([this, m_machine_control] {
|
||||
m_machine_ctrl_panel->Layout();
|
||||
m_machine_control->Fit(m_machine_ctrl_panel);
|
||||
});
|
||||
|
||||
bSizer_status_below->Add(m_machine_ctrl_panel, 0, wxALL, 0);
|
||||
|
||||
@@ -1375,8 +1380,11 @@ StatusBasePanel::StatusBasePanel(wxWindow *parent, wxWindowID id, const wxPoint
|
||||
m_panel_separotor_bottom->SetBackgroundColour(STATUS_PANEL_BG);
|
||||
|
||||
bSizer_status->Add(m_panel_separotor_bottom, 0, wxEXPAND | wxALL, 0);
|
||||
this->SetSizerAndFit(bSizer_status);
|
||||
this->Layout();
|
||||
this->SetSizer(bSizer_status);
|
||||
add_build_step([this, bSizer_status] {
|
||||
bSizer_status->SetSizeHints(this);
|
||||
this->Layout();
|
||||
});
|
||||
}
|
||||
|
||||
StatusBasePanel::~StatusBasePanel()
|
||||
@@ -1614,16 +1622,24 @@ wxBoxSizer *StatusBasePanel::create_machine_control_page(wxWindow *parent)
|
||||
|
||||
wxBoxSizer *bSizer_control = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
auto temp_axis_ctrl_sizer = create_temp_axis_group(parent);
|
||||
auto m_filament_load_sizer = create_filament_group(parent);
|
||||
|
||||
// The slot sizers keep each group's place in bSizer_control.
|
||||
wxBoxSizer *temp_axis_slot = new wxBoxSizer(wxVERTICAL);
|
||||
wxBoxSizer *filament_slot = new wxBoxSizer(wxVERTICAL);
|
||||
/* ams control box or live nozzle-rack panel (rack printers switch between the two) */
|
||||
wxSizer *ams_rack_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
ams_rack_sizer->Add(create_ams_group(parent), 0, wxEXPAND | wxLEFT);
|
||||
|
||||
m_panel_nozzle_rack = new wgtDeviceNozzleRack(parent);
|
||||
m_panel_nozzle_rack->Show(false);
|
||||
ams_rack_sizer->Add(m_panel_nozzle_rack, 0, wxEXPAND | wxLEFT);
|
||||
add_build_step([this, parent, temp_axis_slot, filament_slot] {
|
||||
temp_axis_slot->Add(create_temp_axis_group(parent), 0, wxEXPAND);
|
||||
filament_slot->Add(create_filament_group(parent), 0, wxEXPAND);
|
||||
});
|
||||
add_build_step([this, parent, ams_rack_sizer] {
|
||||
ams_rack_sizer->Add(create_ams_group(parent), 0, wxEXPAND | wxLEFT);
|
||||
});
|
||||
add_build_step([this, parent, ams_rack_sizer] {
|
||||
m_panel_nozzle_rack = new wgtDeviceNozzleRack(parent);
|
||||
m_panel_nozzle_rack->Show(false);
|
||||
ams_rack_sizer->Add(m_panel_nozzle_rack, 0, wxEXPAND | wxLEFT);
|
||||
});
|
||||
|
||||
m_ams_rack_switch = new SwitchBoard(parent, _L("Filament"), _L("Hotends"), wxSize(FromDIP(126), FromDIP(26)));
|
||||
m_ams_rack_switch->updateState("left");
|
||||
@@ -1631,12 +1647,12 @@ wxBoxSizer *StatusBasePanel::create_machine_control_page(wxWindow *parent)
|
||||
m_ams_rack_switch->Bind(wxCUSTOMEVT_SWITCH_POS, &StatusBasePanel::on_ams_rack_switch, this);
|
||||
|
||||
bSizer_control->Add(0, 0, 0, wxTOP, FromDIP(8));
|
||||
bSizer_control->Add(temp_axis_ctrl_sizer, 0, wxALIGN_CENTER|wxLEFT|wxRIGHT, FromDIP(8));
|
||||
bSizer_control->Add(temp_axis_slot, 0, wxALIGN_CENTER|wxLEFT|wxRIGHT, FromDIP(8));
|
||||
bSizer_control->Add(m_ams_rack_switch, 0, wxALIGN_CENTRE|wxTOP, FromDIP(6));
|
||||
bSizer_control->Add(0, 0, 0, wxTOP, FromDIP(6));
|
||||
bSizer_control->Add(ams_rack_sizer, 0, wxALIGN_CENTER|wxLEFT|wxRIGHT, FromDIP(8));
|
||||
bSizer_control->Add(0, 0, 0, wxTOP, FromDIP(6));
|
||||
bSizer_control->Add(m_filament_load_sizer, 0, wxALIGN_CENTER|wxLEFT|wxRIGHT, FromDIP(8));
|
||||
bSizer_control->Add(filament_slot, 0, wxALIGN_CENTER|wxLEFT|wxRIGHT, FromDIP(8));
|
||||
bSizer_control->Add(0, 0, 0, wxTOP, FromDIP(4));
|
||||
|
||||
bSizer_right->Add(bSizer_control, 1, wxEXPAND | wxALL, 0);
|
||||
@@ -2228,8 +2244,10 @@ void StatusBasePanel::expand_filament_loading(wxMouseEvent& e)
|
||||
m_filament_step->Show(tag_show);
|
||||
Layout();
|
||||
Fit();
|
||||
wxGetApp().mainframe->m_monitor->get_status_panel()->Layout();
|
||||
wxGetApp().mainframe->m_monitor->Layout();
|
||||
if (MonitorPanel* monitor = MonitorPanel::if_built()) {
|
||||
monitor->get_status_panel()->Layout();
|
||||
monitor->Layout();
|
||||
}
|
||||
}
|
||||
|
||||
void StatusBasePanel::show_ams_group(bool show)
|
||||
@@ -2240,7 +2258,8 @@ void StatusBasePanel::show_ams_group(bool show)
|
||||
m_ams_control->Fit();
|
||||
Layout();
|
||||
Fit();
|
||||
wxGetApp().mainframe->m_monitor->Layout();
|
||||
if (MonitorPanel* monitor = MonitorPanel::if_built())
|
||||
monitor->Layout();
|
||||
}
|
||||
|
||||
// On rack printers, don't clobber the rack view when the user has the switch on "Hotends".
|
||||
@@ -2253,7 +2272,8 @@ void StatusBasePanel::show_ams_group(bool show)
|
||||
m_ams_control->Fit();
|
||||
Layout();
|
||||
Fit();
|
||||
wxGetApp().mainframe->m_monitor->Layout();
|
||||
if (MonitorPanel* monitor = MonitorPanel::if_built())
|
||||
monitor->Layout();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2277,8 +2297,10 @@ void StatusBasePanel::show_filament_load_group(bool show)
|
||||
Layout();
|
||||
Fit();
|
||||
|
||||
wxGetApp().mainframe->m_monitor->get_status_panel()->Layout();
|
||||
wxGetApp().mainframe->m_monitor->Layout();
|
||||
if (MonitorPanel* monitor = MonitorPanel::if_built()) {
|
||||
monitor->get_status_panel()->Layout();
|
||||
monitor->Layout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2403,6 +2425,12 @@ void StatusPanel::update_camera_state(MachineObject* obj)
|
||||
|
||||
StatusPanel::StatusPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, long style, const wxString &name)
|
||||
: StatusBasePanel(parent, id, pos, size, style)
|
||||
{
|
||||
// Wires the controls the base class builds in steps, so it is the last step.
|
||||
add_build_step([this] { wire_controls(); });
|
||||
}
|
||||
|
||||
void StatusPanel::wire_controls()
|
||||
{
|
||||
init_scaled_buttons();
|
||||
m_buttons.push_back(m_bpButton_z_10);
|
||||
@@ -2514,47 +2542,50 @@ StatusPanel::StatusPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, co
|
||||
|
||||
StatusPanel::~StatusPanel()
|
||||
{
|
||||
// Disconnect Events
|
||||
m_project_task_panel->get_bitmap_thumbnail()->Disconnect(wxEVT_LEFT_DOWN, wxMouseEventHandler(StatusPanel::refresh_thumbnail_webrequest), NULL, this);
|
||||
m_project_task_panel->get_partskip_button()->Disconnect(wxEVT_LEFT_DOWN, wxCommandEventHandler(StatusPanel::on_subtask_partskip), NULL, this);
|
||||
m_project_task_panel->get_pause_resume_button()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_subtask_pause_resume), NULL, this);
|
||||
m_project_task_panel->get_abort_button()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_subtask_abort), NULL, this);
|
||||
m_project_task_panel->get_market_scoring_button()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_market_scoring), NULL, this);
|
||||
m_project_task_panel->get_market_retry_buttom()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_market_retry), NULL, this);
|
||||
m_project_task_panel->get_clean_button()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_print_error_clean), NULL, this);
|
||||
// The controls only exist once the last build step has run.
|
||||
if (built()) {
|
||||
// Disconnect Events
|
||||
m_project_task_panel->get_bitmap_thumbnail()->Disconnect(wxEVT_LEFT_DOWN, wxMouseEventHandler(StatusPanel::refresh_thumbnail_webrequest), NULL, this);
|
||||
m_project_task_panel->get_partskip_button()->Disconnect(wxEVT_LEFT_DOWN, wxCommandEventHandler(StatusPanel::on_subtask_partskip), NULL, this);
|
||||
m_project_task_panel->get_pause_resume_button()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_subtask_pause_resume), NULL, this);
|
||||
m_project_task_panel->get_abort_button()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_subtask_abort), NULL, this);
|
||||
m_project_task_panel->get_market_scoring_button()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_market_scoring), NULL, this);
|
||||
m_project_task_panel->get_market_retry_buttom()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_market_retry), NULL, this);
|
||||
m_project_task_panel->get_clean_button()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_print_error_clean), NULL, this);
|
||||
|
||||
m_setting_button->Disconnect(wxEVT_LEFT_DOWN, wxMouseEventHandler(StatusPanel::on_camera_enter), NULL, this);
|
||||
m_setting_button->Disconnect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(StatusPanel::on_camera_enter), NULL, this);
|
||||
m_tempCtrl_bed->Disconnect(wxEVT_KILL_FOCUS, wxFocusEventHandler(StatusPanel::on_bed_temp_kill_focus), NULL, this);
|
||||
m_tempCtrl_bed->Disconnect(wxEVT_SET_FOCUS, wxFocusEventHandler(StatusPanel::on_bed_temp_set_focus), NULL, this);
|
||||
m_tempCtrl_nozzle->Disconnect(wxEVT_KILL_FOCUS, wxFocusEventHandler(StatusPanel::on_nozzle_temp_kill_focus), NULL, this);
|
||||
m_tempCtrl_nozzle->Disconnect(wxEVT_SET_FOCUS, wxFocusEventHandler(StatusPanel::on_nozzle_temp_set_focus), NULL, this);
|
||||
m_setting_button->Disconnect(wxEVT_LEFT_DOWN, wxMouseEventHandler(StatusPanel::on_camera_enter), NULL, this);
|
||||
m_setting_button->Disconnect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(StatusPanel::on_camera_enter), NULL, this);
|
||||
m_tempCtrl_bed->Disconnect(wxEVT_KILL_FOCUS, wxFocusEventHandler(StatusPanel::on_bed_temp_kill_focus), NULL, this);
|
||||
m_tempCtrl_bed->Disconnect(wxEVT_SET_FOCUS, wxFocusEventHandler(StatusPanel::on_bed_temp_set_focus), NULL, this);
|
||||
m_tempCtrl_nozzle->Disconnect(wxEVT_KILL_FOCUS, wxFocusEventHandler(StatusPanel::on_nozzle_temp_kill_focus), NULL, this);
|
||||
m_tempCtrl_nozzle->Disconnect(wxEVT_SET_FOCUS, wxFocusEventHandler(StatusPanel::on_nozzle_temp_set_focus), NULL, this);
|
||||
|
||||
m_tempCtrl_nozzle_deputy->Disconnect(wxEVT_KILL_FOCUS, wxFocusEventHandler(StatusPanel::on_nozzle_temp_kill_focus), NULL, this);
|
||||
m_tempCtrl_nozzle_deputy->Disconnect(wxEVT_SET_FOCUS, wxFocusEventHandler(StatusPanel::on_nozzle_temp_set_focus), NULL, this);
|
||||
m_tempCtrl_nozzle_deputy->Disconnect(wxEVT_KILL_FOCUS, wxFocusEventHandler(StatusPanel::on_nozzle_temp_kill_focus), NULL, this);
|
||||
m_tempCtrl_nozzle_deputy->Disconnect(wxEVT_SET_FOCUS, wxFocusEventHandler(StatusPanel::on_nozzle_temp_set_focus), NULL, this);
|
||||
|
||||
m_switch_lamp->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_lamp_switch), NULL, this);
|
||||
/*m_switch_nozzle_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this);
|
||||
m_switch_printing_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this);
|
||||
m_switch_cham_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this);*/
|
||||
m_switch_lamp->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_lamp_switch), NULL, this);
|
||||
/*m_switch_nozzle_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this);
|
||||
m_switch_printing_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this);
|
||||
m_switch_cham_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this);*/
|
||||
|
||||
//m_switch_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this);
|
||||
//m_switch_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this);
|
||||
m_switch_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this);
|
||||
//m_switch_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this);
|
||||
//m_switch_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this);
|
||||
m_switch_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this);
|
||||
|
||||
m_bpButton_xy->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_xy), NULL, this);
|
||||
m_bpButton_z_10->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_z_up_10), NULL, this);
|
||||
m_bpButton_z_1->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_z_up_1), NULL, this);
|
||||
m_bpButton_z_down_1->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_z_down_1), NULL, this);
|
||||
m_bpButton_z_down_10->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_z_down_10), NULL, this);
|
||||
m_bpButton_e_10->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_e_up_10), NULL, this);
|
||||
m_bpButton_e_down_10->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_e_down_10), NULL, this);
|
||||
m_nozzle_btn_panel->Disconnect(wxCUSTOMEVT_SWITCH_POS, wxCommandEventHandler(StatusPanel::on_nozzle_selected), NULL, this);
|
||||
m_switch_speed->Disconnect(wxEVT_LEFT_DOWN, wxCommandEventHandler(StatusPanel::on_switch_speed), NULL, this);
|
||||
m_calibration_btn->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_start_calibration), NULL, this);
|
||||
m_options_btn->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_show_print_options), NULL, this);
|
||||
m_safety_btn->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_show_safety_options), NULL, this);
|
||||
m_parts_btn->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_show_parts_options), NULL, this);
|
||||
m_bpButton_xy->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_xy), NULL, this);
|
||||
m_bpButton_z_10->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_z_up_10), NULL, this);
|
||||
m_bpButton_z_1->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_z_up_1), NULL, this);
|
||||
m_bpButton_z_down_1->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_z_down_1), NULL, this);
|
||||
m_bpButton_z_down_10->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_z_down_10), NULL, this);
|
||||
m_bpButton_e_10->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_e_up_10), NULL, this);
|
||||
m_bpButton_e_down_10->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_e_down_10), NULL, this);
|
||||
m_nozzle_btn_panel->Disconnect(wxCUSTOMEVT_SWITCH_POS, wxCommandEventHandler(StatusPanel::on_nozzle_selected), NULL, this);
|
||||
m_switch_speed->Disconnect(wxEVT_LEFT_DOWN, wxCommandEventHandler(StatusPanel::on_switch_speed), NULL, this);
|
||||
m_calibration_btn->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_start_calibration), NULL, this);
|
||||
m_options_btn->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_show_print_options), NULL, this);
|
||||
m_safety_btn->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_show_safety_options), NULL, this);
|
||||
m_parts_btn->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_show_parts_options), NULL, this);
|
||||
}
|
||||
|
||||
// remove warning dialogs
|
||||
if (abort_dlg != nullptr)
|
||||
@@ -5216,7 +5247,9 @@ void StatusPanel::set_default()
|
||||
m_filament_step->Hide();
|
||||
error_info_reset();
|
||||
#ifndef __WXGTK__
|
||||
SetFocus();
|
||||
// Also reached while the panel is built off screen.
|
||||
if (IsShownOnScreen())
|
||||
SetFocus();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "HMS.hpp"
|
||||
#include "PartSkipDialog.hpp"
|
||||
#include "DeviceErrorDialog.hpp"
|
||||
#include "StagedBuild.hpp"
|
||||
|
||||
class StepIndicator;
|
||||
|
||||
@@ -375,7 +376,7 @@ public:
|
||||
void paint(wxPaintEvent&);
|
||||
};
|
||||
|
||||
class StatusBasePanel : public wxScrolledWindow
|
||||
class StatusBasePanel : public wxScrolledWindow, public StagedBuild
|
||||
{
|
||||
protected:
|
||||
wxBitmap m_item_placeholder;
|
||||
@@ -443,7 +444,7 @@ protected:
|
||||
|
||||
|
||||
wxMediaCtrl3 * m_media_ctrl;
|
||||
MediaPlayCtrl * m_media_play_ctrl;
|
||||
MediaPlayCtrl * m_media_play_ctrl{nullptr};
|
||||
|
||||
Label * m_staticText_printing;
|
||||
wxStaticBitmap *m_bitmap_thumbnail;
|
||||
@@ -629,6 +630,7 @@ class StatusPanel : public StatusBasePanel
|
||||
{
|
||||
private:
|
||||
friend class MonitorPanel;
|
||||
void wire_controls();
|
||||
|
||||
protected:
|
||||
std::shared_ptr<SliceInfoPopup> m_slice_info_popup;
|
||||
@@ -679,7 +681,7 @@ protected:
|
||||
std::map<std::string, std::string> m_print_connect_types;
|
||||
std::vector<Button *> m_buttons;
|
||||
int last_status;
|
||||
ScoreData *m_score_data;
|
||||
ScoreData *m_score_data = nullptr;
|
||||
wxBitmap* calib_bitmap = nullptr;
|
||||
CalibMode m_calib_mode;
|
||||
CalibrationMethod m_calib_method;
|
||||
|
||||
+56
-17
@@ -804,6 +804,10 @@ void Tab::OnActivate()
|
||||
}
|
||||
#endif
|
||||
|
||||
// The OnActivate() that shows the tab builds the page.
|
||||
if (wxGetApp().mainframe != nullptr && !wxGetApp().mainframe->is_active_and_shown_tab(m_parent))
|
||||
return;
|
||||
|
||||
// BBS: select on first active
|
||||
if (!m_active_page)
|
||||
restore_last_select_item();
|
||||
@@ -7159,6 +7163,16 @@ void Tab::restore_last_select_item()
|
||||
m_tabctrl->SelectItem(item);
|
||||
}
|
||||
|
||||
bool Tab::page_build_pending() const
|
||||
{
|
||||
return m_active_page != nullptr && m_active_page->build_pending();
|
||||
}
|
||||
|
||||
bool Tab::page_build_step()
|
||||
{
|
||||
return m_active_page != nullptr && m_active_page->build_step(m_mode);
|
||||
}
|
||||
|
||||
void Tab::update_description_lines()
|
||||
{
|
||||
if (m_active_page && m_active_page->title() == "Dependencies" && m_parent_preset_description_line)
|
||||
@@ -7375,7 +7389,7 @@ void Tab::OnKeyDown(wxKeyEvent& event)
|
||||
|
||||
void Tab::compare_preset()
|
||||
{
|
||||
wxGetApp().mainframe->diff_dialog.show(m_type);
|
||||
DiffPresetDialog::ensure()->show(m_type);
|
||||
}
|
||||
|
||||
void Tab::transfer_options(const std::string &name_from, const std::string &name_to, std::vector<std::string> options)
|
||||
@@ -7543,8 +7557,10 @@ void Tab::save_preset(std::string name /*= ""*/, bool detach, bool save_to_proje
|
||||
wxGetApp().get_tab(preset_type)->update_tab_ui();
|
||||
}
|
||||
|
||||
// update preset comboboxes in DiffPresetDlg
|
||||
wxGetApp().mainframe->diff_dialog.update_presets(m_type);
|
||||
// show() reloads the presets, so only a visible Compare dialog needs updating.
|
||||
DiffPresetDialog* diff_dialog = DiffPresetDialog::if_built();
|
||||
if (diff_dialog != nullptr && diff_dialog->IsShown())
|
||||
diff_dialog->update_presets(m_type);
|
||||
}
|
||||
|
||||
// Called for a currently selected preset.
|
||||
@@ -8604,20 +8620,8 @@ void Page::activate(ConfigOptionMode mode, std::function<void()> throw_if_cancel
|
||||
#else
|
||||
//m_vsizer->AddSpacer(10);
|
||||
#endif
|
||||
#if HIDE_FIRST_SPLIT_LINE
|
||||
// BBS: no line spliter for first group
|
||||
bool first = true;
|
||||
#endif
|
||||
for (auto group : m_optgroups) {
|
||||
if (!group->activate(throw_if_canceled))
|
||||
continue;
|
||||
m_vsizer->Add(group->sizer, 0, wxEXPAND | (group->is_legend_line() ? (wxLEFT|wxTOP) : wxALL), m_parent->FromDIP(5)); // ORCA use less margin on parameters section
|
||||
group->update_visibility(mode);
|
||||
#if HIDE_FIRST_SPLIT_LINE
|
||||
if (first) group->stb->Hide();
|
||||
first = false;
|
||||
#endif
|
||||
group->reload_config();
|
||||
for (size_t i = 0; i < m_optgroups.size(); ++i) {
|
||||
activate_group(i, mode, throw_if_canceled);
|
||||
throw_if_canceled();
|
||||
}
|
||||
|
||||
@@ -8636,6 +8640,41 @@ void Page::activate(ConfigOptionMode mode, std::function<void()> throw_if_cancel
|
||||
#endif
|
||||
}
|
||||
|
||||
// Builds one option group; false when it already has its controls.
|
||||
bool Page::activate_group(size_t i, ConfigOptionMode mode, std::function<void()> throw_if_canceled)
|
||||
{
|
||||
auto& group = m_optgroups[i];
|
||||
if (!group->activate(throw_if_canceled))
|
||||
return false;
|
||||
m_vsizer->Add(group->sizer, 0, wxEXPAND | (group->is_legend_line() ? (wxLEFT|wxTOP) : wxALL), m_parent->FromDIP(5)); // ORCA use less margin on parameters section
|
||||
group->update_visibility(mode);
|
||||
#if HIDE_FIRST_SPLIT_LINE
|
||||
// BBS: no line spliter for first group
|
||||
if (i == 0) group->stb->Hide();
|
||||
#endif
|
||||
group->reload_config();
|
||||
return true;
|
||||
}
|
||||
|
||||
// The first group without controls.
|
||||
size_t Page::next_group_to_build() const
|
||||
{
|
||||
return std::find_if(m_optgroups.begin(), m_optgroups.end(), [](const auto& group) { return !group->is_activated(); }) - m_optgroups.begin();
|
||||
}
|
||||
|
||||
bool Page::build_pending() const
|
||||
{
|
||||
return next_group_to_build() < m_optgroups.size();
|
||||
}
|
||||
|
||||
bool Page::build_step(ConfigOptionMode mode)
|
||||
{
|
||||
const size_t i = next_group_to_build();
|
||||
if (i < m_optgroups.size())
|
||||
activate_group(i, mode, [] {});
|
||||
return build_pending();
|
||||
}
|
||||
|
||||
void Page::clear()
|
||||
{
|
||||
for (auto group : m_optgroups)
|
||||
|
||||
@@ -94,6 +94,10 @@ public:
|
||||
void reload_config();
|
||||
void update_visibility(ConfigOptionMode mode, bool update_contolls_visibility);
|
||||
void activate(ConfigOptionMode mode, std::function<void()> throw_if_canceled);
|
||||
// Whether an option group has no controls yet.
|
||||
bool build_pending() const;
|
||||
// Builds the next option group that has no controls yet; true while some remain.
|
||||
bool build_step(ConfigOptionMode mode);
|
||||
void clear();
|
||||
void msw_rescale();
|
||||
void sys_color_changed();
|
||||
@@ -121,6 +125,8 @@ public:
|
||||
std::map<std::string, std::string> m_opt_id_map;
|
||||
|
||||
protected:
|
||||
size_t next_group_to_build() const;
|
||||
bool activate_group(size_t i, ConfigOptionMode mode, std::function<void()> throw_if_canceled);
|
||||
// Color of TreeCtrlItem. The wxColour will be updated only if the new wxColour pointer differs from the currently rendered one.
|
||||
const wxColour* m_item_color;
|
||||
};
|
||||
@@ -437,6 +443,10 @@ public:
|
||||
// BBS: new layout
|
||||
void set_expanded(bool value);
|
||||
void restore_last_select_item();
|
||||
// page_build_pending() says whether the selected page has groups without controls, and
|
||||
// page_build_step() builds one.
|
||||
bool page_build_pending() const;
|
||||
bool page_build_step();
|
||||
|
||||
static bool validate_custom_gcode(const wxString& title, const std::string& gcode);
|
||||
bool validate_custom_gcodes();
|
||||
|
||||
@@ -1962,8 +1962,9 @@ DiffPresetDialog::DiffPresetDialog(MainFrame* mainframe)
|
||||
|
||||
assert(wxGetApp().preset_bundle);
|
||||
|
||||
m_preset_bundle_left = std::make_unique<PresetBundle>(*wxGetApp().preset_bundle);
|
||||
m_preset_bundle_right = std::make_unique<PresetBundle>(*wxGetApp().preset_bundle);
|
||||
// show() copies the app's bundle into both before anything is displayed.
|
||||
m_preset_bundle_left = std::make_unique<PresetBundle>();
|
||||
m_preset_bundle_right = std::make_unique<PresetBundle>();
|
||||
|
||||
// Create UI items
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
#include "Widgets/Button.hpp"
|
||||
#include "Widgets/ScrolledWindow.hpp"
|
||||
#include "Lazy.hpp"
|
||||
|
||||
class ScalableButton;
|
||||
class wxStaticText;
|
||||
@@ -416,7 +417,7 @@ public:
|
||||
//------------------------------------------
|
||||
// DiffPresetDialog
|
||||
//------------------------------------------
|
||||
class DiffPresetDialog : public DPIDialog
|
||||
class DiffPresetDialog : public DPIDialog, public LazyInstance<DiffPresetDialog>
|
||||
{
|
||||
DiffViewCtrl* m_tree { nullptr };
|
||||
wxBoxSizer* m_presets_sizer { nullptr };
|
||||
|
||||
@@ -219,6 +219,7 @@ WebViewPanel::WebViewPanel(wxWindow *parent)
|
||||
Bind(wxEVT_CLOSE_WINDOW, &WebViewPanel::OnClose, this);
|
||||
|
||||
m_LoginUpdateTimer = nullptr;
|
||||
update_mode();
|
||||
}
|
||||
|
||||
WebViewPanel::~WebViewPanel()
|
||||
@@ -416,8 +417,7 @@ void WebViewPanel::OnClose(wxCloseEvent& evt)
|
||||
|
||||
void WebViewPanel::OnFreshLoginStatus(wxTimerEvent &event)
|
||||
{
|
||||
auto mainframe = Slic3r::GUI::wxGetApp().mainframe;
|
||||
if (mainframe && mainframe->m_webview == this) {
|
||||
if (WebViewPanel::if_built() == this) {
|
||||
auto* app_config = Slic3r::GUI::wxGetApp().app_config;
|
||||
if (app_config && app_config->get_stealth_mode()) return;
|
||||
Slic3r::GUI::wxGetApp().get_login_info(ORCA_CLOUD_PROVIDER);
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <wx/tbarbase.h>
|
||||
#include "wx/textctrl.h"
|
||||
#include <wx/timer.h>
|
||||
#include "Lazy.hpp"
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
@@ -33,7 +34,7 @@ class NetworkAgent;
|
||||
namespace GUI {
|
||||
|
||||
|
||||
class WebViewPanel : public wxPanel
|
||||
class WebViewPanel : public wxPanel, public LazyInstance<WebViewPanel>
|
||||
{
|
||||
public:
|
||||
WebViewPanel(wxWindow *parent);
|
||||
|
||||
@@ -5,6 +5,9 @@ add_executable(${_TEST_NAME}_tests
|
||||
test_creality_cfs_match.cpp
|
||||
test_dev_mapping.cpp
|
||||
test_filament_bitmap_utils.cpp
|
||||
test_lazy.cpp
|
||||
test_prebuild_queue.cpp
|
||||
test_staged_build.cpp
|
||||
test_network_versions.cpp
|
||||
test_action_source.cpp
|
||||
test_plugin_host_api.cpp
|
||||
|
||||
@@ -0,0 +1,240 @@
|
||||
#include <catch2/catch_all.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
#include "slic3r/GUI/Lazy.hpp"
|
||||
|
||||
using Slic3r::GUI::Lazy;
|
||||
using Slic3r::GUI::LazyBase;
|
||||
using Slic3r::GUI::LazyInstance;
|
||||
using Slic3r::GUI::StagedBuild;
|
||||
|
||||
namespace {
|
||||
|
||||
struct Plain
|
||||
{
|
||||
int value{ 1 };
|
||||
};
|
||||
|
||||
struct One : LazyInstance<One>
|
||||
{
|
||||
int value{ 2 };
|
||||
};
|
||||
|
||||
// Two steps after the constructor.
|
||||
struct Staged : StagedBuild, LazyInstance<Staged>
|
||||
{
|
||||
std::vector<int> ran;
|
||||
Staged()
|
||||
{
|
||||
add_build_step([this] { ran.push_back(1); });
|
||||
add_build_step([this] { ran.push_back(2); });
|
||||
}
|
||||
void add_step(std::function<void()> step) { add_build_step(std::move(step)); }
|
||||
};
|
||||
|
||||
// Owns what the factories make, since a Lazy does not.
|
||||
template <class T>
|
||||
struct Made
|
||||
{
|
||||
std::vector<std::unique_ptr<T>> objects;
|
||||
T* make()
|
||||
{
|
||||
objects.push_back(std::make_unique<T>());
|
||||
return objects.back().get();
|
||||
}
|
||||
typename Lazy<T>::Factory factory()
|
||||
{
|
||||
return [this] { return make(); };
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("The factory runs on the first unit, not at construction", "[Lazy]")
|
||||
{
|
||||
Made<Plain> made;
|
||||
Lazy<Plain> lazy("plain", 0, made.factory());
|
||||
REQUIRE(made.objects.empty());
|
||||
REQUIRE_FALSE(lazy.built());
|
||||
REQUIRE(lazy.pending());
|
||||
REQUIRE(lazy.get() == nullptr);
|
||||
|
||||
REQUIRE_FALSE(lazy.build_step()); // the only unit
|
||||
REQUIRE(made.objects.size() == 1);
|
||||
REQUIRE(lazy.built());
|
||||
REQUIRE_FALSE(lazy.pending());
|
||||
REQUIRE(lazy.get() == made.objects[0].get());
|
||||
REQUIRE_FALSE(lazy.build_step());
|
||||
REQUIRE(made.objects.size() == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("A staged type takes one unit for the constructor and one per step", "[Lazy]")
|
||||
{
|
||||
Made<Staged> made;
|
||||
Lazy<Staged> lazy("staged", 0, made.factory());
|
||||
REQUIRE(lazy.build_step());
|
||||
REQUIRE(made.objects.size() == 1);
|
||||
REQUIRE(lazy.get() == nullptr); // exists but incomplete
|
||||
REQUIRE(lazy.build_step());
|
||||
REQUIRE(made.objects[0]->ran == std::vector<int>{1});
|
||||
REQUIRE_FALSE(lazy.build_step());
|
||||
REQUIRE(made.objects[0]->ran == std::vector<int>{1, 2});
|
||||
REQUIRE(lazy.get() == made.objects[0].get());
|
||||
}
|
||||
|
||||
TEST_CASE("ensure builds whatever is left and is a no-op afterwards", "[Lazy]")
|
||||
{
|
||||
Made<Staged> made;
|
||||
Lazy<Staged> lazy("staged", 0, made.factory());
|
||||
lazy.build_step();
|
||||
Staged* s = lazy.ensure();
|
||||
REQUIRE(s == made.objects[0].get());
|
||||
REQUIRE(s->ran == std::vector<int>{1, 2});
|
||||
REQUIRE(lazy.ensure() == s);
|
||||
REQUIRE(made.objects.size() == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("when_built waits for completion, then runs at once", "[Lazy]")
|
||||
{
|
||||
Made<Staged> made;
|
||||
Lazy<Staged> lazy("staged", 0, made.factory());
|
||||
std::vector<int> seen;
|
||||
lazy.when_built([&](Staged& s) { seen.push_back(int(s.ran.size())); });
|
||||
lazy.build_step();
|
||||
lazy.build_step();
|
||||
REQUIRE(seen.empty());
|
||||
lazy.build_step();
|
||||
REQUIRE(seen == std::vector<int>{2});
|
||||
lazy.when_built([&](Staged&) { seen.push_back(9); });
|
||||
REQUIRE(seen == std::vector<int>{2, 9});
|
||||
}
|
||||
|
||||
TEST_CASE("A LazyInstance type reaches its holder through the statics", "[Lazy]")
|
||||
{
|
||||
REQUIRE(One::if_built() == nullptr);
|
||||
REQUIRE(One::ensure() == nullptr);
|
||||
Made<One> made;
|
||||
{
|
||||
Lazy<One> lazy("one", 0, made.factory());
|
||||
REQUIRE(One::if_built() == nullptr);
|
||||
One* one = One::ensure();
|
||||
REQUIRE(one == made.objects[0].get());
|
||||
REQUIRE(One::if_built() == one);
|
||||
int seen = 0;
|
||||
One::when_built([&](One& o) { seen = o.value; });
|
||||
REQUIRE(seen == 2);
|
||||
}
|
||||
REQUIRE(One::if_built() == nullptr);
|
||||
}
|
||||
|
||||
TEST_CASE("A newer holder replaces the registration; the older one leaves it alone", "[Lazy]")
|
||||
{
|
||||
Made<One> made;
|
||||
auto first = std::make_unique<Lazy<One>>("first", 0, made.factory());
|
||||
first->ensure();
|
||||
Lazy<One> second("second", 0, made.factory());
|
||||
REQUIRE(One::if_built() == nullptr); // the new holder has not built yet
|
||||
second.ensure();
|
||||
REQUIRE(One::if_built() == made.objects[1].get());
|
||||
first.reset();
|
||||
REQUIRE(One::if_built() == made.objects[1].get());
|
||||
}
|
||||
|
||||
TEST_CASE("The holder reports the name and order it was given", "[Lazy]")
|
||||
{
|
||||
Made<Plain> made;
|
||||
Lazy<Plain> lazy("plain", 7, made.factory());
|
||||
LazyBase& base = lazy;
|
||||
REQUIRE(base.name() == "plain");
|
||||
REQUIRE(base.prebuild_order() == 7);
|
||||
}
|
||||
|
||||
TEST_CASE("A unit that re-enters the holder builds nothing twice", "[Lazy]")
|
||||
{
|
||||
Made<Plain> made;
|
||||
Lazy<Plain>* self = nullptr;
|
||||
int nested_units = 0;
|
||||
Lazy<Plain> lazy("plain", 0, [&] {
|
||||
if (self->build_step()) // as if the constructor pumped the event loop into a slice
|
||||
++nested_units;
|
||||
return made.make();
|
||||
});
|
||||
self = &lazy;
|
||||
REQUIRE_FALSE(lazy.build_step());
|
||||
REQUIRE(nested_units == 0);
|
||||
REQUIRE(made.objects.size() == 1);
|
||||
REQUIRE(lazy.built());
|
||||
}
|
||||
|
||||
TEST_CASE("A factory that returns null leaves the holder unbuilt and not pending", "[Lazy]")
|
||||
{
|
||||
int calls = 0;
|
||||
Lazy<Plain> lazy("plain", 0, [&] { ++calls; return static_cast<Plain*>(nullptr); });
|
||||
REQUIRE_FALSE(lazy.build_step());
|
||||
REQUIRE_FALSE(lazy.built());
|
||||
REQUIRE_FALSE(lazy.pending());
|
||||
REQUIRE(lazy.get() == nullptr);
|
||||
REQUIRE_FALSE(lazy.build_step()); // not retried
|
||||
REQUIRE(calls == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("ensure returns null for a factory that returned null", "[Lazy]")
|
||||
{
|
||||
Lazy<Plain> lazy("plain", 0, [] { return static_cast<Plain*>(nullptr); });
|
||||
REQUIRE(lazy.ensure() == nullptr);
|
||||
REQUIRE_FALSE(lazy.built());
|
||||
}
|
||||
|
||||
TEST_CASE("A nested ensure inside the factory returns null", "[Lazy]")
|
||||
{
|
||||
Made<Plain> made;
|
||||
Lazy<Plain>* self = nullptr;
|
||||
Plain* nested = reinterpret_cast<Plain*>(1);
|
||||
Lazy<Plain> lazy("plain", 0, [&] {
|
||||
nested = self->ensure(); // as if the constructor pumped the event loop into a caller
|
||||
return made.make();
|
||||
});
|
||||
self = &lazy;
|
||||
Plain* built = lazy.ensure();
|
||||
REQUIRE(built == made.objects[0].get());
|
||||
REQUIRE(nested == nullptr);
|
||||
}
|
||||
|
||||
TEST_CASE("A nested ensure during a staged step returns null", "[Lazy]")
|
||||
{
|
||||
Made<Staged> made;
|
||||
Lazy<Staged>* self = nullptr;
|
||||
Staged* nested = reinterpret_cast<Staged*>(1);
|
||||
Lazy<Staged> lazy("staged", 0, [&] {
|
||||
Staged* s = made.make();
|
||||
s->add_step([&] { nested = self->ensure(); }); // as if a step pumped the event loop into a caller
|
||||
return s;
|
||||
});
|
||||
self = &lazy;
|
||||
Staged* built = lazy.ensure();
|
||||
REQUIRE(built == made.objects[0].get());
|
||||
REQUIRE(nested == nullptr);
|
||||
}
|
||||
|
||||
TEST_CASE("A unit that throws leaves the holder free to build the rest", "[Lazy]")
|
||||
{
|
||||
Made<Staged> made;
|
||||
bool thrown = false;
|
||||
Lazy<Staged> lazy("staged", 0, [&] {
|
||||
Staged* s = made.make();
|
||||
s->add_step([&] { thrown = true; throw std::runtime_error("step"); });
|
||||
return s;
|
||||
});
|
||||
lazy.build_step();
|
||||
lazy.build_step();
|
||||
lazy.build_step();
|
||||
REQUIRE_THROWS(lazy.build_step());
|
||||
REQUIRE(thrown);
|
||||
REQUIRE(lazy.pending());
|
||||
REQUIRE_FALSE(lazy.build_step()); // the next unit runs
|
||||
REQUIRE(lazy.built());
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
#include <catch2/catch_all.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "slic3r/GUI/PrebuildQueue.hpp"
|
||||
|
||||
using Slic3r::GUI::LazyBase;
|
||||
using Slic3r::GUI::PrebuildQueue;
|
||||
|
||||
namespace {
|
||||
|
||||
// A task with `left` units, each taking `unit_ms` of the shared fake clock and logging its id.
|
||||
struct Counter : LazyBase
|
||||
{
|
||||
std::string id;
|
||||
int left;
|
||||
int order;
|
||||
long long unit_ms{ 1 };
|
||||
inline static std::vector<int> log;
|
||||
inline static long long now = 0;
|
||||
|
||||
Counter(int id, int left, int order, long long unit_ms = 1) : id(std::to_string(id)), left(left), order(order), unit_ms(unit_ms) {}
|
||||
|
||||
const std::string& name() const override { return id; }
|
||||
bool built() const override { return left == 0; }
|
||||
bool build_step() override
|
||||
{
|
||||
now += unit_ms;
|
||||
log.push_back(std::stoi(id));
|
||||
return --left > 0;
|
||||
}
|
||||
int prebuild_order() const override { return order; }
|
||||
};
|
||||
|
||||
// Resets the shared log and clock at the start of a case.
|
||||
struct Reset
|
||||
{
|
||||
Reset() { Counter::log.clear(); Counter::now = 0; }
|
||||
};
|
||||
|
||||
const auto fake_clock = [] { return Counter::now; };
|
||||
const auto no_input = [] { return false; };
|
||||
|
||||
// Runs slices with an unlimited budget until nothing is pending; one task per slice.
|
||||
void drain(PrebuildQueue& q)
|
||||
{
|
||||
while (q.pending())
|
||||
q.run_slice(1000000, fake_clock, no_input);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("Tasks run lowest order first, equal order in the order added", "[PrebuildQueue]")
|
||||
{
|
||||
Reset reset;
|
||||
Counter a{ 1, 1, 10 }, b{ 2, 1, 10 }, c{ 3, 1, 50 }, d{ 4, 1, 100 };
|
||||
PrebuildQueue q;
|
||||
q.add(c);
|
||||
q.add(a);
|
||||
q.add(b);
|
||||
q.add(d);
|
||||
REQUIRE(q.names() == "1, 2, 3, 4");
|
||||
drain(q);
|
||||
REQUIRE(Counter::log == std::vector<int>{1, 2, 3, 4});
|
||||
}
|
||||
|
||||
TEST_CASE("A task with nothing pending is skipped, not removed", "[PrebuildQueue]")
|
||||
{
|
||||
Reset reset;
|
||||
Counter a{ 1, 0, 0 }, b{ 2, 2, 1 };
|
||||
PrebuildQueue q;
|
||||
q.add(a);
|
||||
q.add(b);
|
||||
REQUIRE(q.pending());
|
||||
auto slice = q.run_slice(1, fake_clock, no_input); // one unit of b
|
||||
REQUIRE(slice.units == 1);
|
||||
REQUIRE(Counter::log == std::vector<int>{2});
|
||||
|
||||
a.left = 1; // a's work returned; it comes first again
|
||||
q.run_slice(1, fake_clock, no_input);
|
||||
REQUIRE(Counter::log == std::vector<int>{2, 1});
|
||||
}
|
||||
|
||||
TEST_CASE("A slice with nothing pending runs no unit", "[PrebuildQueue]")
|
||||
{
|
||||
Reset reset;
|
||||
PrebuildQueue q;
|
||||
REQUIRE_FALSE(q.pending());
|
||||
auto slice = q.run_slice(40, fake_clock, no_input);
|
||||
REQUIRE(slice.units == 0);
|
||||
REQUIRE_FALSE(slice.completed);
|
||||
REQUIRE_FALSE(slice.remaining);
|
||||
}
|
||||
|
||||
TEST_CASE("A slice stops once its budget is spent, after the unit that crossed it", "[PrebuildQueue]")
|
||||
{
|
||||
Reset reset;
|
||||
Counter a{ 1, 10, 0, 15 };
|
||||
PrebuildQueue q;
|
||||
q.add(a);
|
||||
auto slice = q.run_slice(40, fake_clock, no_input);
|
||||
REQUIRE(slice.units == 3); // units end at 15, 30 and 45 ms; the one crossing 40 is the last
|
||||
REQUIRE(slice.ms == 45);
|
||||
REQUIRE_FALSE(slice.completed);
|
||||
REQUIRE(slice.remaining);
|
||||
REQUIRE(a.left == 7);
|
||||
}
|
||||
|
||||
TEST_CASE("A slice stops after the unit during which input arrived", "[PrebuildQueue]")
|
||||
{
|
||||
Reset reset;
|
||||
Counter a{ 1, 10, 0 };
|
||||
bool input = false;
|
||||
PrebuildQueue q;
|
||||
q.add(a);
|
||||
auto slice = q.run_slice(40, fake_clock, [&] { input = a.left == 8; return input; });
|
||||
REQUIRE(slice.units == 2);
|
||||
REQUIRE_FALSE(slice.completed);
|
||||
REQUIRE(slice.remaining);
|
||||
}
|
||||
|
||||
TEST_CASE("A slice reports completion, whether work remains, and each unit's time", "[PrebuildQueue]")
|
||||
{
|
||||
Reset reset;
|
||||
Counter a{ 1, 2, 0, 5 }, b{ 2, 1, 1 };
|
||||
std::vector<long long> unit_ms;
|
||||
PrebuildQueue q;
|
||||
q.add(a);
|
||||
q.add(b);
|
||||
auto slice = q.run_slice(40, fake_clock, no_input, [&](const std::string& name, long long ms) {
|
||||
REQUIRE(name == "1");
|
||||
unit_ms.push_back(ms);
|
||||
});
|
||||
REQUIRE(slice.units == 2);
|
||||
REQUIRE(slice.completed);
|
||||
REQUIRE(slice.name == "1");
|
||||
REQUIRE(slice.remaining); // b
|
||||
REQUIRE(unit_ms == std::vector<long long>{5, 5});
|
||||
slice = q.run_slice(40, fake_clock, no_input);
|
||||
REQUIRE(slice.completed);
|
||||
REQUIRE_FALSE(slice.remaining);
|
||||
REQUIRE_FALSE(q.pending());
|
||||
}
|
||||
|
||||
TEST_CASE("A unit may add a task to the queue it runs from", "[PrebuildQueue]")
|
||||
{
|
||||
Reset reset;
|
||||
PrebuildQueue q;
|
||||
Counter later{ 2, 1, 5 };
|
||||
struct Adder : LazyBase
|
||||
{
|
||||
PrebuildQueue& q;
|
||||
Counter& later;
|
||||
std::string id{ "1" };
|
||||
bool done{ false };
|
||||
Adder(PrebuildQueue& q, Counter& later) : q(q), later(later) {}
|
||||
const std::string& name() const override { return id; }
|
||||
bool built() const override { return done; }
|
||||
bool build_step() override
|
||||
{
|
||||
Counter::log.push_back(1);
|
||||
done = true;
|
||||
q.add(later);
|
||||
return false;
|
||||
}
|
||||
int prebuild_order() const override { return 0; }
|
||||
} first{ q, later };
|
||||
q.add(first);
|
||||
drain(q);
|
||||
REQUIRE(Counter::log == std::vector<int>{1, 2});
|
||||
}
|
||||
|
||||
TEST_CASE("clear drops every task", "[PrebuildQueue]")
|
||||
{
|
||||
Reset reset;
|
||||
Counter a{ 1, 1, 0 };
|
||||
PrebuildQueue q;
|
||||
q.add(a);
|
||||
q.clear();
|
||||
REQUIRE_FALSE(q.pending());
|
||||
REQUIRE(q.run_slice(40, fake_clock, no_input).units == 0);
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
#include <catch2/catch_all.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "slic3r/GUI/StagedBuild.hpp"
|
||||
|
||||
using Slic3r::GUI::StagedBuild;
|
||||
|
||||
namespace {
|
||||
|
||||
// Exposes the protected queueing calls and records the order steps ran in.
|
||||
struct Staged : StagedBuild
|
||||
{
|
||||
std::vector<int> ran;
|
||||
void queue(int id) { add_build_step([this, id] { ran.push_back(id); }); }
|
||||
void queue_child(Staged& child) { add_build_steps_of(child); }
|
||||
void queue_nested(int id, int nested)
|
||||
{
|
||||
add_build_step([this, id, nested] {
|
||||
ran.push_back(id);
|
||||
queue(nested);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("Steps run in the order they were queued, one per build_step", "[StagedBuild]")
|
||||
{
|
||||
Staged s;
|
||||
s.queue(1);
|
||||
s.queue(2);
|
||||
s.queue(3);
|
||||
REQUIRE_FALSE(s.built());
|
||||
|
||||
REQUIRE(s.build_step());
|
||||
REQUIRE(s.ran == std::vector<int>{1});
|
||||
REQUIRE(s.build_step());
|
||||
REQUIRE(s.ran == std::vector<int>{1, 2});
|
||||
REQUIRE_FALSE(s.build_step());
|
||||
REQUIRE(s.ran == std::vector<int>{1, 2, 3});
|
||||
REQUIRE(s.built());
|
||||
REQUIRE_FALSE(s.build_step());
|
||||
REQUIRE(s.ran.size() == 3);
|
||||
}
|
||||
|
||||
TEST_CASE("A panel with no steps is built from the start", "[StagedBuild]")
|
||||
{
|
||||
Staged s;
|
||||
REQUIRE(s.built());
|
||||
REQUIRE_FALSE(s.build_step());
|
||||
}
|
||||
|
||||
TEST_CASE("A step may queue another step, which runs after the ones already queued", "[StagedBuild]")
|
||||
{
|
||||
Staged s;
|
||||
s.queue_nested(1, 3);
|
||||
s.queue(2);
|
||||
|
||||
REQUIRE(s.build_step());
|
||||
REQUIRE_FALSE(s.built());
|
||||
REQUIRE(s.build_step());
|
||||
REQUIRE_FALSE(s.build_step());
|
||||
REQUIRE(s.ran == std::vector<int>{1, 2, 3});
|
||||
REQUIRE(s.built());
|
||||
}
|
||||
|
||||
TEST_CASE("A parent waits for steps a child queues after being adopted", "[StagedBuild]")
|
||||
{
|
||||
Staged child;
|
||||
child.queue_nested(1, 2); // step 1 queues step 2 while it runs
|
||||
Staged parent;
|
||||
parent.queue_child(child); // one forwarder, for step 1
|
||||
parent.queue(10);
|
||||
|
||||
REQUIRE(parent.build_step()); // child step 1, which queues step 2
|
||||
REQUIRE(parent.build_step()); // 10; own steps exhausted, the child still has 2
|
||||
REQUIRE_FALSE(parent.built());
|
||||
REQUIRE_FALSE(parent.build_step()); // child step 2
|
||||
REQUIRE(child.ran == std::vector<int>{1, 2});
|
||||
REQUIRE(parent.ran == std::vector<int>{10});
|
||||
REQUIRE(parent.built());
|
||||
}
|
||||
|
||||
TEST_CASE("A child's remaining steps are forwarded one per parent step", "[StagedBuild]")
|
||||
{
|
||||
Staged child;
|
||||
child.queue(1);
|
||||
child.queue(2);
|
||||
child.queue(3);
|
||||
REQUIRE(child.build_step()); // the parent adopts only what is left
|
||||
|
||||
Staged parent;
|
||||
parent.queue_child(child);
|
||||
parent.queue(10);
|
||||
|
||||
REQUIRE(parent.build_step());
|
||||
REQUIRE(child.ran == std::vector<int>{1, 2});
|
||||
REQUIRE(parent.build_step());
|
||||
REQUIRE(child.ran == std::vector<int>{1, 2, 3});
|
||||
REQUIRE(child.built());
|
||||
REQUIRE_FALSE(parent.build_step());
|
||||
REQUIRE(parent.ran == std::vector<int>{10});
|
||||
REQUIRE(parent.built());
|
||||
}
|
||||
Reference in New Issue
Block a user