mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-18 14:32:36 +00:00
Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
080f27f602 | ||
|
|
23c77f15cf | ||
|
|
00f78c18cf | ||
|
|
2dd3ef7cff | ||
|
|
2bfab589ee | ||
|
|
4a72a3bba2 | ||
|
|
173706750d | ||
|
|
ffb2946b20 | ||
|
|
e89a05b1e4 | ||
|
|
02a8011b97 |
@@ -685,6 +685,18 @@ jobs:
|
|||||||
name: OrcaSlicer_profile_validator_Linux_ubuntu_${{ env.ubuntu-ver }}_${{ env.ver }}
|
name: OrcaSlicer_profile_validator_Linux_ubuntu_${{ env.ubuntu-ver }}_${{ env.ver }}
|
||||||
path: './build/src/Release/OrcaSlicer_profile_validator'
|
path: './build/src/Release/OrcaSlicer_profile_validator'
|
||||||
|
|
||||||
|
# generate_system_cache is what scripts/build_preset_cache.sh bakes the
|
||||||
|
# <vendor>.opc caches with; it was already built by the "Build system
|
||||||
|
# preset cache (Linux)" step above. The .opc format is 64-bit
|
||||||
|
# little-endian native, i.e. identical across every platform Orca ships,
|
||||||
|
# so only the Linux binary is published.
|
||||||
|
- name: Upload generate_system_cache Ubuntu
|
||||||
|
if: ${{ ! env.ACT && runner.os == 'Linux' && !vars.SELF_HOSTED && inputs.arch != 'aarch64' }}
|
||||||
|
uses: actions/upload-artifact@v7
|
||||||
|
with:
|
||||||
|
name: generate_system_cache_Linux_ubuntu_${{ env.ubuntu-ver }}_${{ env.ver }}
|
||||||
|
path: './build/src/dev-utils/Release/generate_system_cache'
|
||||||
|
|
||||||
- name: Deploy Ubuntu release
|
- name: Deploy Ubuntu release
|
||||||
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && env.deploy_nightly == 'true' && runner.os == 'Linux' && !vars.SELF_HOSTED }}
|
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && env.deploy_nightly == 'true' && runner.os == 'Linux' && !vars.SELF_HOSTED }}
|
||||||
uses: WebFreak001/deploy-nightly@v3.2.0
|
uses: WebFreak001/deploy-nightly@v3.2.0
|
||||||
@@ -715,6 +727,17 @@ jobs:
|
|||||||
asset_content_type: application/octet-stream
|
asset_content_type: application/octet-stream
|
||||||
max_releases: 1
|
max_releases: 1
|
||||||
|
|
||||||
|
- name: Deploy Ubuntu generate_system_cache release
|
||||||
|
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED && inputs.arch != 'aarch64' }}
|
||||||
|
uses: WebFreak001/deploy-nightly@v3.2.0
|
||||||
|
with:
|
||||||
|
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
|
||||||
|
release_id: 137995723
|
||||||
|
asset_path: ./build/src/dev-utils/Release/generate_system_cache
|
||||||
|
asset_name: generate_system_cache_Linux${{ env.ubuntu-ver-str }}_nightly
|
||||||
|
asset_content_type: application/octet-stream
|
||||||
|
max_releases: 1
|
||||||
|
|
||||||
- name: Deploy orca_custom_preset_tests
|
- name: Deploy orca_custom_preset_tests
|
||||||
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED && inputs.arch != 'aarch64' }}
|
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED && inputs.arch != 'aarch64' }}
|
||||||
uses: WebFreak001/deploy-nightly@v3.2.0
|
uses: WebFreak001/deploy-nightly@v3.2.0
|
||||||
|
|||||||
@@ -0,0 +1,253 @@
|
|||||||
|
name: Post-merge profiles
|
||||||
|
|
||||||
|
# Push-triggered counterpart to check_profiles.yml (which only gates PRs). When a
|
||||||
|
# profile change lands on main or a release branch, rebuild the affected vendors'
|
||||||
|
# binary preset caches (<vendor>.opc) and publish each as a versioned ZIP asset on
|
||||||
|
# a per-Orca-version release of the profiles repo. From there OrcaCloud's OTA
|
||||||
|
# Manager lists the asset, a maintainer attaches a changelog and hits Publish, and
|
||||||
|
# only then does it become a live OTA update - this workflow does none of that
|
||||||
|
# last part (no changelog, no R2, no webhook).
|
||||||
|
#
|
||||||
|
# Asset contract expected by OrcaCloud's release scanner:
|
||||||
|
# ^(\d+\.\d+\.\d+)_([^_]+)_(\d+(?:\.\d+){3})_(\d{12})\.zip$
|
||||||
|
# <orca_ver>_<vendor>_<profile_version>_<UTC yyyymmddHHMM>.zip (zip root: <vendor>.opc)
|
||||||
|
#
|
||||||
|
# Setup (App + secrets): docs/ota/post-merge-profiles-setup.md
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- release/*
|
||||||
|
paths:
|
||||||
|
- 'resources/profiles/**'
|
||||||
|
- '.github/workflows/post_merge_profiles.yml'
|
||||||
|
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
# One run per branch; let a run finish rather than cancel it, since it publishes.
|
||||||
|
concurrency:
|
||||||
|
group: post-merge-profiles-${{ github.ref }}
|
||||||
|
cancel-in-progress: false
|
||||||
|
|
||||||
|
env:
|
||||||
|
# generate_system_cache is published to this repo's own nightly-builds release
|
||||||
|
# by build_orca.yml's Linux leg. The job guard pins github.repository to
|
||||||
|
# OrcaSlicer/OrcaSlicer, so this resolves there.
|
||||||
|
TOOL_REPO: ${{ github.repository }}
|
||||||
|
TOOL_ASSET: generate_system_cache_Linux_Ubuntu2404_nightly
|
||||||
|
# Where per-vendor ZIP assets are published; OrcaCloud's OTA reads this repo.
|
||||||
|
PROFILES_OWNER: OrcaSlicer
|
||||||
|
PROFILES_REPO: orcaslicer-profiles
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish_profile_caches:
|
||||||
|
name: Publish profile caches
|
||||||
|
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' }}
|
||||||
|
# FOLDER_MERGERS is an environment-scoped variable, shared with the PR
|
||||||
|
# merge bot. Keep this environment free of protection rules so this
|
||||||
|
# push-triggered job does not wait for a reviewer.
|
||||||
|
environment: merge-delegation
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v7
|
||||||
|
with:
|
||||||
|
# Enough history to reach github.event.before for the changed-vendor
|
||||||
|
# diff on a normal push; deeper pushes fall back to HEAD^..HEAD in the
|
||||||
|
# step below. fetch-depth: 0 would clone all of OrcaSlicer's history.
|
||||||
|
fetch-depth: 50
|
||||||
|
|
||||||
|
- name: Resolve changed vendors
|
||||||
|
id: vendors
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
FOLDER_MERGERS: ${{ vars.FOLDER_MERGERS }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
base='${{ github.event.before }}'
|
||||||
|
head='${{ github.sha }}'
|
||||||
|
# Zero SHA (branch created / force push) or manual dispatch: fall back
|
||||||
|
# to this commit's own diff.
|
||||||
|
if [ -z "$base" ] || [ "$base" = "0000000000000000000000000000000000000000" ] || ! git cat-file -e "$base^{commit}" 2>/dev/null; then
|
||||||
|
base="$head^"
|
||||||
|
fi
|
||||||
|
echo "Diffing $base..$head"
|
||||||
|
|
||||||
|
mapfile -t candidates < <(
|
||||||
|
git diff --name-only "$base" "$head" -- resources/profiles \
|
||||||
|
| sed -nE 's#^resources/profiles/([^/]+)/.*#\1#p; s#^resources/profiles/([^/]+)\.json$#\1#p' \
|
||||||
|
| sort -u
|
||||||
|
)
|
||||||
|
|
||||||
|
vendors=()
|
||||||
|
for v in "${candidates[@]:-}"; do
|
||||||
|
[ -n "$v" ] || continue
|
||||||
|
json="resources/profiles/$v.json"
|
||||||
|
# A vendor has a manifest plus either a preset directory or a version
|
||||||
|
# field; this drops non-vendor files such as blacklist.json.
|
||||||
|
if [ -f "$json" ] && { [ -d "resources/profiles/$v" ] || jq -e '.version' "$json" >/dev/null 2>&1; }; then
|
||||||
|
vendors+=("$v")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "${#vendors[@]}" -eq 0 ]; then
|
||||||
|
echo "No changed vendor profiles in this push; nothing to publish."
|
||||||
|
echo "vendors=" >> "$GITHUB_OUTPUT"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# A vendor is eligible only when both the profile directory and its
|
||||||
|
# sibling bundle JSON are covered by at least one FOLDER_MERGERS
|
||||||
|
# grant. The account part is intentionally ignored here: this is a
|
||||||
|
# post-merge safety check, not an authorization check for a command.
|
||||||
|
grants=()
|
||||||
|
while IFS= read -r raw_line; do
|
||||||
|
line="${raw_line#"${raw_line%%[![:space:]]*}"}"
|
||||||
|
line="${line%"${line##*[![:space:]]}"}"
|
||||||
|
[ -n "$line" ] || continue
|
||||||
|
[[ "$line" == \#* ]] && continue
|
||||||
|
[[ "$line" == *:* ]] || continue
|
||||||
|
|
||||||
|
grant="${line#*:}"
|
||||||
|
grant="${grant#"${grant%%[![:space:]]*}"}"
|
||||||
|
grant="${grant%"${grant##*[![:space:]]}"}"
|
||||||
|
while [[ "$grant" == */ ]]; do grant="${grant%/}"; done
|
||||||
|
grants+=("$grant")
|
||||||
|
done <<< "${FOLDER_MERGERS:-}"
|
||||||
|
|
||||||
|
is_granted() {
|
||||||
|
local path="$1"
|
||||||
|
local grant
|
||||||
|
for grant in "${grants[@]:-}"; do
|
||||||
|
if [[ "$path" == "$grant" || "$path" == "$grant/"* ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
unauthorized=()
|
||||||
|
for v in "${vendors[@]}"; do
|
||||||
|
if ! is_granted "resources/profiles/$v" || ! is_granted "resources/profiles/$v.json"; then
|
||||||
|
unauthorized+=("$v")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "${#unauthorized[@]}" -ne 0 ]; then
|
||||||
|
echo "Changed vendor profiles are not covered by FOLDER_MERGERS: ${unauthorized[*]}"
|
||||||
|
echo "No profile caches will be published for this push."
|
||||||
|
echo "vendors=" >> "$GITHUB_OUTPUT"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf 'Changed vendors: %s\n' "${vendors[*]}"
|
||||||
|
echo "vendors=${vendors[*]}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Resolve Orca version
|
||||||
|
id: orca
|
||||||
|
if: steps.vendors.outputs.vendors != ''
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
raw="$(sed -nE 's/^set\(SoftFever_VERSION "([^"]+)".*/\1/p' version.inc | head -1)"
|
||||||
|
[ -n "$raw" ] || { echo "::error::could not read SoftFever_VERSION from version.inc"; exit 1; }
|
||||||
|
orca_ver="${raw%%-*}"
|
||||||
|
if ! [[ "$orca_ver" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||||
|
echo "::error::Orca version '$orca_ver' (from '$raw') is not X.Y.Z"; exit 1
|
||||||
|
fi
|
||||||
|
# release_tag is what the desktop client sends as orca_version and what
|
||||||
|
# OrcaCloud keys R2 on; orca_ver (X.Y.Z) is the asset-name prefix.
|
||||||
|
echo "release_tag=$raw" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "orca_ver=$orca_ver" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "Orca version: release_tag=$raw asset_prefix=$orca_ver"
|
||||||
|
|
||||||
|
- name: Validate profile versions
|
||||||
|
id: pver
|
||||||
|
if: steps.vendors.outputs.vendors != ''
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
: > "$RUNNER_TEMP/pver.tsv"
|
||||||
|
for v in ${{ steps.vendors.outputs.vendors }}; do
|
||||||
|
pv="$(jq -r '.version // empty' "resources/profiles/$v.json")"
|
||||||
|
if ! [[ "$pv" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||||
|
echo "::error::vendor $v version '${pv:-<none>}' must be 4 numeric parts (A.B.C.D) for the OTA asset name; fix resources/profiles/$v.json"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
printf '%s\t%s\n' "$v" "$pv" >> "$RUNNER_TEMP/pver.tsv"
|
||||||
|
echo "$v -> $pv"
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Download generate_system_cache
|
||||||
|
if: steps.vendors.outputs.vendors != ''
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
# gh (with the default token) rather than an unauthenticated curl: keeps
|
||||||
|
# working if TOOL_REPO is ever private and avoids anonymous rate limits.
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
gh release download nightly-builds --repo "$TOOL_REPO" \
|
||||||
|
--pattern "$TOOL_ASSET" --output generate_system_cache --clobber
|
||||||
|
chmod +x generate_system_cache
|
||||||
|
|
||||||
|
- name: Build caches and package assets
|
||||||
|
id: pkg
|
||||||
|
if: steps.vendors.outputs.vendors != ''
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
# One timestamp for the whole run so a multi-vendor merge groups together.
|
||||||
|
ts="$(date -u +%Y%m%d%H%M)"
|
||||||
|
orca_ver='${{ steps.orca.outputs.orca_ver }}'
|
||||||
|
out="$RUNNER_TEMP/assets"
|
||||||
|
mkdir -p "$out"
|
||||||
|
|
||||||
|
for v in ${{ steps.vendors.outputs.vendors }}; do
|
||||||
|
./generate_system_cache -p "$GITHUB_WORKSPACE/resources/profiles" -v "$v" -l 2
|
||||||
|
opc="resources/profiles/$v.opc"
|
||||||
|
[ -f "$opc" ] || { echo "::error::$opc was not generated"; exit 1; }
|
||||||
|
pv="$(awk -F'\t' -v v="$v" '$1==v{print $2}' "$RUNNER_TEMP/pver.tsv")"
|
||||||
|
name="${orca_ver}_${v}_${pv}_${ts}.zip"
|
||||||
|
( cd resources/profiles && zip -q -j "$out/$name" "$v.opc" )
|
||||||
|
echo "packaged $name"
|
||||||
|
done
|
||||||
|
echo "dir=$out" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Mint profiles-repo token
|
||||||
|
id: token
|
||||||
|
if: steps.vendors.outputs.vendors != ''
|
||||||
|
uses: actions/create-github-app-token@v1
|
||||||
|
with:
|
||||||
|
app-id: ${{ secrets.PROFILES_APP_ID }}
|
||||||
|
private-key: ${{ secrets.PROFILES_APP_PRIVATE_KEY }}
|
||||||
|
owner: ${{ env.PROFILES_OWNER }}
|
||||||
|
repositories: ${{ env.PROFILES_REPO }}
|
||||||
|
|
||||||
|
- name: Publish assets to profiles release
|
||||||
|
if: steps.vendors.outputs.vendors != ''
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ steps.token.outputs.token }}
|
||||||
|
RELEASE_TAG: ${{ steps.orca.outputs.release_tag }}
|
||||||
|
ASSET_DIR: ${{ steps.pkg.outputs.dir }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
repo="$PROFILES_OWNER/$PROFILES_REPO"
|
||||||
|
if ! gh release view "$RELEASE_TAG" --repo "$repo" >/dev/null 2>&1; then
|
||||||
|
echo "Creating release $RELEASE_TAG on $repo"
|
||||||
|
gh release create "$RELEASE_TAG" --repo "$repo" \
|
||||||
|
--title "$RELEASE_TAG" --notes "Profile cache assets for Orca $RELEASE_TAG." \
|
||||||
|
--latest=false
|
||||||
|
fi
|
||||||
|
# Asset names are timestamp-unique; a clash means a bug, so don't --clobber.
|
||||||
|
gh release upload "$RELEASE_TAG" --repo "$repo" "$ASSET_DIR"/*.zip
|
||||||
|
|
||||||
|
{
|
||||||
|
echo "### Published to \`$repo\` release \`$RELEASE_TAG\`"
|
||||||
|
for f in "$ASSET_DIR"/*.zip; do echo "- \`$(basename "$f")\`"; done
|
||||||
|
} >> "$GITHUB_STEP_SUMMARY"
|
||||||
@@ -292,5 +292,6 @@ src/slic3r/GUI/AMSDryControl.cpp
|
|||||||
src/slic3r/GUI/AMSDryControl.hpp
|
src/slic3r/GUI/AMSDryControl.hpp
|
||||||
src/libslic3r/PresetBundle.cpp
|
src/libslic3r/PresetBundle.cpp
|
||||||
src/slic3r/GUI/CAD/DesignPanel.cpp
|
src/slic3r/GUI/CAD/DesignPanel.cpp
|
||||||
|
src/slic3r/GUI/CAD/SketchInlineEditor.cpp
|
||||||
src/slic3r/GUI/Gizmos/GLGizmoPrimitive.cpp
|
src/slic3r/GUI/Gizmos/GLGizmoPrimitive.cpp
|
||||||
src/slic3r/GUI/Gizmos/GLGizmoSketch.cpp
|
src/slic3r/GUI/Gizmos/GLGizmoSketch.cpp
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ int main(int argc, char* argv[])
|
|||||||
#else
|
#else
|
||||||
("path,p", po::value<std::string>()->default_value("../../../resources/profiles"), "Path to profiles directory")
|
("path,p", po::value<std::string>()->default_value("../../../resources/profiles"), "Path to profiles directory")
|
||||||
#endif
|
#endif
|
||||||
("log_level,l", po::value<int>()->default_value(2), "Log level (0=trace, 2=info, 4=error)");
|
("log_level,l", po::value<int>()->default_value(2), "Log level (0=trace, 2=info, 4=error)")
|
||||||
|
("vendor,v", po::value<std::string>()->default_value(""), "Vendor name. Optional; generate the cache for this vendor only (the Orca filament library is always included as the inheritance base). All vendors if not specified.");
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
po::variables_map vm;
|
po::variables_map vm;
|
||||||
@@ -38,6 +39,7 @@ int main(int argc, char* argv[])
|
|||||||
|
|
||||||
const std::string profiles_path = vm["path"].as<std::string>();
|
const std::string profiles_path = vm["path"].as<std::string>();
|
||||||
const int log_level = vm["log_level"].as<int>();
|
const int log_level = vm["log_level"].as<int>();
|
||||||
|
const std::string vendor = vm["vendor"].as<std::string>();
|
||||||
|
|
||||||
if (!fs::exists(profiles_path) || !fs::is_directory(profiles_path)) {
|
if (!fs::exists(profiles_path) || !fs::is_directory(profiles_path)) {
|
||||||
std::cerr << "Error: '" << profiles_path << "' is not a valid directory\n";
|
std::cerr << "Error: '" << profiles_path << "' is not a valid directory\n";
|
||||||
@@ -59,8 +61,12 @@ int main(int argc, char* argv[])
|
|||||||
preset_bundle->set_is_validation_mode(true);
|
preset_bundle->set_is_validation_mode(true);
|
||||||
preset_bundle->set_default_suppressed(true);
|
preset_bundle->set_default_suppressed(true);
|
||||||
preset_bundle->set_generate_vendor_caches(true);
|
preset_bundle->set_generate_vendor_caches(true);
|
||||||
|
// Empty == every vendor. Otherwise only this vendor (plus the always-loaded
|
||||||
|
// Orca filament library) is parsed, so only its <vendor>.opc is written.
|
||||||
|
preset_bundle->set_vendor_to_validate(vendor);
|
||||||
|
|
||||||
std::cout << "Loading system presets from: " << profiles_path << "\n";
|
std::cout << "Loading system presets from: " << profiles_path
|
||||||
|
<< (vendor.empty() ? "" : " (vendor: " + vendor + ")") << "\n";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// In validation mode data_dir() is the profiles directory set above, so the
|
// In validation mode data_dir() is the profiles directory set above, so the
|
||||||
@@ -71,6 +77,14 @@ int main(int argc, char* argv[])
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A specific vendor must have produced its own cache; the always-loaded
|
||||||
|
// filament library alone would otherwise mask a misspelt or removed name.
|
||||||
|
if (!vendor.empty() && !fs::exists(fs::path(profiles_path) / (vendor + ".opc"))) {
|
||||||
|
std::cerr << "No cache was generated for vendor '" << vendor << "' under " << profiles_path
|
||||||
|
<< " - check the vendor name.\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
size_t cache_count = 0;
|
size_t cache_count = 0;
|
||||||
for (auto& entry : fs::directory_iterator(profiles_path))
|
for (auto& entry : fs::directory_iterator(profiles_path))
|
||||||
if (boost::iends_with(entry.path().string(), ".opc"))
|
if (boost::iends_with(entry.path().string(), ".opc"))
|
||||||
|
|||||||
@@ -403,7 +403,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
|||||||
// bind to their default mode; the other modes stay in the toolbar flyout. Registered here
|
// bind to their default mode; the other modes stay in the toolbar flyout. Registered here
|
||||||
// where select_tool is in scope; the closures run at key-press time (members are live by then).
|
// where select_tool is in scope; the closures run at key-press time (members are live by then).
|
||||||
auto sk_key = [this, select_tool](int ch, DesignSketchTool::Mode m, const wxString& h) {
|
auto sk_key = [this, select_tool](int ch, DesignSketchTool::Mode m, const wxString& h) {
|
||||||
m_keys_sketch[ch] = [this, select_tool, m, h] { select_tool(m, h); };
|
m_keys_sketch[ch] = [select_tool, m, h] { select_tool(m, h); };
|
||||||
};
|
};
|
||||||
sk_key('L', DesignSketchTool::Mode::Line, _L("Line — click start, then end"));
|
sk_key('L', DesignSketchTool::Mode::Line, _L("Line — click start, then end"));
|
||||||
sk_key('R', DesignSketchTool::Mode::CornerRect, _L("Rectangle — click two opposite corners"));
|
sk_key('R', DesignSketchTool::Mode::CornerRect, _L("Rectangle — click two opposite corners"));
|
||||||
@@ -1445,16 +1445,16 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
|||||||
// Choose FIRST, then open: open_tool() titles the card from m_dressup_type, so setting
|
// Choose FIRST, then open: open_tool() titles the card from m_dressup_type, so setting
|
||||||
// it afterwards left the header reading "Fillet 1" over a chamfer. Nothing in the
|
// it afterwards left the header reading "Fillet 1" over a chamfer. Nothing in the
|
||||||
// opener resets the combo, so this order is safe.
|
// opener resets the combo, so this order is safe.
|
||||||
m_verb_actions["btn:dress#0"] = [this, open_feature, k_dress] {
|
m_verb_actions["btn:dress#0"] = [this, open_feature] {
|
||||||
if (m_dressup_type) m_dressup_type->SetSelection(0); open_feature(k_dress); };
|
if (m_dressup_type) m_dressup_type->SetSelection(0); open_feature(k_dress); };
|
||||||
m_verb_actions["btn:dress#1"] = [this, open_feature, k_dress] {
|
m_verb_actions["btn:dress#1"] = [this, open_feature] {
|
||||||
if (m_dressup_type) m_dressup_type->SetSelection(1); open_feature(k_dress); };
|
if (m_dressup_type) m_dressup_type->SetSelection(1); open_feature(k_dress); };
|
||||||
for (int op = 0; op < 3; ++op)
|
for (int op = 0; op < 3; ++op)
|
||||||
m_verb_actions["btn:bool#" + std::to_string(op)] = [this, open_feature, k_bool, op] {
|
m_verb_actions["btn:bool#" + std::to_string(op)] = [this, open_feature, op] {
|
||||||
open_feature(k_bool);
|
open_feature(k_bool);
|
||||||
if (m_bool_op) { m_bool_op->SetSelection(op); refresh_preview(); } };
|
if (m_bool_op) { m_bool_op->SetSelection(op); refresh_preview(); } };
|
||||||
for (int t = 0; t < 2; ++t)
|
for (int t = 0; t < 2; ++t)
|
||||||
m_verb_actions["btn:pat#" + std::to_string(t)] = [this, open_feature, k_pat, t] {
|
m_verb_actions["btn:pat#" + std::to_string(t)] = [this, open_feature, t] {
|
||||||
open_feature(k_pat); if (m_pattern_type) m_pattern_type->SetSelection(t); };
|
open_feature(k_pat); if (m_pattern_type) m_pattern_type->SetSelection(t); };
|
||||||
|
|
||||||
auto* b_poly = icon_btn("design_polygon", _L("Polygon"));
|
auto* b_poly = icon_btn("design_polygon", _L("Polygon"));
|
||||||
@@ -6625,7 +6625,7 @@ void DesignPanel::show_offer_menu(const wxPoint& screen_pos)
|
|||||||
mate_ghost = false;
|
mate_ghost = false;
|
||||||
};
|
};
|
||||||
menu.Bind(wxEVT_MENU_HIGHLIGHT,
|
menu.Bind(wxEVT_MENU_HIGHLIGHT,
|
||||||
[this, cs_a, cs_b, opts, mate_base, &mate_ghost, drop_ghost](wxMenuEvent& e) {
|
[this, cs_a, cs_b, opts, &mate_ghost, drop_ghost](wxMenuEvent& e) {
|
||||||
const int i = e.GetMenuId() - (mate_base + 1);
|
const int i = e.GetMenuId() - (mate_base + 1);
|
||||||
// Off the palette (a verb row, the header, or nothing) — a stale ghost from the row
|
// Off the palette (a verb row, the header, or nothing) — a stale ghost from the row
|
||||||
// you just left is worse than none, so it goes as soon as the cursor does.
|
// you just left is worse than none, so it goes as soon as the cursor does.
|
||||||
@@ -6635,7 +6635,7 @@ void DesignPanel::show_offer_menu(const wxPoint& screen_pos)
|
|||||||
m_viewport->repaint_now(); // synchronous: the popup owns the loop, a queued repaint is never serviced
|
m_viewport->repaint_now(); // synchronous: the popup owns the loop, a queued repaint is never serviced
|
||||||
});
|
});
|
||||||
|
|
||||||
menu.Bind(wxEVT_MENU, [this, cs_a, cs_b, opts, mate_base, drop_ghost](wxCommandEvent& e) {
|
menu.Bind(wxEVT_MENU, [this, cs_a, cs_b, opts, drop_ghost](wxCommandEvent& e) {
|
||||||
const int i = e.GetId() - (mate_base + 1);
|
const int i = e.GetId() - (mate_base + 1);
|
||||||
if (i < 0 || i >= int(opts.size()) || !opts[i].viable) return;
|
if (i < 0 || i >= int(opts.size()) || !opts[i].viable) return;
|
||||||
drop_ghost(); // the real bodies are about to become the ghost's pose
|
drop_ghost(); // the real bodies are about to become the ghost's pose
|
||||||
@@ -6666,7 +6666,7 @@ void DesignPanel::show_offer_menu(const wxPoint& screen_pos)
|
|||||||
// status message on screen, and clicking one created nothing at all. The whole mate palette
|
// status message on screen, and clicking one created nothing at all. The whole mate palette
|
||||||
// enumerated perfectly and fired nothing. Restricting the range keeps each half to its own ids
|
// enumerated perfectly and fired nothing. Restricting the range keeps each half to its own ids
|
||||||
// regardless of bind order.
|
// regardless of bind order.
|
||||||
menu.Bind(wxEVT_MENU_HIGHLIGHT, [this, &bound, base](wxMenuEvent& e) {
|
menu.Bind(wxEVT_MENU_HIGHLIGHT, [this, &bound](wxMenuEvent& e) {
|
||||||
const int i = e.GetMenuId() - base;
|
const int i = e.GetMenuId() - base;
|
||||||
if (i < 0 || i >= int(bound.size()) || bound[i] == nullptr || bound[i]->hint == nullptr)
|
if (i < 0 || i >= int(bound.size()) || bound[i] == nullptr || bound[i]->hint == nullptr)
|
||||||
return;
|
return;
|
||||||
@@ -6674,7 +6674,7 @@ void DesignPanel::show_offer_menu(const wxPoint& screen_pos)
|
|||||||
set_status(wxGetTranslation(wxString::FromUTF8(bound[i]->hint)));
|
set_status(wxGetTranslation(wxString::FromUTF8(bound[i]->hint)));
|
||||||
m_status->Update(); // the popup owns the loop; without this the line repaints late
|
m_status->Update(); // the popup owns the loop; without this the line repaints late
|
||||||
}, base, base + 499); // 499: the mate section starts at base + 500 (see mate_base)
|
}, base, base + 499); // 499: the mate section starts at base + 500 (see mate_base)
|
||||||
menu.Bind(wxEVT_MENU, [this, &bound, base](wxCommandEvent& e) {
|
menu.Bind(wxEVT_MENU, [this, &bound](wxCommandEvent& e) {
|
||||||
const int i = e.GetId() - base;
|
const int i = e.GetId() - base;
|
||||||
if (i >= 0 && i < int(bound.size()) && bound[i])
|
if (i >= 0 && i < int(bound.size()) && bound[i])
|
||||||
run_offer_action(bound[i]->action);
|
run_offer_action(bound[i]->action);
|
||||||
@@ -7935,7 +7935,7 @@ void DesignPanel::apply_live_constraint(SketchConstraintType type)
|
|||||||
constraint_reject_text(plan.reason, type), int(sel.size())));
|
constraint_reject_text(plan.reason, type), int(sel.size())));
|
||||||
return;
|
return;
|
||||||
case ConstraintPlan::Kind::AskValue:
|
case ConstraintPlan::Kind::AskValue:
|
||||||
m_viewport->open_inline_value(plan.prefill, [this, plan, commit](double v) {
|
m_viewport->open_inline_value(plan.prefill, [plan, commit](double v) {
|
||||||
commit(plan.defs, v);
|
commit(plan.defs, v);
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ void SketchInlineEditor::do_commit()
|
|||||||
// just sits there and the user has no idea what it wants. Say so in the title line and
|
// just sits there and the user has no idea what it wants. Say so in the title line and
|
||||||
// keep editing.
|
// keep editing.
|
||||||
ux_trace("refused", m_title, std::string("typed=") + m_buf);
|
ux_trace("refused", m_title, std::string("typed=") + m_buf);
|
||||||
m_err = (m_buf[0] == '\0') ? into_u8(_L("Enter a number")) : into_u8(_L("Not a number"));
|
m_err = (m_buf[0] == '\0') ? _u8L("Enter a number") : _u8L("Not a number");
|
||||||
m_focus_pending = true;
|
m_focus_pending = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
#endif
|
#endif
|
||||||
#include <imgui/imgui_internal.h>
|
#include <imgui/imgui_internal.h>
|
||||||
|
|
||||||
#define L(s) Slic3r::GUI::I18N::translate((s)).c_str()
|
|
||||||
#define UL(s) Slic3r::GUI::I18N::translate_utf8((s)).c_str()
|
#define UL(s) Slic3r::GUI::I18N::translate_utf8((s)).c_str()
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
@@ -374,15 +373,15 @@ void GLGizmoSketch::on_render_input_window(float x, float y, float bottom_limit)
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (m_sp.is_pocket && has_sel) {
|
if (m_sp.is_pocket && has_sel) {
|
||||||
if (btn(L("Pocket (Cut)"), ok)) apply_pocket();
|
if (btn(_u8L("Pocket (Cut)").c_str(), ok)) apply_pocket();
|
||||||
} else if (is_revolve) {
|
} else if (is_revolve) {
|
||||||
if (btn(L("Revolve"), ok)) apply_revolve();
|
if (btn(_u8L("Revolve").c_str(), ok)) apply_revolve();
|
||||||
} else {
|
} else {
|
||||||
if (btn(L("Extrude"), ok)) apply_extrude();
|
if (btn(_u8L("Extrude").c_str(), ok)) apply_extrude();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui::Button(L("Clear All"), {-1,0})) clear_all();
|
if (ImGui::Button(_u8L("Clear All").c_str(), {-1,0})) clear_all();
|
||||||
if (ImGui::Button(L("Close"), {-1,0})) m_parent.reset_all_gizmos();
|
if (ImGui::Button(_u8L("Close").c_str(), {-1,0})) m_parent.reset_all_gizmos();
|
||||||
|
|
||||||
GizmoImguiEnd();
|
GizmoImguiEnd();
|
||||||
ImGuiWrapper::pop_toolbar_style();
|
ImGuiWrapper::pop_toolbar_style();
|
||||||
|
|||||||
@@ -891,7 +891,7 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection *dependent_
|
|||||||
top_title_temp_v->Add(top_title_temp_h, 1, wxALIGN_CENTER, 0);
|
top_title_temp_v->Add(top_title_temp_h, 1, wxALIGN_CENTER, 0);
|
||||||
m_panel_temp->SetSizer(top_title_temp_v);
|
m_panel_temp->SetSizer(top_title_temp_v);
|
||||||
m_panel_temp->Layout();
|
m_panel_temp->Layout();
|
||||||
m_sizer_top->Add(m_panel_temp, 0, wxALIGN_CENTER, 0);
|
m_sizer_top->Add(m_panel_temp, 1, wxALIGN_CENTER, 0);
|
||||||
|
|
||||||
title_block_middle = new wxPanel(m_table_top, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
title_block_middle = new wxPanel(m_table_top, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
||||||
title_block_middle->SetBackgroundColour(wxColour(172, 172, 172));
|
title_block_middle->SetBackgroundColour(wxColour(172, 172, 172));
|
||||||
@@ -935,12 +935,11 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection *dependent_
|
|||||||
m_panel_newv->Layout();
|
m_panel_newv->Layout();
|
||||||
m_sizer_top->Add(m_panel_newv, 0, wxALIGN_CENTER, 0);
|
m_sizer_top->Add(m_panel_newv, 0, wxALIGN_CENTER, 0);
|
||||||
//m_sizer_top->Add(top_title_newv, 1, wxALIGN_CENTER, 0);
|
//m_sizer_top->Add(top_title_newv, 1, wxALIGN_CENTER, 0);
|
||||||
m_sizer_top->AddStretchSpacer();
|
|
||||||
|
|
||||||
m_table_top->SetSizer(m_sizer_top);
|
m_table_top->SetSizer(m_sizer_top);
|
||||||
m_table_top->Layout();
|
m_table_top->Layout();
|
||||||
m_sizer_top->Fit(m_table_top);
|
m_sizer_top->Fit(m_table_top);
|
||||||
m_sizer_tab->Add(m_table_top, 0, wxEXPAND, 0);
|
m_sizer_tab->Add(m_table_top, 1, 0, 0);
|
||||||
|
|
||||||
m_scrolledWindow = new wxScrolledWindow(m_panel_tab, wxID_ANY, wxDefaultPosition, UNSAVE_CHANGE_DIALOG_SCROLL_WINDOW_SIZE, wxNO_BORDER|wxVSCROLL);
|
m_scrolledWindow = new wxScrolledWindow(m_panel_tab, wxID_ANY, wxDefaultPosition, UNSAVE_CHANGE_DIALOG_SCROLL_WINDOW_SIZE, wxNO_BORDER|wxVSCROLL);
|
||||||
m_scrolledWindow->SetScrollRate(0, 5);
|
m_scrolledWindow->SetScrollRate(0, 5);
|
||||||
|
|||||||
@@ -2042,6 +2042,8 @@ bool PluginManager::delete_and_unsubscribe_cloud_plugin(const std::string& plugi
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unload_plugin(plugin_key);
|
||||||
|
|
||||||
if (!m_cloud_service.request_cloud_unsubscribe(descriptor, error)) {
|
if (!m_cloud_service.request_cloud_unsubscribe(descriptor, error)) {
|
||||||
set_plugin_error(plugin_key, error);
|
set_plugin_error(plugin_key, error);
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
Reference in New Issue
Block a user