Compare commits

..

1 Commits

Author SHA1 Message Date
Ian Chua
367047f731 fix: bambu slice hover popup disappears to fast macos 2026-05-22 19:24:06 +08:00
20 changed files with 140 additions and 283 deletions

View File

@@ -142,7 +142,7 @@ jobs:
flatpak:
name: "Flatpak"
container:
image: ghcr.io/flathub-infra/flatpak-github-actions:gnome-50
image: ghcr.io/flathub-infra/flatpak-github-actions:gnome-49
options: --privileged
volumes:
- /usr/local/lib/android:/usr/local/lib/android

View File

@@ -199,22 +199,22 @@ echo -e "${GREEN}All required dependencies found${NC}"
# Install runtime and SDK if requested
if [[ "$INSTALL_RUNTIME" == true ]]; then
echo -e "${YELLOW}Installing GNOME runtime and SDK...${NC}"
flatpak install --user -y flathub org.gnome.Platform//50
flatpak install --user -y flathub org.gnome.Sdk//50
flatpak install --user -y flathub org.gnome.Platform//49
flatpak install --user -y flathub org.gnome.Sdk//49
fi
# Check if required runtime is available
if ! flatpak info --user org.gnome.Platform//50 &> /dev/null; then
echo -e "${RED}Error: GNOME Platform 50 runtime is not installed.${NC}"
if ! flatpak info --user org.gnome.Platform//49 &> /dev/null; then
echo -e "${RED}Error: GNOME Platform 49 runtime is not installed.${NC}"
echo "Run with -i flag to install it automatically, or install manually:"
echo "flatpak install --user flathub org.gnome.Platform//50"
echo "flatpak install --user flathub org.gnome.Platform//49"
exit 1
fi
if ! flatpak info --user org.gnome.Sdk//50 &> /dev/null; then
echo -e "${RED}Error: GNOME SDK 50 is not installed.${NC}"
if ! flatpak info --user org.gnome.Sdk//49 &> /dev/null; then
echo -e "${RED}Error: GNOME SDK 49 is not installed.${NC}"
echo "Run with -i flag to install it automatically, or install manually:"
echo "flatpak install --user flathub org.gnome.Sdk//50"
echo "flatpak install --user flathub org.gnome.Sdk//49"
exit 1
fi

View File

@@ -22,7 +22,7 @@ ARCH="$(uname -m)"
NO_DEBUG_INFO=false
FORCE_PULL=false
FORCE_CLEAN=true
CONTAINER_IMAGE="ghcr.io/flathub-infra/flatpak-github-actions:gnome-50"
CONTAINER_IMAGE="ghcr.io/flathub-infra/flatpak-github-actions:gnome-49"
normalize_arch() {
case "$1" in
@@ -142,16 +142,6 @@ fi
DOCKER_RUN_ARGS=(run --rm -i --privileged)
# When building from a git worktree, $PROJECT_ROOT/.git is a file pointing to the
# main repo's git dir (outside $PROJECT_ROOT). The git commands and flatpak-builder
# inside the container need that path to resolve, so bind-mount the common git dir
# read-only at its original absolute path. No-op for a normal clone.
GIT_COMMON_DIR="$(git -C "$PROJECT_ROOT" rev-parse --path-format=absolute --git-common-dir 2>/dev/null || true)"
if [ -n "$GIT_COMMON_DIR" ] && [ "$GIT_COMMON_DIR" != "$PROJECT_ROOT/.git" ]; then
echo " Git worktree detected; mounting common git dir read-only: $GIT_COMMON_DIR"
DOCKER_RUN_ARGS+=(-v "$GIT_COMMON_DIR":"$GIT_COMMON_DIR":ro)
fi
# Pass build parameters as env vars so the inner script doesn't need
# variable expansion from the outer shell (avoids quoting issues).
echo "=== Starting Flatpak build inside container ==="
@@ -185,8 +175,8 @@ git config --global --add safe.directory '/src/.flatpak-builder/git/*'
# Install required SDK extensions (not pre-installed in the container image)
flatpak install -y --noninteractive --arch="$BUILD_ARCH" flathub \
org.gnome.Platform//50 \
org.gnome.Sdk//50 \
org.gnome.Platform//49 \
org.gnome.Sdk//49 \
org.freedesktop.Sdk.Extension.llvm21//25.08 || true
install_end=$(date +%s)

View File

@@ -45,12 +45,6 @@
<color type="primary" scheme_preference="dark">#00695C</color>
</branding>
<releases>
<release version="2.4.0-dev" date="2026-05-22" type="development">
<url type="details">https://github.com/OrcaSlicer/OrcaSlicer/releases/tag/nightly-builds</url>
<description>
<p>See the release page for detailed changelog.</p>
</description>
</release>
<release version="2.3.2" date="2025-03-23">
<url type="details">https://github.com/OrcaSlicer/OrcaSlicer/releases/tag/v2.3.2</url>
<description>

View File

@@ -1,6 +1,6 @@
app-id: com.orcaslicer.OrcaSlicer
runtime: org.gnome.Platform
runtime-version: "50"
runtime-version: "49"
sdk: org.gnome.Sdk
sdk-extensions:
- org.freedesktop.Sdk.Extension.llvm21
@@ -115,9 +115,7 @@ modules:
- -DwxUSE_ZLIB=sys
- -DwxUSE_LIBJPEG=sys
- -DwxUSE_LIBTIFF=OFF
- -DwxUSE_LIBWEBP=builtin
- -DwxUSE_EXPAT=sys
- -DwxUSE_NANOSVG=OFF
- -DCMAKE_EXE_LINKER_FLAGS=-fuse-ld=lld
- -DCMAKE_SHARED_LINKER_FLAGS=-fuse-ld=lld
- -DCMAKE_MODULE_LINKER_FLAGS=-fuse-ld=lld

View File

@@ -3,7 +3,7 @@
sudo apt update
sudo apt install build-essential flatpak flatpak-builder gnome-software-plugin-flatpak -y
flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
flatpak install flathub org.gnome.Platform//50 org.gnome.Sdk//50 org.freedesktop.Sdk.Extension.llvm21//25.08
flatpak install flathub org.gnome.Platform//48 org.gnome.Sdk//48
##

View File

@@ -23,7 +23,6 @@
#include <cstring>
#include <iostream>
#include <math.h>
#include <csignal>
#if defined(__linux__) || defined(__LINUX__)
#include <condition_variable>
@@ -7460,13 +7459,6 @@ extern "C" {
#else /* _MSC_VER */
int main(int argc, char **argv)
{
#ifndef _WIN32
// Ignore SIGPIPE so a write to a closed socket (e.g. a dropped printer
// network connection) returns EPIPE to the caller instead of terminating
// the whole process. Without this, losing the printer link kills
// OrcaSlicer with SIGPIPE (exit 141) and produces no crash report.
std::signal(SIGPIPE, SIG_IGN);
#endif
return CLI().run(argc, argv);
}
#endif /* _MSC_VER */

View File

@@ -864,10 +864,10 @@ std::string GCodeWriter::_spiral_travel_to_z(double z, const Vec2d &ij_offset, c
// Determine number of segments based on Resolution
// --------------------------------------------------------------------
const double ref_resolution = 0.01; // reference resolution in mm
const double ref_segments = 8.0; // reference number of segments at reference resolution
const double ref_segments = 16.0; // reference number of segments at reference resolution
// number of linear segments to use for approximating the arc, clamp between 4 and 16
const int segments = std::clamp(int(std::round(ref_segments * (ref_resolution / m_resolution))), 4, 16);
// number of linear segments to use for approximating the arc, clamp between 4 and 24
const int segments = std::clamp(int(std::round(ref_segments * (ref_resolution / m_resolution))), 4, 24);
// --------------------------------------------------------------------
const double px = m_pos(0) - m_x_offset; // take plate offset into consideration

View File

@@ -581,6 +581,14 @@ void Preset::load_info(const std::string& file)
catch (...) {
return;
}
//TODO: workaround for current info file convert, will remove it later
if (this->updated_time == 0) {
this->updated_time = (long long)Slic3r::Utils::get_current_time_utc();
//this->sync_info = "update";
BOOST_LOG_TRIVIAL(info) << boost::format("old info file, updated time to %1%") % this->updated_time;
save_info();
}
}
void Preset::save_info(std::string file)
@@ -2178,29 +2186,19 @@ bool PresetCollection::load_user_preset(std::string name, std::map<std::string,
}
}
// base_id is only required for presets inheriting from a parent. Root presets
// with an empty "inherits" field intentionally have no base_id.
std::string based_id;
const auto base_id = preset_values.find(BBL_JSON_KEY_BASE_ID);
if (base_id != preset_values.end()) {
based_id = base_id->second;
} else {
const auto inherits_iter = preset_values.find(BBL_JSON_KEY_INHERITS);
const bool preset_inherits_from_parent = inherits_iter != preset_values.end() && !inherits_iter->second.empty();
if (preset_inherits_from_parent) {
// This indicates that there is inherits exists but there is no base_id
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__
<< boost::format("can not find base_id, not loading for user preset %1%") % canonical_name;
unlock();
return false;
}
// base_id
if (preset_values.find(BBL_JSON_KEY_BASE_ID) == preset_values.end()) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format("can not find base_id, not loading for user preset %1%") % canonical_name;
unlock();
return false;
}
std::string cloud_base_id = preset_values[BBL_JSON_KEY_BASE_ID];
//filament_id
std::string cloud_filament_id;
if ((m_type == Preset::TYPE_FILAMENT) && preset_values.find(BBL_JSON_KEY_FILAMENT_ID) != preset_values.end()) {
cloud_filament_id = preset_values[BBL_JSON_KEY_FILAMENT_ID];
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " " << canonical_name << " filament_id: " << cloud_filament_id << " base_id: " << based_id;
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " " << canonical_name << " filament_id: " << cloud_filament_id << " base_id: " << cloud_base_id;
}
DynamicPrintConfig new_config, cloud_config;
@@ -2273,7 +2271,7 @@ bool PresetCollection::load_user_preset(std::string name, std::map<std::string,
iter->version = cloud_version.value();
iter->user_id = cloud_user_id;
iter->setting_id = cloud_setting_id;
iter->base_id = based_id;
iter->base_id = cloud_base_id;
iter->filament_id = cloud_filament_id;
update_alias(*iter);
//presets_loaded.emplace_back(*it->second);
@@ -2292,7 +2290,7 @@ bool PresetCollection::load_user_preset(std::string name, std::map<std::string,
preset.version = cloud_version.value();
preset.user_id = cloud_user_id;
preset.setting_id = cloud_setting_id;
preset.base_id = based_id;
preset.base_id = cloud_base_id;
preset.filament_id = cloud_filament_id;
update_alias(preset);
@@ -3653,22 +3651,20 @@ void PresetCollection::set_custom_preset_alias(Preset &preset)
// For printers, there is nothing to remove
// For prints AKA processes, the postfix should be kept
// Alias should be set here, as the preset name may be augmented further later (i.e., prefixing relative path for bundles)
std::string bare_preset_name = get_preset_bare_name(preset.name);
std::string alias_name = bare_preset_name;
const bool is_root_filament_preset =
m_type == Preset::Type::TYPE_FILAMENT &&
preset.config.has(BBL_JSON_KEY_INHERITS) &&
preset.config.option<ConfigOptionString>(BBL_JSON_KEY_INHERITS)->value.empty();
if (is_root_filament_preset) {
const size_t suffix_separator_pos = bare_preset_name.find_first_of("@");
if (suffix_separator_pos != std::string::npos) {
alias_name = bare_preset_name.substr(0, suffix_separator_pos);
boost::trim_right(alias_name);
if (alias_name.empty())
alias_name = bare_preset_name;
std::string alias_name;
std::string preset_name = get_preset_bare_name(preset.name);
if (m_type == Preset::Type::TYPE_FILAMENT && preset.config.has(BBL_JSON_KEY_INHERITS) && preset.config.option<ConfigOptionString>(BBL_JSON_KEY_INHERITS)->value.empty()) {
if (alias_name.empty()) {
size_t end_pos = preset_name.find_first_of("@");
if (end_pos != std::string::npos) {
alias_name = preset_name.substr(0, end_pos);
boost::trim_right(alias_name);
}
}
}
else {
alias_name = preset_name;
}
preset.alias = std::move(alias_name);
m_map_alias_to_profile_name[preset.alias].push_back(preset.name);

View File

@@ -606,24 +606,13 @@ VendorType PresetBundle::get_current_vendor_type()
{
auto t = VendorType::Unknown;
auto config = &printers.get_edited_preset().config;
const auto* printer_model = config->opt<ConfigOptionString>("printer_model");
if (printer_model == nullptr) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": printer_model is "
<< (config->has("printer_model") ? "not a string" : "missing")
<< ", vendor type is Unknown";
return t;
}
std::string vendor_name;
for (const auto& vendor_profile : vendors) {
for (const auto& vendor_model : vendor_profile.second.models) {
if (vendor_model.name == printer_model->value) {
for (auto vendor_profile : vendors) {
for (auto vendor_model : vendor_profile.second.models)
if (vendor_model.name == config->opt_string("printer_model")) {
vendor_name = vendor_profile.first;
break;
}
}
if (!vendor_name.empty())
break;
}
if (!vendor_name.empty())
{
@@ -3790,17 +3779,7 @@ int PresetBundle::get_printer_extruder_count() const
{
const Preset& printer_preset = this->printers.get_edited_preset();
const auto* nozzle_diameter = printer_preset.config.option<ConfigOptionFloats>("nozzle_diameter");
if (nozzle_diameter == nullptr) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": nozzle_diameter is missing, using 1 extruder";
return 1;
}
if (nozzle_diameter->values.empty()) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": nozzle_diameter is empty, using 1 extruder";
return 1;
}
int count = int(nozzle_diameter->values.size());
int count = printer_preset.config.option<ConfigOptionFloats>("nozzle_diameter")->values.size();
return count;
}

View File

@@ -1,7 +1,4 @@
#include <nlohmann/json.hpp>
#include <exception>
#include "DevManager.h"
#include "DevUtil.h"
@@ -154,15 +151,11 @@ namespace Slic3r
{
keep_alive();
MachineObject* obj = this->get_selected_machine();
if (!obj) {
BOOST_LOG_TRIVIAL(warning) << "DeviceManager::check_pushing selected machine not found";
return;
}
std::chrono::system_clock::time_point start = std::chrono::system_clock::now();
auto internal = std::chrono::duration_cast<std::chrono::milliseconds>(start - obj->last_update_time);
if (!obj->is_support_mqtt_alive)
if (obj && !obj->is_support_mqtt_alive)
{
if (internal.count() > TIMEOUT_FOR_STRAT && internal.count() < 1000 * 60 * 60 * 300)
{
@@ -923,33 +916,18 @@ namespace Slic3r
const auto cloud_provider = Slic3r::GUI::wxGetApp().get_printer_cloud_provider();
if (Slic3r::GUI::wxGetApp().is_user_login(cloud_provider))
{
try {
m_manager->check_pushing();
} catch (const std::exception& e) {
BOOST_LOG_TRIVIAL(error) << "DeviceManagerRefresher::on_timer check_pushing exception="
<< e.what();
} catch (...) {
BOOST_LOG_TRIVIAL(error) << "DeviceManagerRefresher::on_timer check_pushing unknown exception";
}
try {
m_manager->check_pushing();
try
{
agent->refresh_connection(cloud_provider);
} catch (const std::exception& e) {
BOOST_LOG_TRIVIAL(error) << "DeviceManagerRefresher::on_timer refresh_connection exception="
<< e.what();
} catch (...) {
BOOST_LOG_TRIVIAL(error) << "DeviceManagerRefresher::on_timer refresh_connection unknown exception";
}
catch (...)
{
;
}
}
// certificate
try {
agent->install_device_cert(obj->get_dev_id(), obj->is_lan_mode_printer());
} catch (const std::exception& e) {
BOOST_LOG_TRIVIAL(error) << "DeviceManagerRefresher::on_timer install_device_cert exception="
<< e.what();
} catch (...) {
BOOST_LOG_TRIVIAL(error) << "DeviceManagerRefresher::on_timer install_device_cert unknown exception";
}
agent->install_device_cert(obj->get_dev_id(), obj->is_lan_mode_printer());
}
}

View File

@@ -338,7 +338,15 @@ void FilamentGroupPopup::OnRadioBtn(int idx)
}
}
void FilamentGroupPopup::OnTimer(wxTimerEvent &event) { Dismiss(); }
void FilamentGroupPopup::OnTimer(wxTimerEvent&)
{
if (IsMouseInPopup()) {
StartTimer();
return;
}
Dismiss();
}
void FilamentGroupPopup::Dismiss() {
m_active = false;
@@ -348,19 +356,22 @@ void FilamentGroupPopup::Dismiss() {
void FilamentGroupPopup::OnLeaveWindow(wxMouseEvent &)
{
wxPoint pos = this->ScreenToClient(wxGetMousePosition());
if (this->GetClientRect().Contains(pos)) return;
if (this->GetScreenRect().Contains(wxGetMousePosition())) return;
StartTimer();
}
void FilamentGroupPopup::OnEnterWindow(wxMouseEvent &)
{
// Ignore spurious ENTER synthesized by PopupWindow::OnMouseEvent2 on macOS.
wxPoint pos = this->ScreenToClient(wxGetMousePosition());
if (!this->GetClientRect().Contains(pos)) return;
if (!this->GetScreenRect().Contains(wxGetMousePosition())) return;
ResetTimer();
}
bool FilamentGroupPopup::IsMouseInPopup() const
{
return this->GetScreenRect().Contains(wxGetMousePosition());
}
void FilamentGroupPopup::UpdateButtonStatus(int hover_idx)
{
for (int i = 0; i < ButtonType::btCount; ++i) {
@@ -394,4 +405,4 @@ void FilamentGroupPopup::UpdateButtonStatus(int hover_idx)
Fit();
}
}} // namespace Slic3r::GUI
}} // namespace Slic3r::GUI

View File

@@ -36,6 +36,7 @@ private:
void OnEnterWindow(wxMouseEvent &);
void OnTimer(wxTimerEvent &event);
void Dismiss();
bool IsMouseInPopup() const;
void CreateBmps();

View File

@@ -5851,20 +5851,16 @@ void GUI_App::reload_settings()
return;
}
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << __LINE__ << " cloud user preset number is: " << user_presets.size();
auto refresh_synced_ui = [this, user_presets = std::move(user_presets)]() mutable {
if (is_closing() || !preset_bundle || !app_config || !mainframe)
return;
// Check the user presets for any system vendors that need to be installed
for (auto data : user_presets) {
if (!check_preset_parent_available(data))
add_pending_vendor_preset(data);
}
load_pending_vendors();
preset_bundle->load_user_presets(*app_config, user_presets, ForwardCompatibilitySubstitutionRule::Enable);
preset_bundle->save_user_presets(*app_config, get_delete_cache_presets());
// Orca: settings changed, refresh ui to reflect the new preset values
// Check the user presets for any system vendors that need to be installed
for (auto data : user_presets) {
if (!check_preset_parent_available(data))
add_pending_vendor_preset(data);
}
load_pending_vendors();
preset_bundle->load_user_presets(*app_config, user_presets, ForwardCompatibilitySubstitutionRule::Enable);
preset_bundle->save_user_presets(*app_config, get_delete_cache_presets());
// Orca: settings changed, refresh ui to reflect the new preset values
auto refresh_synced_ui = [this] {
mainframe->update_side_preset_ui();
for (auto tab : tabs_list) {
tab->reload_config();

View File

@@ -664,14 +664,6 @@ void ParamsPanel::update_mode()
sync_mode_view(m_mode_view);
sync_mode_view(m_current_tab ? dynamic_cast<Tab*>(m_current_tab)->m_mode_view : nullptr);
auto sync_mode_icon = [&](ScalableButton* mode_icon) {
if (mode_icon == nullptr)
return;
mode_icon->Show(app_mode != comDevelop);
};
sync_mode_icon(m_mode_icon);
sync_mode_icon(m_current_tab ? dynamic_cast<Tab*>(m_current_tab)->m_mode_icon : nullptr);
}
void ParamsPanel::msw_rescale()

View File

@@ -146,6 +146,7 @@ protected:
//BBS: GUI refactor
wxPanel* m_top_panel;
ScalableButton* m_mode_icon; // ORCA m_static_title replacement
wxBoxSizer* m_main_sizer;
wxBoxSizer* m_top_sizer;
wxBoxSizer* m_top_left_sizer;
@@ -306,7 +307,6 @@ public:
int m_update_cnt = 0;
ModeSwitchButton *m_mode_view = nullptr;
ScalableButton* m_mode_icon = nullptr; // ORCA m_static_title replacement
SwitchButton *m_extruder_switch = nullptr;
MultiSwitchButton *m_variant_combo = nullptr;

View File

@@ -42,8 +42,7 @@ static std::map<wxColour, wxColour> gDarkColors{
{"#D7E8DE", "#1F2B27"}, // rgb(215, 232, 222) Not Used anymore // Leftover from BBS
{"#2B3436", "#808080"}, // rgb(43, 52, 54) Not Used anymore // Leftover from BBS. Was used as main fill color of icons
{"#ABABAB", "#ABABAB"},
{"#D9D9D9", "#27272A"}, // rgb(217, 217, 217) Sidebar > Toggle button track color
{"#FFFEFE", "#D9D9D9"}, // rgb(255, 254, 254) Sidebar > Toggle button thumb color
{"#D9D9D9", "#2D2D32"}, // rgb(217, 217, 217) Sidebar > Toggle button track color
{"#EBF9F0", "#293F34"},
//{"#F0F0F0", "#4C4C54"},
// ORCA

View File

@@ -220,40 +220,14 @@ void SwitchButton::update()
ModeSwitchButton::ModeSwitchButton(wxWindow* parent, wxWindowID id)
{
background_color = StateColor(
std::make_pair(wxColour("#D9D9D9"), (int) StateColor::Disabled),
std::make_pair(wxColour("#D9D9D9"), (int) StateColor::Normal)
);
std::make_pair(wxColour(0xF1, 0xF1, 0xF1), (int) StateColor::Disabled),
std::make_pair(wxColour(0xE3, 0xE3, 0xE3), (int) StateColor::Pressed),
std::make_pair(wxColour(0xD9, 0xD9, 0xD9), (int) StateColor::Normal));
border_color = StateColor(
std::make_pair(wxColour("#D9D9D9"), (int) StateColor::Disabled),
std::make_pair(wxColour("#D9D9D9"), (int) StateColor::Hovered | ~StateColor::Focused),
std::make_pair(wxColour("#26A69A"), (int) StateColor::Focused),
std::make_pair(wxColour("#D9D9D9"), (int) StateColor::Normal)
);
track_background = StateColor(
std::make_pair(wxColour("#009688"), (int) StateColor::Disabled),
std::make_pair(wxColour("#009688"), (int) StateColor::Normal)
);
track_border = StateColor(
std::make_pair(wxColour("#D9D9D9"), (int) StateColor::Disabled),
std::make_pair(wxColour("#009688"), (int) StateColor::Hovered | ~StateColor::Focused),
std::make_pair(wxColour("#26A69A"), (int) StateColor::Focused),
std::make_pair(wxColour("#009688"), (int) StateColor::Normal)
);
dot_active = StateColor(
std::make_pair(wxColour("#FFFEFE"), (int) StateColor::Disabled),
std::make_pair(wxColour("#FFFEFE"), (int) StateColor::Normal)
);
dot_dimmed = StateColor(
std::make_pair(wxColour("#EEEEEE"), (int) StateColor::Disabled),
std::make_pair(wxColour("#EEEEEE"), (int) StateColor::Normal)
);
text_color = StateColor(
std::make_pair(wxColour("#6B6B6B"), (int) StateColor::Disabled),
std::make_pair(wxColour("#6B6B6B"), (int) StateColor::Normal)
);
state_handler.attach(std::vector<StateColor const*>{&dot_active, &dot_dimmed, &text_color});
state_handler.update_binds();
std::make_pair(wxColour(0xEA, 0xEA, 0xEA), (int) StateColor::Disabled),
std::make_pair(wxColour(0xBC, 0xBC, 0xBC), (int) StateColor::Hovered),
std::make_pair(wxColour(0xC8, 0xC8, 0xC8), (int) StateColor::Focused),
std::make_pair(wxColour(0xCE, 0xCE, 0xCE), (int) StateColor::Normal));
StaticBox::Create(parent, id, wxDefaultPosition, wxDefaultSize, 0);
SetBackgroundColour(StaticBox::GetParentBackgroundColor(parent));
@@ -289,7 +263,7 @@ void ModeSwitchButton::SelectAndNotify(int selection)
void ModeSwitchButton::Rescale()
{
const wxSize button_size = FromDIP(wxSize(48, 18));
const wxSize button_size = FromDIP(wxSize(48, 20));
SetMinSize(button_size);
SetMaxSize(button_size);
SetSize(button_size);
@@ -300,70 +274,63 @@ void ModeSwitchButton::Rescale()
bool ModeSwitchButton::Enable(bool enable /* = true */)
{
const bool changed = StaticBox::Enable(enable);
if (changed){
wxCommandEvent e(EVT_ENABLE_CHANGED);
e.SetEventObject(this);
GetEventHandler()->ProcessEvent(e);
m_enabled = enable; // IsEnabled() not works because variable changes after paint event
if (changed)
Refresh();
}
return changed;
}
void ModeSwitchButton::doRender(wxDC& dc)
{
const wxRect bounds = GetClientRect();
dc.SetPen(*wxTRANSPARENT_PEN);
dc.SetBrush(wxBrush(GetBackgroundColour()));
dc.DrawRectangle(GetClientRect());
const wxRect bounds = GetClientRect().Deflate(1);
if (bounds.width <= 0 || bounds.height <= 0)
return;
const int states = state_handler.states();
const bool hovered = (states & StateHandler::Hovered) != 0;
const bool focused = (states & StateHandler::Focused) != 0;
const bool disabled = !IsEnabled();
const wxColour track_fill = disabled ? wxColour(0xD0, 0xD0, 0xD4) :
m_pressed ? wxColour(0x5A, 0x5D, 0x64) : wxColour(0x66, 0x69, 0x70);
const wxColour track_border = disabled ? wxColour(0xDD, 0xDD, 0xE0) :
focused ? wxColour("#009688") :
hovered ? wxColour(0x7A, 0x7D, 0x84) : wxColour(0x75, 0x78, 0x7F);
const wxColour active_fill = disabled ? wxColour(0x9E, 0xBE, 0xB9) :
m_pressed ? wxColour(0x00877B) : wxColour("#009688");
const wxColour active_dot = disabled ? wxColour(0xEC, 0xF4, 0xF2) : wxColour(0xB7, 0xEB, 0xE3);
const wxColour inactive_dot = disabled ? wxColour(0xF2, 0xF2, 0xF4) : wxColour(0xB5, 0xB7, 0xBD);
const wxColour thumb_fill = disabled ? wxColour(0xFA, 0xFA, 0xFA) : *wxWHITE;
const wxColour thumb_border = disabled ? wxColour(0xE7, 0xE7, 0xEA) : wxColour(0xDD, 0xDF, 0xE3);
dc.SetPen(wxPen(track_border, 1));
dc.SetBrush(wxBrush(track_fill));
dc.DrawRoundedRectangle(bounds, bounds.height / 2.0);
const wxRect thumb = thumb_rect_for(m_selection);
const int fill_right = std::min(bounds.GetRight(), thumb.GetX() + thumb.GetWidth() / 2 + FromDIP(2));
wxRect active(bounds.x, bounds.y, fill_right - bounds.x + 1, bounds.height);
dc.SetPen(*wxTRANSPARENT_PEN);
dc.SetBrush(wxBrush(GetBackgroundColour()));
dc.DrawRectangle(bounds);
dc.SetBrush(wxBrush(active_fill));
dc.DrawRoundedRectangle(active, bounds.height / 2.0);
int states = state_handler.states();
double v_center = bounds.height / 2.0;
const int dot_radius = std::max(FromDIP(1), thumb.height / 7);
for (int idx = 0; idx < 3; ++idx) {
if (idx == m_selection)
continue;
// Background
dc.SetPen(wxPen(border_color.colorForStates(states), 1));
dc.SetBrush(wxBrush(background_color.colorForStates(states)));
dc.DrawRoundedRectangle(bounds, v_center);
if (m_enabled) {
double dot_dist = (bounds.width - bounds.height) * 0.50;
// Track
dc.SetPen(wxPen(track_border.colorForStates(states), 1));
dc.SetBrush(wxBrush(track_background.colorForStates(states)));
wxRect track_rc = bounds;
track_rc.width = int(v_center * 2.0 + dot_dist * m_selection);
dc.DrawRoundedRectangle(track_rc, v_center);
// Dots
dc.SetPen(*wxTRANSPARENT_PEN);
for (int idx = 0; idx < 3; ++idx) {
dc.SetBrush(wxBrush((idx <= m_selection ? dot_active : dot_dimmed).colorForStates(states)));
dc.DrawCircle(wxPoint(v_center + dot_dist * idx, v_center), track_rc.height * (double)(idx == m_selection ? 0.32 : 0.16));
}
const wxRect slot = thumb_rect_for(idx);
const wxPoint center(slot.GetX() + slot.GetWidth() / 2, slot.GetY() + slot.GetHeight() / 2);
dc.SetBrush(wxBrush(idx < m_selection ? active_dot : inactive_dot));
dc.DrawCircle(center, dot_radius);
}
else { // Developer mode
wxString str = "DEV";
int kerning = 3; // pixels between chars
dc.SetTextForeground(text_color.colorForStates(states));
wxCoord totalWidth = 0;
for (char c : str)
totalWidth += dc.GetTextExtent(wxString(c)).x + kerning;
totalWidth -= kerning;
wxCoord x = bounds.x + (bounds.width - totalWidth) / 2;
wxCoord y = bounds.y + (bounds.height - dc.GetTextExtent(str).y) / 2 - 1;
for (char c : str) {
wxString ch(c);
dc.DrawText(ch, x, y);
x += dc.GetTextExtent(ch).x + kerning;
}
}
dc.SetPen(wxPen(thumb_border, 1));
dc.SetBrush(wxBrush(thumb_fill));
dc.DrawRoundedRectangle(thumb, thumb.height / 2.0);
}
void ModeSwitchButton::mouseDown(wxMouseEvent& event)

View File

@@ -78,13 +78,7 @@ private:
private:
int m_selection { 0 };
bool m_pressed { false };
bool m_enabled { true };
wxString m_tooltips[3];
StateColor dot_active;
StateColor dot_dimmed;
StateColor text_color;
StateColor track_background;
StateColor track_border;
};
class MultiSwitchButton : public StaticBox

View File

@@ -100,33 +100,3 @@ TEST_CASE("Legacy bundle import without bundle metadata stays in the user preset
CHECK(fs::equivalent(fs::path(imported->file).parent_path().parent_path(), user_root / PRESET_PRINT_NAME));
}
TEST_CASE("Current vendor type tolerates missing printer model", "[Preset][Bundle]")
{
PresetBundle bundle;
VendorProfile orca_vendor("ORCA");
VendorProfile::PrinterModel model;
model.name = "Orca Test";
orca_vendor.models.emplace_back(model);
bundle.vendors.emplace("ORCA", std::move(orca_vendor));
bundle.printers.get_edited_preset().config.erase("printer_model");
CHECK(bundle.get_current_vendor_type() == VendorType::Unknown);
}
TEST_CASE("Printer extruder count tolerates missing nozzle diameter", "[Preset][Bundle]")
{
PresetBundle bundle;
DynamicPrintConfig& config = bundle.printers.get_edited_preset().config;
config.erase("nozzle_diameter");
CHECK(bundle.get_printer_extruder_count() == 1);
config.set_key_value("nozzle_diameter", new ConfigOptionFloats());
CHECK(bundle.get_printer_extruder_count() == 1);
config.set_key_value("nozzle_diameter", new ConfigOptionFloats({ 0.4, 0.6 }));
CHECK(bundle.get_printer_extruder_count() == 2);
}