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
26 changed files with 505 additions and 1125 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

@@ -122,7 +122,7 @@ msgid "On highlighted overhangs only"
msgstr "Nur an hervorgehobenen Überhängen"
msgid "Erase all"
msgstr "Alles löschen"
msgstr ""
msgid "Highlight overhang areas"
msgstr "Bereiche mit Überhang hervorheben"
@@ -350,10 +350,10 @@ msgid "Fixed step drag"
msgstr "Fester Schritt ziehen"
msgid "Context Menu"
msgstr "Kontextmenü"
msgstr ""
msgid "Toggle Auto-Drop"
msgstr "Automatisches Absenken umschalten"
msgstr ""
msgid "Single sided scaling"
msgstr "Einseitige Skalierung"
@@ -509,10 +509,10 @@ msgid "Multiple"
msgstr "Mehrere"
msgid "Count"
msgstr "Anzahl"
msgstr ""
msgid "Gap"
msgstr "Spalt"
msgstr ""
msgid "Spacing"
msgstr "Abstand"
@@ -883,7 +883,7 @@ msgid "Advanced"
msgstr "Erweiterte Einstellungen"
msgid "Reset all options except the text and operation"
msgstr "Alle Optionen außer dem Text und der Operation zurücksetzen"
msgstr ""
msgid ""
"The text cannot be written using the selected font. Please try choosing a "
@@ -1805,16 +1805,16 @@ msgid "Info"
msgstr "Info"
msgid "Loading printer & filament profiles"
msgstr "Lade Drucker- und Filamentprofile"
msgstr ""
msgid "Creating main window"
msgstr "Erstelle Hauptfenster"
msgstr ""
msgid "Loading current preset"
msgstr "Lade aktuelles Preset"
msgstr ""
msgid "Showing main window"
msgstr "Zeige Hauptfenster"
msgstr ""
msgid ""
"The OrcaSlicer configuration file may be corrupted and cannot be parsed.\n"
@@ -1897,12 +1897,9 @@ msgid ""
"Please check your network connectivity\n"
"(HTTP %u)"
msgstr ""
"Verbindung zu OrcaCloud fehlgeschlagen.\n"
"Bitte überprüfen Sie Ihre Netzwerkverbindung\n"
"(HTTP %u)"
msgid "Cloud Error"
msgstr "Cloud-Fehler"
msgstr ""
msgid "Retrieving printer information, please try again later."
msgstr "Empfange Druckerinformationen, bitte später erneut versuchen."
@@ -6103,13 +6100,13 @@ msgid "Export"
msgstr "Exportieren"
msgid "Sync Presets"
msgstr "Presets synchronisieren"
msgstr ""
msgid "Pull and apply the latest presets from OrcaCloud"
msgstr "Die neuesten Presets von OrcaCloud abrufen und anwenden"
msgstr ""
msgid "You must be logged in to sync presets from cloud."
msgstr "Sie müssen angemeldet sein, um Presets aus der Cloud zu synchronisieren."
msgstr ""
msgid "Quit"
msgstr "Beenden"
@@ -6239,7 +6236,7 @@ msgid "Preset Bundle"
msgstr "Vorlagen-Bundle"
msgid "Syncing presets from cloud…"
msgstr "Synchronisiere Presets aus der Cloud…"
msgstr ""
msgid "Help"
msgstr "Hilfe"
@@ -8944,25 +8941,25 @@ msgid "If enabled, reverses the direction of zoom with mouse wheel."
msgstr "Wenn aktiviert, wird die Richtung des Zooms mit dem Mausrad umgekehrt."
msgid "Pan"
msgstr "Schwenken"
msgstr ""
msgid "Left Mouse Drag"
msgstr "Linke Maustaste drücken"
msgstr ""
msgid "Set the action that dragging the left mouse button should perform."
msgstr "Legen Sie die Aktion fest, die das Ziehen der linken Maustaste ausführen soll."
msgstr ""
msgid "Middle Mouse Drag"
msgstr "Mittlere Maustaste drücken"
msgstr ""
msgid "Set the action that dragging the middle mouse button should perform."
msgstr "Legen Sie die Aktion fest, die das Ziehen der mittleren Maustaste ausführen soll."
msgstr ""
msgid "Right Mouse Drag"
msgstr "Rechte Maustaste drücken"
msgstr ""
msgid "Set the action that dragging the right mouse button should perform."
msgstr "Legen Sie die Aktion fest, die das Ziehen der rechten Maustaste ausführen soll."
msgstr ""
msgid "Clear my choice on..."
msgstr "Meine Auswahl löschen bei ..."
@@ -8989,13 +8986,13 @@ msgstr ""
"der Datei."
msgid "Graphics"
msgstr "Grafik"
msgstr ""
msgid "Anti-aliasing"
msgstr "Kantenglättung"
msgstr ""
msgid "MSAA Multiplier"
msgstr "MSAA-Multiplikator"
msgstr ""
msgid ""
"Set the Multi-Sample Anti-Aliasing level.\n"
@@ -9007,19 +9004,12 @@ msgid ""
"\n"
"Requires application restart."
msgstr ""
"Stellen Sie die Stufe der Multi-Sample-Kantenglättung ein.\n"
"Höhere Werte führen zu glatteren Kanten, aber die Auswirkungen auf die "
"Leistung sind exponentiell.\n"
"Niedrigere Werte verbessern die Leistung auf Kosten von gezackten Kanten.\n"
"Wenn deaktiviert, wird empfohlen, FXAA zu aktivieren, um gezackte Kanten mit minimalen Auswirkungen auf die Leistung zu reduzieren.\n"
"\n"
"Erfordert einen Neustart der Anwendung."
msgid "Disabled"
msgstr "Deaktiviert"
msgid "FXAA post-processing"
msgstr "FXAA-Nachbearbeitung"
msgstr ""
msgid ""
"Applies Fast Approximate Anti-Aliasing as a screen-space pass.\n"
@@ -9027,34 +9017,26 @@ msgid ""
"\n"
"Takes effect immediately."
msgstr ""
"Führt Fast Approximate Anti-Aliasing als Bildschirmraum-Pass aus.\n"
"Nützlich, um die MSAA-Einstellung zu deaktivieren oder zu reduzieren, um die Leistung zu verbessern.\n"
"\n"
"ist sofort wirksam"
msgid "FPS"
msgstr "FPS"
msgstr ""
msgid "FPS cap"
msgstr "FPS-Begrenzung"
msgstr ""
msgid "(0 = unlimited)"
msgstr "(0 = unbegrenzt)"
msgstr ""
msgid ""
"Limits viewport frame rate to reduce GPU load and power usage.\n"
"Set to 0 for unlimited frame rate."
msgstr ""
"Begrenzt die Bildrate des Viewports, um die GPU-Auslastung und den "
"Energieverbrauch zu reduzieren.\n"
"Auf 0 setzen für unbegrenzte Bildrate."
msgid "Show FPS overlay"
msgstr "FPS-Overlay anzeigen"
msgstr ""
msgid "Displays current viewport FPS in the top-right corner."
msgstr "Zeigt die aktuelle FPS des Viewports in der oberen rechten Ecke an."
msgstr ""
msgid "Login region"
msgstr "Login region"
@@ -9225,17 +9207,13 @@ msgid "Skip AMS blacklist check"
msgstr "Überspringen der AMS Blacklist-Prüfung"
msgid "(Experimental) Keep painted feature after mesh change"
msgstr "(Experimentell) Behalte bemalte Funktionen nach Mesh-Änderung bei"
msgstr ""
msgid ""
"Attempt to keep painted features (color/seam/support/fuzzy etc.) after "
"changing the object mesh (such as cut/reload from disk/simplify/fix etc.)\n"
"Highly experimental! Slow and may create artifact."
msgstr ""
"Versuchen Sie, bemalte Funktionen (Farbe/Naht/Stütze/unscharf usw.) nach "
"Änderung des Objekt-Meshs (z. B. schneiden/neu laden von der Festplatte/vereinfachen/reparieren usw.) beizubehalten\n"
"Sehr experimentell! Langsam und kann Artefakte erzeugen."
msgid "Allow Abnormal Storage"
msgstr "Fehlerhaften Speicher zulassen"
@@ -10961,37 +10939,26 @@ msgid ""
" %s first layer %d %s, other layers %d %s\n"
" %s max delta %d %s, current delta %d %s\n"
msgstr ""
" - %s:\n"
" %s erste Schicht %d %s, andere Schichten %d %s\n"
" %s maximale Delta %d %s, aktuelle Delta %d %s\n"
msgid ""
"Some first-layer and other-layer temperature pairs exceed safety limits.\n"
msgstr "Einige Temperaturpaare für die erste und andere Schicht überschreiten die Sicherheitsgrenzen.\n"
msgstr ""
msgid ""
"\n"
"Invalid pairs:\n"
msgstr ""
"\n"
"Ungültige Paare:\n"
msgid ""
"\n"
"You can go back to edit values, or continue if this is intentional."
msgstr ""
"\n"
"Sie können zurückgehen, um die Werte zu bearbeiten, oder fortfahren, wenn dies "
"absichtlich ist."
msgid ""
"\n"
"\n"
"Continue anyway?"
msgstr ""
"\n"
"\n"
"Trotzdem fortfahren?"
msgid "Temperature Safety Check"
msgstr "Temperatur-Sicherheitsprüfung"
@@ -11075,10 +11042,6 @@ msgid ""
"\"%1%\"\n"
"and \"%2%\" will open without any changes."
msgstr ""
"Alle \"Neuer Wert\" Einstellungen in\n"
"\"%1%\"\n"
"werden gespeichert und \"%2%\" wird ohne Änderungen geöffnet."
msgid "Click the right mouse button to display the full text."
msgstr ""
@@ -11684,7 +11647,7 @@ msgid "Login"
msgstr "Anmelden"
msgid "Login failed. Please try again."
msgstr "Anmeldung fehlgeschlagen. Bitte versuchen Sie es erneut."
msgstr ""
msgid "[Action Required] "
msgstr "[Aktion erforderlich] "
@@ -11724,16 +11687,16 @@ msgid "Global shortcuts"
msgstr "Globale Tastaturkürzel"
msgid "Pan View"
msgstr "Ansicht verschieben"
msgstr ""
msgid "Rotate View"
msgstr "Ansicht drehen"
msgstr ""
msgid "Middle mouse button"
msgstr "Mittlere Maustaste"
msgstr ""
msgid "Zoom View"
msgstr "Ansicht zoomen"
msgstr ""
msgid ""
"Auto orients selected objects or all objects. If there are selected objects, "
@@ -12636,8 +12599,6 @@ msgid ""
"The Hollow base pattern is not supported by this support type; Rectilinear "
"will be used instead."
msgstr ""
"Das Hohl-Basis-Muster wird von diesem Stütztyp nicht unterstützt; Stattdessen "
"wird das Rechteckmuster verwendet."
msgid ""
"Support enforcers are used but support is not enabled. Please enable support."
@@ -14885,16 +14846,16 @@ msgid "Auto For Match"
msgstr "Automatisch für Übereinstimmung"
msgid "Enable filament dynamic map"
msgstr "Dynamische Filamentzuordnung aktivieren"
msgstr ""
msgid "Enable dynamic filament mapping during print."
msgstr "Dynamische Filamentzuordnung während des Drucks aktivieren."
msgstr ""
msgid "Has filament switcher"
msgstr "Hat Filamentwechsler"
msgstr ""
msgid "Printer has a filament switcher hardware (e.g., AMS)."
msgstr "Der Drucker verfügt über eine Filamentwechsler-Hardware (z. B. AMS)."
msgstr ""
msgid "Flush temperature"
msgstr "Spültemperatur"
@@ -15415,7 +15376,7 @@ msgstr ""
"unterstützt."
msgid "Z-buckling bias optimization (experimental)"
msgstr "Z-Buckling-Bias-Optimierung (experimentell)"
msgstr ""
msgid ""
"Tightens the gyroid wave along the Z (vertical) axis at low infill density "
@@ -15424,11 +15385,6 @@ msgid ""
"~30% sparse infill density and above. Only applies when Sparse infill "
"pattern is set to Gyroid."
msgstr ""
"Strafft die Gyroid-Welle entlang der Z-Achse (vertikal) bei geringer Fülldichte, "
"um die effektive vertikale Säulenlänge zu verkürzen und die Z-Achsen-Kompressions-"
"Knickfestigkeit zu verbessern. Der Filamentverbrauch bleibt erhalten. Keine "
"Auswirkung bei ~30% einfacher Fülldichte und darüber. Gilt nur, wenn das einfache "
"Füllmuster auf Gyroid eingestellt ist."
msgid "Sparse infill pattern"
msgstr "Füllmuster"
@@ -16318,7 +16274,7 @@ msgstr ""
"bringen.Setze den Wert auf 0, um diese Funktion zu deaktivieren."
msgid "Minimum non-zero part cooling fan speed"
msgstr "Minimale nicht-null Lüftergeschwindigkeit für die Teilekühlung"
msgstr ""
msgid ""
"Some part-cooling fans cannot start spinning when commanded below a certain "
@@ -16337,11 +16293,6 @@ msgid ""
"below the one you know it can actually spool at.\n"
"Set to 0 to deactivate."
msgstr ""
"Einige Teilekühlventilatoren können nicht zu drehen beginnen, wenn sie unter einem bestimmten PWM-Arbeitszyklus befehligt werden. Wenn dieser Wert über 0 eingestellt ist, wird jeder nicht-null-Teilekühlventilatorbefehl auf mindestens diesen Prozentsatz angehoben, damit der Lüfter zuverlässig startet. Ein Lüfterbefehl von 0 (Lüfter aus) wird immer genau eingehalten. Diese Begrenzung wird nach jeder anderen Lüfterberechnung angewendet (Erstschicht-Ramp-up, Schichtzeit-Interpolation, Überhangs-/Brücken-/Stützstruktur-Schnittstellen-/Glättungsüberschreibungen), sodass die Skalierung weiterhin im Bereich [dieser Wert, 100%] erfolgt.\n"
"\n"
"Wenn Ihre Firmware den Lüfter bereits unter einem Schwellenwert deaktiviert (z.B. Klipper's [fan] off_below: 0.10 schaltet den Lüfter aus, wenn der befehligte Arbeitszyklus unter 10% liegt), sollten idealerweise dieser Wert und der Firmware-Schwellenwert auf denselben Wert eingestellt werden. Wenn sie übereinstimmen (z.B. off_below: 0.10 in Klipper und 10% hier), garantiert der Slicer, dass er nie einen nicht-null-Wert emittiert, den die Firmware stillschweigend fallen lässt, und der Lüfter nie einen Wert erhält, der unter dem Wert liegt, den er tatsächlich anfahren kann.\n"
"\n"
"Setze den Wert auf 0, um diese Funktion zu deaktivieren."
msgid "%"
msgstr "%"
@@ -18432,7 +18383,7 @@ msgid "Enable filament ramming"
msgstr "Erlaube Filamentrammen"
msgid "Tool change on wipe tower"
msgstr "Werkzeugwechsel auf dem Reinigungsturm"
msgstr ""
msgid ""
"Force the toolhead to travel to the wipe tower before issuing the tool "
@@ -18443,8 +18394,6 @@ msgid ""
"this option if you want the tool change to always be issued above the wipe "
"tower instead."
msgstr ""
"Erzwinge, dass der Werkzeugkopf zum Reinigungsturm fährt, bevor der Werkzeugwechselbefehl (Tx) ausgegeben wird. Nur relevant für Mehrfach-Extruder (Mehrfach-Werkzeugkopf) Drucker, die einen Typ-2-Reinigungsturm verwenden. Standardmäßig überspringt Orca die Fahrt auf Mehrfach-Werkzeugkopf-Maschinen, da die Firmware den Kopfwechsel übernimmt, was dazu führen kann, dass der Tx-Befehl über dem gedruckten Teil ausgegeben wird. Aktivieren Sie diese Option, wenn Sie möchten, dass der Werkzeugwechsel immer über dem Reinigungsturm ausgegeben wird."
msgid "No sparse layers (beta)"
msgstr "Keine dünnen Schichten (Beta)"
@@ -19396,7 +19345,7 @@ msgstr ""
"verschiedene Materialien aufeinandertreffen."
msgid "Cool down from interface boost during prime tower"
msgstr "Abkühlung von der Schnittstellen-Boost während des Reinigungsturms"
msgstr "^"
msgid ""
"When interface-layer temperature boost is active, set the nozzle back to "
@@ -22045,18 +21994,12 @@ msgid ""
"\n"
"Available nozzle profiles for this printer:"
msgstr ""
"\n"
"\n"
"Verfügbare Düsenprofile für diesen Drucker:"
msgid ""
"\n"
"\n"
"Choose YES to switch existing preset:"
msgstr ""
"\n"
"\n"
"Wählen Sie JA, um das vorhandene Profil zu wechseln:"
msgid "Printer Created Successfully"
msgstr "Drucker erfolgreich erstellt"
@@ -23265,16 +23208,16 @@ msgid "Detection radius"
msgstr "Erkennungsradius"
msgid "Selected"
msgstr "Ausgewählt"
msgstr ""
msgid "Auto-generate"
msgstr "Automatisch generieren"
msgstr ""
msgid "Generate brim ears using Max angle and Detection radius"
msgstr "Mausohren mit Maximalwinkel und Erkennungsradius generieren"
msgstr ""
msgid "Add or Select"
msgstr "Hinzufügen oder auswählen"
msgstr ""
msgid ""
"Warning: The brim type is not set to \"painted\", the brim ears will not "
@@ -23287,7 +23230,7 @@ msgid "Set the brim type of this object to \"painted\""
msgstr "Den Brim-Typ dieses Objekts auf \"bemalt\" setzen"
msgid "invalid brim ears"
msgstr "Ungültige Mausohren"
msgstr ""
msgid "Brim Ears"
msgstr "Mausohren"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1 +1 @@
02.00.00.30
02.00.00.29

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 ==="
@@ -177,16 +167,16 @@ format_duration() {
overall_start=$(date +%s)
install_start=$overall_start
# This container runs as root, but the bind-mounted workspace and .flatpak-builder
# cache are host-user-owned, so git's dubious-ownership check rejects them and
# breaks flatpak-builder's git checkouts (e.g. wxWidgets). Trust every repo: safe
# in this ephemeral build container, and covers the workspace, mirrors and builds.
git config --global --add safe.directory '*'
# The workspace and .flatpak-builder cache are bind-mounted from the host.
# Git inside the container may reject cached source repos as unsafe due to
# ownership mismatch, which breaks flatpak-builder when it reuses git sources.
git config --global --add safe.directory /src
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
@@ -24,11 +24,6 @@ finish-args:
- --filesystem=xdg-run/gvfs
- --filesystem=/run/media
- --filesystem=/media
# Network shares (CIFS/NFS) are commonly mounted under /mnt, which the default
# finish-args don't grant; without it, opening a model from such a share fails
# with "The file does not contain any geometry data."
# https://github.com/flathub/com.orcaslicer.OrcaSlicer/issues/2
- --filesystem=/mnt
- --filesystem=/run/spnav.sock:ro
# Allow read-only access to OrcaSlicer's legacy config and cache directories (if they exist) for migration purposes.
- --filesystem=~/.var/app/io.github.orcaslicer.OrcaSlicer:ro
@@ -120,12 +115,7 @@ modules:
- -DwxUSE_ZLIB=sys
- -DwxUSE_LIBJPEG=sys
- -DwxUSE_LIBTIFF=OFF
# sys, not builtin (unlike the static deps build): wxWidgets installs the
# builtin libwxwebp*.so only for static builds, so a shared build leaves
# them missing at runtime. The GNOME runtime provides libwebp.
- -DwxUSE_LIBWEBP=sys
- -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

@@ -32,8 +32,7 @@ namespace AABBTreeLines {
{
Vec<LineType::Dim, typename LineType::Scalar> nearest_point;
const LineType& line = lines[primitive_index];
const Vec<LineType::Dim, typename LineType::Scalar> origin_cast = origin.template cast<typename LineType::Scalar>();
squared_distance = line_alg::distance_to_squared(line, origin_cast, &nearest_point);
squared_distance = line_alg::distance_to_squared(line, origin.template cast<typename LineType::Scalar>(), &nearest_point);
return nearest_point.template cast<ScalarType>();
}
};

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

@@ -193,18 +193,20 @@ void StaticBox::doRender(wxDC& dc)
if ((border_width && border_color.count() > 0) || background_color.count() > 0) {
wxRect rc(0, 0, size.x, size.y);
if (border_width && border_color.count() > 0) {
const double scale = dc.GetContentScaleFactor();
// Snap rect edges to physical pixel boundaries so the 1px pen doesn't straddle a pixel boundary
auto snap = [&](int logical) -> int {
return (int)(ceil(logical * scale) / scale);
};
int deflate = snap(border_width / 2.0); // at 175%: snap(0.5) = snap→1/1.75 ≈ 1
rc.x += deflate;
rc.y += deflate;
rc.width -= deflate * 2;
rc.height -= deflate * 2;
if (dc.GetContentScaleFactor() == 1.0) {
int d = floor(border_width / 2.0);
int d2 = floor(border_width - 1);
rc.x += d;
rc.width -= d2;
rc.y += d;
rc.height -= d2;
} else {
int d = 1;
rc.x += d;
rc.width -= d;
rc.y += d;
rc.height -= d;
}
dc.SetPen(wxPen(border_color.colorForStates(states), border_width, border_style));
} else {
dc.SetPen(wxPen(background_color.colorForStates(states)));

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);
}