Merge branch 'main' into main
This commit is contained in:
@@ -782,6 +782,7 @@ void SoHighlightElementAction::initClass()
|
||||
SO_ACTION_INIT_CLASS(SoHighlightElementAction,SoAction);
|
||||
|
||||
SO_ENABLE(SoHighlightElementAction, SoSwitchElement);
|
||||
SO_ENABLE(SoHighlightElementAction, SoModelMatrixElement);
|
||||
|
||||
SO_ACTION_ADD_METHOD(SoNode,nullAction);
|
||||
|
||||
@@ -849,6 +850,7 @@ void SoSelectionElementAction::initClass()
|
||||
SO_ACTION_INIT_CLASS(SoSelectionElementAction,SoAction);
|
||||
|
||||
SO_ENABLE(SoSelectionElementAction, SoSwitchElement);
|
||||
SO_ENABLE(SoSelectionElementAction, SoModelMatrixElement);
|
||||
|
||||
SO_ACTION_ADD_METHOD(SoNode,nullAction);
|
||||
|
||||
|
||||
@@ -54,10 +54,9 @@ from addonmanager_update_all_gui import UpdateAllGUI
|
||||
import addonmanager_utilities as utils
|
||||
import addonmanager_freecad_interface as fci
|
||||
import AddonManager_rc # This is required by Qt, it's not unused
|
||||
from package_list import PackageList, PackageListItemModel
|
||||
from addonmanager_package_details_controller import PackageDetailsController
|
||||
from Widgets.addonmanager_widget_package_details_view import PackageDetailsView
|
||||
from composite_view import CompositeView
|
||||
from Widgets.addonmanager_widget_global_buttons import WidgetGlobalButtonBar
|
||||
from package_list import PackageListItemModel
|
||||
from Addon import Addon
|
||||
from AddonStats import AddonStats
|
||||
from manage_python_dependencies import (
|
||||
@@ -138,11 +137,13 @@ class CommandAddonManager:
|
||||
self.update_all_worker = None
|
||||
self.developer_mode = None
|
||||
self.installer_gui = None
|
||||
self.composite_view = None
|
||||
self.button_bar = None
|
||||
|
||||
self.update_cache = False
|
||||
self.dialog = None
|
||||
self.startup_sequence = []
|
||||
self.packages_with_updates = set()
|
||||
|
||||
# Set up the connection checker
|
||||
self.connection_checker = ConnectionCheckerGUI()
|
||||
@@ -193,7 +194,7 @@ class CommandAddonManager:
|
||||
pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons")
|
||||
w = pref.GetInt("WindowWidth", 800)
|
||||
h = pref.GetInt("WindowHeight", 600)
|
||||
self.dialog.resize(w, h)
|
||||
self.composite_view = CompositeView(self.dialog)
|
||||
self.button_bar = WidgetGlobalButtonBar(self.dialog)
|
||||
|
||||
# If we are checking for updates automatically, hide the Check for updates button:
|
||||
@@ -204,19 +205,11 @@ class CommandAddonManager:
|
||||
self.button_bar.update_all_addons.hide()
|
||||
|
||||
# Set up the listing of packages using the model-view-controller architecture
|
||||
self.package_list = PackageList(self.dialog)
|
||||
self.item_model = PackageListItemModel()
|
||||
self.package_list.setModel(self.item_model)
|
||||
self.dialog.layout().addWidget(self.package_list)
|
||||
self.composite_view.setModel(self.item_model)
|
||||
self.dialog.layout().addWidget(self.composite_view)
|
||||
self.dialog.layout().addWidget(self.button_bar)
|
||||
|
||||
# Package details start out hidden
|
||||
self.packageDetails = PackageDetailsView(self.dialog)
|
||||
self.package_details_controller = PackageDetailsController(self.packageDetails)
|
||||
self.packageDetails.hide()
|
||||
index = self.dialog.layout().indexOf(self.package_list)
|
||||
self.dialog.layout().insertWidget(index, self.packageDetails)
|
||||
|
||||
# set nice icons to everything, by theme with fallback to FreeCAD icons
|
||||
self.dialog.setWindowIcon(QtGui.QIcon(":/icons/AddonManager.svg"))
|
||||
|
||||
@@ -243,17 +236,16 @@ class CommandAddonManager:
|
||||
)
|
||||
self.button_bar.python_dependencies.clicked.connect(self.show_python_updates_dialog)
|
||||
self.button_bar.developer_tools.clicked.connect(self.show_developer_tools)
|
||||
self.package_list.ui.progressBar.stop_clicked.connect(self.stop_update)
|
||||
self.package_list.itemSelected.connect(self.table_row_activated)
|
||||
self.package_list.setEnabled(False)
|
||||
self.package_details_controller.execute.connect(self.executemacro)
|
||||
self.package_details_controller.install.connect(self.launch_installer_gui)
|
||||
self.package_details_controller.uninstall.connect(self.remove)
|
||||
self.package_details_controller.update.connect(self.update)
|
||||
self.package_details_controller.back.connect(self.on_buttonBack_clicked)
|
||||
self.package_details_controller.update_status.connect(self.status_updated)
|
||||
self.composite_view.package_list.ui.progressBar.stop_clicked.connect(self.stop_update)
|
||||
self.composite_view.package_list.setEnabled(False)
|
||||
self.composite_view.execute.connect(self.executemacro)
|
||||
self.composite_view.install.connect(self.launch_installer_gui)
|
||||
self.composite_view.uninstall.connect(self.remove)
|
||||
self.composite_view.update.connect(self.update)
|
||||
self.composite_view.update_status.connect(self.status_updated)
|
||||
|
||||
# center the dialog over the FreeCAD window
|
||||
self.dialog.resize(w, h)
|
||||
mw = FreeCADGui.getMainWindow()
|
||||
self.dialog.move(
|
||||
mw.frameGeometry().topLeft() + mw.rect().center() - self.dialog.rect().center()
|
||||
@@ -400,14 +392,11 @@ class CommandAddonManager:
|
||||
self.check_python_updates,
|
||||
self.fetch_addon_stats,
|
||||
self.fetch_addon_score,
|
||||
self.select_addon,
|
||||
]
|
||||
pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons")
|
||||
if pref.GetBool("DownloadMacros", False):
|
||||
self.startup_sequence.append(self.load_macro_metadata)
|
||||
selection = pref.GetString("SelectedAddon", "")
|
||||
if selection:
|
||||
self.startup_sequence.insert(2, functools.partial(self.select_addon, selection))
|
||||
pref.SetString("SelectedAddon", "")
|
||||
self.number_of_progress_regions = len(self.startup_sequence)
|
||||
self.current_progress_region = 0
|
||||
self.do_next_startup_phase()
|
||||
@@ -428,7 +417,7 @@ class CommandAddonManager:
|
||||
)
|
||||
pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons")
|
||||
pref.SetString("LastCacheUpdate", date.today().isoformat())
|
||||
self.package_list.item_filter.invalidateFilter()
|
||||
self.composite_view.package_list.item_filter.invalidateFilter()
|
||||
|
||||
def populate_packages_table(self) -> None:
|
||||
self.item_model.clear()
|
||||
@@ -481,8 +470,8 @@ class CommandAddonManager:
|
||||
f.write(json.dumps(self.package_cache, indent=" "))
|
||||
|
||||
def activate_table_widgets(self) -> None:
|
||||
self.package_list.setEnabled(True)
|
||||
self.package_list.ui.view_bar.search.setFocus()
|
||||
self.composite_view.package_list.setEnabled(True)
|
||||
self.composite_view.package_list.ui.view_bar.search.setFocus()
|
||||
self.do_next_startup_phase()
|
||||
|
||||
def populate_macros(self) -> None:
|
||||
@@ -581,17 +570,12 @@ class CommandAddonManager:
|
||||
else:
|
||||
self.do_next_startup_phase()
|
||||
|
||||
def select_addon(self, name: str) -> None:
|
||||
found = False
|
||||
for addon in self.item_model.repos:
|
||||
if addon.name == name:
|
||||
self.table_row_activated(addon)
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
FreeCAD.Console.PrintWarning(
|
||||
translate("AddonsInstaller", "Could not find addon '{}' to select\n").format(name)
|
||||
)
|
||||
def select_addon(self) -> None:
|
||||
prefs = fci.Preferences()
|
||||
selection = prefs.get("SelectedAddon")
|
||||
if selection:
|
||||
self.composite_view.package_list.select_addon(selection)
|
||||
prefs.set("SelectedAddon", "")
|
||||
self.do_next_startup_phase()
|
||||
|
||||
def check_updates(self) -> None:
|
||||
@@ -648,7 +632,11 @@ class CommandAddonManager:
|
||||
|
||||
if number_of_updates:
|
||||
self.button_bar.set_number_of_available_updates(number_of_updates)
|
||||
elif hasattr(self, "check_worker") and self.check_worker.isRunning():
|
||||
elif (
|
||||
hasattr(self, "check_worker")
|
||||
and self.check_worker is not None
|
||||
and self.check_worker.isRunning()
|
||||
):
|
||||
self.button_bar.update_all_addons.setText(
|
||||
translate("AddonsInstaller", "Checking for updates...")
|
||||
)
|
||||
@@ -698,14 +686,14 @@ class CommandAddonManager:
|
||||
self.get_addon_score_worker.update_addon_score.connect(self.update_addon_score)
|
||||
self.get_addon_score_worker.start()
|
||||
else:
|
||||
self.package_list.ui.view_bar.set_rankings_available(False)
|
||||
self.composite_view.package_list.ui.view_bar.set_rankings_available(False)
|
||||
self.do_next_startup_phase()
|
||||
|
||||
def update_addon_score(self, addon: Addon):
|
||||
self.item_model.reload_item(addon)
|
||||
|
||||
def score_fetched_successfully(self):
|
||||
self.package_list.ui.view_bar.set_rankings_available(True)
|
||||
self.composite_view.package_list.ui.view_bar.set_rankings_available(True)
|
||||
|
||||
def show_developer_tools(self) -> None:
|
||||
"""Display the developer tools dialog"""
|
||||
@@ -780,27 +768,11 @@ class CommandAddonManager:
|
||||
|
||||
return addonicon
|
||||
|
||||
def table_row_activated(self, selected_repo: Addon) -> None:
|
||||
"""a row was activated, show the relevant data"""
|
||||
|
||||
self.package_list.hide()
|
||||
self.packageDetails.show()
|
||||
self.package_details_controller.show_repo(selected_repo)
|
||||
|
||||
def show_information(self, message: str) -> None:
|
||||
"""shows generic text in the information pane"""
|
||||
|
||||
self.package_list.ui.progressBar.set_status(message)
|
||||
self.package_list.ui.progressBar.repaint()
|
||||
|
||||
def show_workbench(self, repo: Addon) -> None:
|
||||
self.package_list.hide()
|
||||
self.packageDetails.show()
|
||||
self.package_details_controller.show_repo(repo)
|
||||
|
||||
def on_buttonBack_clicked(self) -> None:
|
||||
self.packageDetails.hide()
|
||||
self.package_list.show()
|
||||
self.composite_view.package_list.ui.progressBar.set_status(message)
|
||||
self.composite_view.package_list.ui.progressBar.repaint()
|
||||
|
||||
def append_to_repos_list(self, repo: Addon) -> None:
|
||||
"""this function allows threads to update the main list of workbenches"""
|
||||
@@ -815,7 +787,7 @@ class CommandAddonManager:
|
||||
else:
|
||||
repo.set_status(Addon.Status.NO_UPDATE_AVAILABLE)
|
||||
self.item_model.reload_item(repo)
|
||||
self.package_details_controller.show_repo(repo)
|
||||
self.composite_view.package_details_controller.show_repo(repo)
|
||||
|
||||
def launch_installer_gui(self, addon: Addon) -> None:
|
||||
if self.installer_gui is not None:
|
||||
@@ -861,12 +833,12 @@ class CommandAddonManager:
|
||||
def hide_progress_widgets(self) -> None:
|
||||
"""hides the progress bar and related widgets"""
|
||||
|
||||
self.package_list.ui.progressBar.hide()
|
||||
self.package_list.ui.view_bar.search.setFocus()
|
||||
self.composite_view.package_list.ui.progressBar.hide()
|
||||
self.composite_view.package_list.ui.view_bar.search.setFocus()
|
||||
|
||||
def show_progress_widgets(self) -> None:
|
||||
if self.package_list.ui.progressBar.isHidden():
|
||||
self.package_list.ui.progressBar.show()
|
||||
if self.composite_view.package_list.ui.progressBar.isHidden():
|
||||
self.composite_view.package_list.ui.progressBar.show()
|
||||
|
||||
def update_progress_bar(self, current_value: int, max_value: int) -> None:
|
||||
"""Update the progress bar, showing it if it's hidden"""
|
||||
@@ -883,10 +855,10 @@ class CommandAddonManager:
|
||||
completed_region_portion = (self.current_progress_region - 1) * region_size
|
||||
current_region_portion = (float(current_value) / float(max_value)) * region_size
|
||||
value = completed_region_portion + current_region_portion
|
||||
self.package_list.ui.progressBar.set_value(
|
||||
self.composite_view.package_list.ui.progressBar.set_value(
|
||||
value * 10
|
||||
) # Out of 1000 segments, so it moves sort of smoothly
|
||||
self.package_list.ui.progressBar.repaint()
|
||||
self.composite_view.package_list.ui.progressBar.repaint()
|
||||
|
||||
def stop_update(self) -> None:
|
||||
self.cleanup_workers()
|
||||
@@ -910,7 +882,7 @@ class CommandAddonManager:
|
||||
if repo.status() == Addon.Status.PENDING_RESTART:
|
||||
self.restart_required = True
|
||||
self.item_model.reload_item(repo)
|
||||
self.package_details_controller.show_repo(repo)
|
||||
self.composite_view.package_details_controller.show_repo(repo)
|
||||
if repo in self.packages_with_updates:
|
||||
self.packages_with_updates.remove(repo)
|
||||
self.enable_updates(len(self.packages_with_updates))
|
||||
|
||||
@@ -31,6 +31,7 @@ SET(AddonManager_SRCS
|
||||
addonmanager_macro_parser.py
|
||||
addonmanager_metadata.py
|
||||
addonmanager_package_details_controller.py
|
||||
addonmanager_preferences_defaults.json
|
||||
addonmanager_pyside_interface.py
|
||||
addonmanager_readme_controller.py
|
||||
addonmanager_update_all_gui.py
|
||||
@@ -47,6 +48,7 @@ SET(AddonManager_SRCS
|
||||
change_branch.py
|
||||
change_branch.ui
|
||||
compact_view.py
|
||||
composite_view.py
|
||||
dependency_resolution_dialog.ui
|
||||
developer_mode.ui
|
||||
developer_mode_add_content.ui
|
||||
@@ -69,7 +71,6 @@ SET(AddonManager_SRCS
|
||||
loading.html
|
||||
manage_python_dependencies.py
|
||||
NetworkManager.py
|
||||
addonmanager_package_details_controller.py
|
||||
package_list.py
|
||||
PythonDependencyUpdateDialog.ui
|
||||
select_toolbar_dialog.ui
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
# Addon Manager Future Work
|
||||
|
||||
* Restructure widgets into logical groups to better enable showing and hiding those groups all at once.
|
||||
* Reduce coupling between data and UI, switching logical groupings of widgets into a MVC or similar framework.
|
||||
* Particularly in the addons list
|
||||
* Download Addon statistics from central location.
|
||||
* Allow sorting on those statistics.
|
||||
* Download a "rank" from user-specified locations.
|
||||
* Allow sorting on that rank.
|
||||
* Implement a server-side cache of Addon metadata.
|
||||
* Implement an "offline mode" that does not attempt to use remote data for anything.
|
||||
* When installing a Preference Pack, offer to apply it once installed, and to undo after that.
|
||||
* Better support "headless" mode, with no GUI.
|
||||
* Add "Composite" display mode, showing compact list and details at the same time
|
||||
|
||||
@@ -75,6 +75,8 @@ class WidgetAddonButtons(QtWidgets.QWidget):
|
||||
self.retranslateUi(None)
|
||||
|
||||
def _setup_ui(self):
|
||||
if self.layout():
|
||||
self.setLayout(None) # TODO: Check this
|
||||
self.horizontal_layout = QtWidgets.QHBoxLayout()
|
||||
self.horizontal_layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.back = QtWidgets.QToolButton(self)
|
||||
@@ -98,6 +100,9 @@ class WidgetAddonButtons(QtWidgets.QWidget):
|
||||
self.horizontal_layout.addWidget(self.change_branch)
|
||||
self.setLayout(self.horizontal_layout)
|
||||
|
||||
def set_show_back_button(self, show: bool) -> None:
|
||||
self.back.setVisible(show)
|
||||
|
||||
def _set_icons(self):
|
||||
self.back.setIcon(QtGui.QIcon.fromTheme("back", QtGui.QIcon(":/icons/button_left.svg")))
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ class MessageType(Enum):
|
||||
|
||||
@dataclass
|
||||
class UpdateInformation:
|
||||
unchecked: bool = True
|
||||
check_in_progress: bool = False
|
||||
update_available: bool = False
|
||||
detached_head: bool = False
|
||||
@@ -112,6 +113,7 @@ class PackageDetailsView(QtWidgets.QWidget):
|
||||
self.vertical_layout.addWidget(self.message_label)
|
||||
self.vertical_layout.addWidget(self.location_label)
|
||||
self.vertical_layout.addWidget(self.readme_browser)
|
||||
self.button_bar.hide() # Start with no bar
|
||||
|
||||
def set_location(self, location: Optional[str]):
|
||||
if location is not None:
|
||||
@@ -274,6 +276,8 @@ class PackageDetailsView(QtWidgets.QWidget):
|
||||
def _get_update_status_string(self) -> str:
|
||||
if self.update_info.check_in_progress:
|
||||
return translate("AddonsInstaller", "Update check in progress") + "."
|
||||
elif self.update_info.unchecked:
|
||||
return ""
|
||||
if self.update_info.detached_head:
|
||||
return (
|
||||
translate(
|
||||
|
||||
@@ -63,6 +63,7 @@ class WidgetReadmeBrowser(QtWidgets.QTextBrowser):
|
||||
def setMarkdown(self, md: str):
|
||||
"""Provides an optional fallback to the markdown library for older versions of Qt (prior to 5.15) that did not
|
||||
have native markdown support. Lacking that, plaintext is displayed."""
|
||||
geometry = self.geometry()
|
||||
if hasattr(super(), "setMarkdown"):
|
||||
super().setMarkdown(md)
|
||||
else:
|
||||
@@ -76,6 +77,7 @@ class WidgetReadmeBrowser(QtWidgets.QTextBrowser):
|
||||
FreeCAD.Console.Warning(
|
||||
"Qt < 5.15 and no `import markdown` -- falling back to plain text display\n"
|
||||
)
|
||||
self.setGeometry(geometry)
|
||||
|
||||
def set_resource(self, resource_url: str, image: Optional[QtGui.QImage]):
|
||||
"""Once a resource has been fetched (or the fetch has failed), this method should be used to inform the widget
|
||||
|
||||
@@ -119,7 +119,6 @@ class WidgetViewSelector(QtWidgets.QWidget):
|
||||
self.composite_button.setIcon(
|
||||
QtGui.QIcon.fromTheme("composite_button", QtGui.QIcon(":/icons/composite_view.svg"))
|
||||
)
|
||||
self.composite_button.hide() # TODO: Implement this view
|
||||
|
||||
self.horizontal_layout.addWidget(self.compact_button)
|
||||
self.horizontal_layout.addWidget(self.expanded_button)
|
||||
|
||||
@@ -442,10 +442,8 @@ class GitManager:
|
||||
on the Mac actually requires us to check for that installation."""
|
||||
try:
|
||||
subprocess.check_output(["xcode-select", "-p"])
|
||||
fci.Console.PrintMessage("XCode command line tools are installed: git is available\n")
|
||||
return True
|
||||
except subprocess.CalledProcessError:
|
||||
fci.Console.PrintMessage("XCode command line tools are not installed: not using git\n")
|
||||
return False
|
||||
|
||||
def _synchronous_call_git(self, args: List[str]) -> str:
|
||||
|
||||
@@ -55,7 +55,6 @@ class PackageDetailsController(QtCore.QObject):
|
||||
update = QtCore.Signal(Addon)
|
||||
execute = QtCore.Signal(Addon)
|
||||
update_status = QtCore.Signal(Addon)
|
||||
check_for_update = QtCore.Signal(Addon)
|
||||
|
||||
def __init__(self, widget=None):
|
||||
super().__init__()
|
||||
@@ -63,9 +62,10 @@ class PackageDetailsController(QtCore.QObject):
|
||||
self.readme_controller = ReadmeController(self.ui.readme_browser)
|
||||
self.worker = None
|
||||
self.addon = None
|
||||
self.status_update_thread = None
|
||||
self.update_check_thread = None
|
||||
self.original_disabled_state = None
|
||||
self.original_status = None
|
||||
self.check_for_update_worker = None
|
||||
try:
|
||||
self.git_manager = GitManager()
|
||||
except NoGitFound:
|
||||
@@ -76,9 +76,6 @@ class PackageDetailsController(QtCore.QObject):
|
||||
self.ui.button_bar.install.clicked.connect(lambda: self.install.emit(self.addon))
|
||||
self.ui.button_bar.uninstall.clicked.connect(lambda: self.uninstall.emit(self.addon))
|
||||
self.ui.button_bar.update.clicked.connect(lambda: self.update.emit(self.addon))
|
||||
self.ui.button_bar.check_for_update.clicked.connect(
|
||||
lambda: self.check_for_update.emit(self.addon)
|
||||
)
|
||||
self.ui.button_bar.change_branch.clicked.connect(self.change_branch_clicked)
|
||||
self.ui.button_bar.enable.clicked.connect(self.enable_clicked)
|
||||
self.ui.button_bar.disable.clicked.connect(self.disable_clicked)
|
||||
@@ -89,6 +86,10 @@ class PackageDetailsController(QtCore.QObject):
|
||||
self.addon = repo
|
||||
self.readme_controller.set_addon(repo)
|
||||
self.original_disabled_state = self.addon.is_disabled()
|
||||
if repo is not None:
|
||||
self.ui.button_bar.show()
|
||||
else:
|
||||
self.ui.button_bar.hide()
|
||||
|
||||
if self.worker is not None:
|
||||
if not self.worker.isFinished():
|
||||
@@ -99,6 +100,7 @@ class PackageDetailsController(QtCore.QObject):
|
||||
self.ui.set_installed(installed)
|
||||
update_info = UpdateInformation()
|
||||
if installed:
|
||||
update_info.unchecked = self.addon.status() == Addon.Status.UNCHECKED
|
||||
update_info.update_available = self.addon.status() == Addon.Status.UPDATE_AVAILABLE
|
||||
update_info.check_in_progress = False # TODO: Implement the "check in progress" status
|
||||
if repo.metadata:
|
||||
@@ -117,25 +119,30 @@ class PackageDetailsController(QtCore.QObject):
|
||||
self.update_macro_info(repo)
|
||||
|
||||
if repo.status() == Addon.Status.UNCHECKED:
|
||||
if not self.status_update_thread:
|
||||
self.status_update_thread = QtCore.QThread()
|
||||
self.status_create_addon_list_worker = CheckSingleUpdateWorker(repo)
|
||||
self.status_create_addon_list_worker.moveToThread(self.status_update_thread)
|
||||
self.status_update_thread.finished.connect(
|
||||
self.status_create_addon_list_worker.deleteLater
|
||||
self.ui.button_bar.check_for_update.show()
|
||||
self.ui.button_bar.check_for_update.setText(
|
||||
translate("AddonsInstaller", "Check for " "update")
|
||||
)
|
||||
self.check_for_update.connect(self.status_create_addon_list_worker.do_work)
|
||||
self.status_create_addon_list_worker.update_status.connect(self.display_repo_status)
|
||||
self.status_update_thread.start()
|
||||
update_info.check_in_progress = True
|
||||
self.ui.set_update_available(update_info)
|
||||
self.check_for_update.emit(self.addon)
|
||||
self.ui.button_bar.check_for_update.setEnabled(True)
|
||||
if not self.update_check_thread:
|
||||
self.update_check_thread = QtCore.QThread()
|
||||
self.check_for_update_worker = CheckSingleUpdateWorker(repo)
|
||||
self.check_for_update_worker.moveToThread(self.update_check_thread)
|
||||
self.update_check_thread.finished.connect(self.check_for_update_worker.deleteLater)
|
||||
self.ui.button_bar.check_for_update.clicked.connect(
|
||||
self.check_for_update_worker.do_work
|
||||
)
|
||||
self.check_for_update_worker.update_status.connect(self.display_repo_status)
|
||||
self.update_check_thread.start()
|
||||
else:
|
||||
self.ui.button_bar.check_for_update.hide()
|
||||
|
||||
flags = WarningFlags()
|
||||
flags.required_freecad_version = self.requires_newer_freecad()
|
||||
flags.obsolete = repo.obsolete
|
||||
flags.python2 = repo.python2
|
||||
self.ui.set_warning_flags(flags)
|
||||
self.set_change_branch_button_state()
|
||||
|
||||
def requires_newer_freecad(self) -> Optional[Version]:
|
||||
"""If the current package is not installed, returns the first supported version of
|
||||
@@ -159,7 +166,7 @@ class PackageDetailsController(QtCore.QObject):
|
||||
"""The change branch button is only available for installed Addons that have a .git directory
|
||||
and in runs where the git is available."""
|
||||
|
||||
self.ui.button_bar.change_branch_button.hide()
|
||||
self.ui.button_bar.change_branch.hide()
|
||||
|
||||
pref = fci.ParamGet("User parameter:BaseApp/Preferences/Addons")
|
||||
show_switcher = pref.GetBool("ShowBranchSwitcher", False)
|
||||
@@ -186,7 +193,7 @@ class PackageDetailsController(QtCore.QObject):
|
||||
|
||||
# If all four above checks passed, then it's possible for us to switch
|
||||
# branches, if there are any besides the one we are on: show the button
|
||||
self.ui.button_bar.change_branch_button.show()
|
||||
self.ui.button_bar.change_branch.show()
|
||||
|
||||
def update_macro_info(self, repo: Addon) -> None:
|
||||
if not repo.macro.url:
|
||||
@@ -256,3 +263,7 @@ class PackageDetailsController(QtCore.QObject):
|
||||
self.addon.set_status(Addon.Status.PENDING_RESTART)
|
||||
self.ui.set_new_branch(name)
|
||||
self.update_status.emit(self.addon)
|
||||
|
||||
def display_repo_status(self, addon):
|
||||
self.update_status.emit(self.addon)
|
||||
self.show_repo(self.addon)
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"AddonsStatsURL": "https://freecad.org/addon_stats.json",
|
||||
"AutoCheck": false,
|
||||
"BlockedMacros": "BOLTS,WorkFeatures,how to install,documentation,PartsLibrary,FCGear",
|
||||
"CompositeSplitterState": "",
|
||||
"CustomRepoHash": "",
|
||||
"CustomRepositories": "",
|
||||
"CustomToolbarName": "Auto-Created Macro Toolbar",
|
||||
|
||||
@@ -23,7 +23,15 @@
|
||||
|
||||
""" Provides a class for showing the list view and detail view at the same time. """
|
||||
|
||||
import addonmanager_freecad_interface
|
||||
import base64
|
||||
|
||||
from addonmanager_freecad_interface import Preferences
|
||||
|
||||
from Addon import Addon
|
||||
from Widgets.addonmanager_widget_package_details_view import PackageDetailsView
|
||||
from addonmanager_package_details_controller import PackageDetailsController
|
||||
from Widgets.addonmanager_widget_view_selector import AddonManagerDisplayStyle
|
||||
from package_list import PackageList
|
||||
|
||||
# Get whatever version of PySide we can
|
||||
try:
|
||||
@@ -43,14 +51,110 @@ from PySide import QtCore, QtWidgets
|
||||
|
||||
class CompositeView(QtWidgets.QWidget):
|
||||
"""A widget that displays the Addon Manager's top bar, the list of Addons, and the detail
|
||||
view, all on a single pane (with no switching). Detail view is shown in its "icon-only" mode
|
||||
for the installation, etc. buttons. The bottom bar remains visible throughout."""
|
||||
view. Depending on the view mode selected, these may all be displayed at once, or selecting
|
||||
an addon in the list may case the list to hide and the detail view to show."""
|
||||
|
||||
install = QtCore.Signal(Addon)
|
||||
uninstall = QtCore.Signal(Addon)
|
||||
update = QtCore.Signal(Addon)
|
||||
execute = QtCore.Signal(Addon)
|
||||
update_status = QtCore.Signal(Addon)
|
||||
check_for_update = QtCore.Signal(Addon)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.package_details = PackageDetailsView(self)
|
||||
self.package_details_controller = PackageDetailsController(self.package_details)
|
||||
self.package_list = PackageList(self)
|
||||
prefs = Preferences()
|
||||
self.display_style = prefs.get("ViewStyle")
|
||||
self.main_layout = QtWidgets.QHBoxLayout(self)
|
||||
self.splitter = QtWidgets.QSplitter(self)
|
||||
self.splitter.addWidget(self.package_list)
|
||||
self.package_list.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
|
||||
self.splitter.addWidget(self.package_details)
|
||||
self.package_details.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
|
||||
self.splitter.setOrientation(QtCore.Qt.Horizontal)
|
||||
self.splitter.setContentsMargins(0, 0, 0, 0)
|
||||
self.splitter.setSizePolicy(
|
||||
QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding
|
||||
)
|
||||
self.main_layout.addWidget(self.splitter)
|
||||
self.layout().setContentsMargins(0, 0, 0, 0)
|
||||
self._setup_ui()
|
||||
self._setup_connections()
|
||||
self._restore_splitter_state()
|
||||
|
||||
# TODO: Refactor the Addon Manager's display into four custom widgets:
|
||||
# 1) The top bar showing the filter and search
|
||||
# 2) The package list widget, which can take three forms (expanded, compact, and list)
|
||||
# 3) The installer bar, which can take two forms (text and icon)
|
||||
# 4) The bottom bar
|
||||
def _save_splitter_state(self):
|
||||
"""Write the splitter state into an Addon manager preference, CompositeSplitterState"""
|
||||
prefs = Preferences()
|
||||
state = self.splitter.saveState()
|
||||
encoded = base64.b64encode(state).decode("ASCII")
|
||||
prefs.set("CompositeSplitterState", encoded)
|
||||
|
||||
def _restore_splitter_state(self):
|
||||
"""Restore the splitter state from CompositeSplitterState"""
|
||||
prefs = Preferences()
|
||||
encoded = prefs.get("CompositeSplitterState")
|
||||
if encoded:
|
||||
state = base64.b64decode(encoded)
|
||||
self.splitter.restoreState(state)
|
||||
|
||||
def setModel(self, model):
|
||||
self.package_list.setModel(model)
|
||||
|
||||
def set_display_style(self, style: AddonManagerDisplayStyle):
|
||||
self.display_style = style
|
||||
self._setup_ui()
|
||||
|
||||
def _setup_ui(self):
|
||||
if self.display_style == AddonManagerDisplayStyle.EXPANDED:
|
||||
self._setup_expanded_ui()
|
||||
elif self.display_style == AddonManagerDisplayStyle.COMPACT:
|
||||
self._setup_compact_ui()
|
||||
elif self.display_style == AddonManagerDisplayStyle.COMPOSITE:
|
||||
self._setup_composite_ui()
|
||||
else:
|
||||
raise RuntimeError("Invalid display style")
|
||||
self.package_list.set_view_style(self.display_style)
|
||||
|
||||
def _setup_expanded_ui(self):
|
||||
self.package_list.show()
|
||||
self.package_details.hide()
|
||||
self.package_details.button_bar.set_show_back_button(True)
|
||||
|
||||
def _setup_compact_ui(self):
|
||||
self.package_list.show()
|
||||
self.package_details.hide()
|
||||
self.package_details.button_bar.set_show_back_button(True)
|
||||
|
||||
def _setup_composite_ui(self):
|
||||
self.package_list.show()
|
||||
self.package_details.show()
|
||||
self.package_details.button_bar.set_show_back_button(False)
|
||||
|
||||
def _setup_connections(self):
|
||||
self.package_list.itemSelected.connect(self.addon_selected)
|
||||
self.package_details_controller.back.connect(self._back_button_clicked)
|
||||
self.package_details_controller.install.connect(self.install)
|
||||
self.package_details_controller.uninstall.connect(self.uninstall)
|
||||
self.package_details_controller.update.connect(self.update)
|
||||
self.package_details_controller.execute.connect(self.execute)
|
||||
self.package_details_controller.update_status.connect(self.update_status)
|
||||
self.package_list.ui.view_bar.view_changed.connect(self.set_display_style)
|
||||
self.splitter.splitterMoved.connect(self._splitter_moved)
|
||||
|
||||
def addon_selected(self, addon):
|
||||
self.package_details_controller.show_repo(addon)
|
||||
if self.display_style != AddonManagerDisplayStyle.COMPOSITE:
|
||||
self.package_list.hide()
|
||||
self.package_details.show()
|
||||
self.package_details.button_bar.set_show_back_button(True)
|
||||
|
||||
def _back_button_clicked(self):
|
||||
if self.display_style != AddonManagerDisplayStyle.COMPOSITE:
|
||||
self.package_list.show()
|
||||
self.package_details.hide()
|
||||
|
||||
def _splitter_moved(self, position: int, index: int) -> None:
|
||||
self._save_splitter_state()
|
||||
|
||||
@@ -65,7 +65,6 @@ class PackageList(QtWidgets.QWidget):
|
||||
self.ui.listPackages.setItemDelegate(self.item_delegate)
|
||||
|
||||
self.ui.listPackages.clicked.connect(self.on_listPackages_clicked)
|
||||
self.ui.view_bar.view_changed.connect(self.set_view_style)
|
||||
self.ui.view_bar.filter_changed.connect(self.update_status_filter)
|
||||
self.ui.view_bar.search_changed.connect(self.item_filter.setFilterRegularExpression)
|
||||
self.ui.view_bar.sort_changed.connect(self.item_filter.setSortRole)
|
||||
@@ -105,6 +104,20 @@ class PackageList(QtWidgets.QWidget):
|
||||
)
|
||||
self.item_filter.setHideUnlicensed(pref.GetBool("HideUnlicensed", False))
|
||||
|
||||
def select_addon(self, addon_name: str):
|
||||
for index, addon in enumerate(self.item_model.repos):
|
||||
if addon.name == addon_name:
|
||||
row_index = self.item_model.createIndex(index, 0)
|
||||
if self.item_filter.filterAcceptsRow(index):
|
||||
self.ui.listPackages.setCurrentIndex(row_index)
|
||||
else:
|
||||
FreeCAD.Console.PrintLog(
|
||||
f"Addon {addon_name} is not visible given current "
|
||||
"filter: not selecting it."
|
||||
)
|
||||
return
|
||||
FreeCAD.Console.PrintLog(f"Could not find addon '{addon_name}' to select it")
|
||||
|
||||
def on_listPackages_clicked(self, index: QtCore.QModelIndex):
|
||||
"""Determine what addon was selected and emit the itemSelected signal with it as
|
||||
an argument."""
|
||||
@@ -124,10 +137,10 @@ class PackageList(QtWidgets.QWidget):
|
||||
|
||||
def set_view_style(self, style: AddonManagerDisplayStyle) -> None:
|
||||
"""Set the style (compact or expanded) of the list"""
|
||||
self.item_model.layoutAboutToBeChanged.emit()
|
||||
if self.item_model:
|
||||
self.item_model.layoutAboutToBeChanged.emit()
|
||||
self.item_delegate.set_view(style)
|
||||
# TODO: Update to support composite
|
||||
if style == AddonManagerDisplayStyle.COMPACT:
|
||||
if style == AddonManagerDisplayStyle.COMPACT or style == AddonManagerDisplayStyle.COMPOSITE:
|
||||
self.ui.listPackages.setSpacing(2)
|
||||
self.ui.listPackages.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerItem)
|
||||
self.ui.listPackages.verticalScrollBar().setSingleStep(-1)
|
||||
@@ -135,7 +148,8 @@ class PackageList(QtWidgets.QWidget):
|
||||
self.ui.listPackages.setSpacing(5)
|
||||
self.ui.listPackages.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
|
||||
self.ui.listPackages.verticalScrollBar().setSingleStep(24)
|
||||
self.item_model.layoutChanged.emit()
|
||||
if self.item_model:
|
||||
self.item_model.layoutChanged.emit()
|
||||
|
||||
pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons")
|
||||
pref.SetInt("ViewStyle", style)
|
||||
@@ -288,6 +302,9 @@ class PackageListItemDelegate(QtWidgets.QStyledItemDelegate):
|
||||
elif self.displayStyle == AddonManagerDisplayStyle.COMPACT:
|
||||
self.widget = self.compact
|
||||
self._setup_compact_view(repo)
|
||||
elif self.displayStyle == AddonManagerDisplayStyle.COMPOSITE:
|
||||
self.widget = self.compact # For now re-use the compact list
|
||||
self._setup_composite_view(repo)
|
||||
self.widget.adjustSize()
|
||||
|
||||
def _setup_expanded_view(self, addon: Addon) -> None:
|
||||
@@ -334,6 +351,22 @@ class PackageListItemDelegate(QtWidgets.QStyledItemDelegate):
|
||||
else:
|
||||
self.widget.ui.labelDescription.setText(self._get_sort_label_text(addon))
|
||||
|
||||
def _setup_composite_view(self, addon: Addon) -> None:
|
||||
self.widget.ui.labelPackageName.setText(f"<b>{addon.display_name}</b>")
|
||||
self.widget.ui.labelIcon.setPixmap(addon.icon.pixmap(QtCore.QSize(16, 16)))
|
||||
self.widget.ui.labelStatus.setText(self.get_compact_update_string(addon))
|
||||
self.widget.ui.labelIcon.setText("")
|
||||
if addon.metadata:
|
||||
self.widget.ui.labelVersion.setText(f"<i>v{addon.metadata.version}</i>")
|
||||
elif addon.macro:
|
||||
self._set_macro_version_label(addon)
|
||||
else:
|
||||
self.widget.ui.labelVersion.setText("")
|
||||
if self.sort_order != SortOptions.Alphabetical:
|
||||
self.widget.ui.labelDescription.setText(self._get_sort_label_text(addon))
|
||||
else:
|
||||
self.widget.ui.labelDescription.setText("")
|
||||
|
||||
def _set_package_maintainer_label(self, addon: Addon):
|
||||
maintainers = addon.metadata.maintainer
|
||||
maintainers_string = ""
|
||||
@@ -395,14 +428,13 @@ class PackageListItemDelegate(QtWidgets.QStyledItemDelegate):
|
||||
return ""
|
||||
|
||||
def _get_compact_description(self, addon: Addon) -> str:
|
||||
description = ""
|
||||
if addon.metadata:
|
||||
trimmed_text = addon.metadata.description
|
||||
# TODO: Un-hardcode the 25 character limiter
|
||||
return trimmed_text.replace("\r\n", " ")[:25] + "..."
|
||||
if addon.macro and addon.macro.comment:
|
||||
trimmed_text = addon.macro.comment
|
||||
return trimmed_text.replace("\r\n", " ")[:25] + "..."
|
||||
return ""
|
||||
description = addon.metadata.description
|
||||
elif addon.macro and addon.macro.comment:
|
||||
description = addon.macro.comment
|
||||
trimmed_text, _, _ = description.partition(".")
|
||||
return trimmed_text.replace("\n", " ")
|
||||
|
||||
@staticmethod
|
||||
def get_compact_update_string(repo: Addon) -> str:
|
||||
|
||||
@@ -212,20 +212,20 @@ class AnnotationStyleEditor(gui_base.GuiCommandSimplest):
|
||||
elif index == 1:
|
||||
# Add new... entry
|
||||
reply = QtWidgets.QInputDialog.getText(None,
|
||||
"Create new style",
|
||||
"Style name:")
|
||||
translate("draft", "Create new style"),
|
||||
translate("draft", "Style name:"))
|
||||
if reply[1]:
|
||||
# OK or Enter pressed
|
||||
name = reply[0].strip()
|
||||
if name == "":
|
||||
QtWidgets.QMessageBox.information(None,
|
||||
"Style name required",
|
||||
"No style name specified")
|
||||
translate("draft", "Style name required"),
|
||||
translate("draft", "No style name specified"))
|
||||
self.form.comboBoxStyles.setCurrentIndex(0)
|
||||
elif name in self.styles:
|
||||
QtWidgets.QMessageBox.information(None,
|
||||
"Style exists",
|
||||
"This style name already exists")
|
||||
translate("draft", "Style exists"),
|
||||
translate("draft", "This style name already exists"))
|
||||
self.form.comboBoxStyles.setCurrentIndex(0)
|
||||
else:
|
||||
# create new style from current editor values
|
||||
@@ -253,10 +253,10 @@ class AnnotationStyleEditor(gui_base.GuiCommandSimplest):
|
||||
|
||||
if self.get_style_users(style):
|
||||
reply = QtWidgets.QMessageBox.question(None,
|
||||
"Style in use",
|
||||
"This style is used by some objects in this document. Are you sure?",
|
||||
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
|
||||
QtWidgets.QMessageBox.No)
|
||||
translate("draft", "Style in use"),
|
||||
translate("draft", "This style is used by some objects in this document. Are you sure?"),
|
||||
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
|
||||
QtWidgets.QMessageBox.No)
|
||||
if reply == QtWidgets.QMessageBox.No:
|
||||
return
|
||||
self.form.comboBoxStyles.removeItem(index)
|
||||
@@ -268,8 +268,8 @@ class AnnotationStyleEditor(gui_base.GuiCommandSimplest):
|
||||
style = self.form.comboBoxStyles.itemText(index)
|
||||
|
||||
reply = QtWidgets.QInputDialog.getText(None,
|
||||
"Rename style",
|
||||
"New name:",
|
||||
translate("draft", "Rename style"),
|
||||
translate("draft", "New name:"),
|
||||
QtWidgets.QLineEdit.Normal,
|
||||
style)
|
||||
if reply[1]:
|
||||
@@ -277,8 +277,8 @@ class AnnotationStyleEditor(gui_base.GuiCommandSimplest):
|
||||
newname = reply[0]
|
||||
if newname in self.styles:
|
||||
reply = QtWidgets.QMessageBox.information(None,
|
||||
"Style exists",
|
||||
"This style name already exists")
|
||||
translate("draft", "Style exists"),
|
||||
translate("draft", "This style name already exists"))
|
||||
else:
|
||||
self.form.comboBoxStyles.setItemText(index, newname)
|
||||
value = self.styles[style]
|
||||
|
||||
@@ -393,6 +393,7 @@ def _get_param_dictionary():
|
||||
|
||||
# Arch parameters that are not in the preferences:
|
||||
param_dict["Mod/Arch"] = {
|
||||
"applyConstructionStyle": ("bool", True),
|
||||
"ClaimHosted": ("bool", True),
|
||||
"CustomIfcSchema": ("string", ""), # importIFClegacy.py
|
||||
"createIfcGroups": ("bool", False), # importIFClegacy.py
|
||||
|
||||
@@ -71,7 +71,7 @@ void FaceMakerBullseye::Build_Essence()
|
||||
//validity check
|
||||
for (TopoDS_Wire& w : myWires) {
|
||||
if (!BRep_Tool::IsClosed(w))
|
||||
throw Base::ValueError("Wire is not closed.");
|
||||
throw Base::ValueError(QT_TRANSLATE_NOOP("Exception", "Wire is not closed."));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -206,7 +206,7 @@ class TaskPanel:
|
||||
|
||||
if cleanup:
|
||||
self.removeGlobalCallbacks()
|
||||
FreeCADGui.Snapper.off(True)
|
||||
FreeCADGui.Snapper.off()
|
||||
if self.buttonBox:
|
||||
self.buttonBox.setEnabled(True)
|
||||
self.removeEscapeShortcut()
|
||||
|
||||
@@ -272,6 +272,10 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
# Note that emitting preambles between moves breaks some dressups and prevents path optimization on some controllers
|
||||
pathParams["preamble"] = False
|
||||
|
||||
# disable path sorting for offset and zigzag-offset paths
|
||||
if hasattr(obj, "OffsetPattern") and obj.OffsetPattern in ["ZigZagOffset", "Offset"] and hasattr(obj, "MinTravel") and not obj.MinTravel:
|
||||
pathParams["sort_mode"] = 0
|
||||
|
||||
if not self.areaOpRetractTool(obj):
|
||||
pathParams["threshold"] = 2.001 * self.radius
|
||||
|
||||
|
||||
@@ -93,19 +93,10 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
|
||||
if not (FeatureRestMachining & self.pocketFeatures()):
|
||||
form.useRestMachining.hide()
|
||||
|
||||
# if True:
|
||||
# # currently doesn't have an effect or is experimental
|
||||
# form.minTravel.hide()
|
||||
|
||||
return form
|
||||
|
||||
def updateMinTravel(self, obj, setModel=True):
|
||||
if obj.UseStartPoint:
|
||||
self.form.minTravel.setEnabled(True)
|
||||
else:
|
||||
self.form.minTravel.setChecked(False)
|
||||
self.form.minTravel.setEnabled(False)
|
||||
|
||||
if setModel and obj.MinTravel != self.form.minTravel.isChecked():
|
||||
obj.MinTravel = self.form.minTravel.isChecked()
|
||||
|
||||
|
||||
@@ -624,11 +624,108 @@ void CArea::MakePocketToolpath(std::list<CCurve> &curve_list, const CAreaPocketP
|
||||
|
||||
if(params.mode == SingleOffsetPocketMode || params.mode == ZigZagThenSingleOffsetPocketMode)
|
||||
{
|
||||
// if there are already curves, attempt to start the offset from the current tool position
|
||||
bool done = false;
|
||||
if (!curve_list.empty() && !curve_list.back().m_vertices.empty()) {
|
||||
// find the closest curve to the start point
|
||||
const Point start = curve_list.back().m_vertices.back().m_p;
|
||||
auto curve_itmin = a_offset.m_curves.begin();
|
||||
double dmin = Point::tolerance;
|
||||
for (auto it = a_offset.m_curves.begin(); it != a_offset.m_curves.end(); it++) {
|
||||
const double dist = it->NearestPoint(start).dist(start);
|
||||
if (dist < dmin) {
|
||||
dmin = dist;
|
||||
curve_itmin = it;
|
||||
}
|
||||
}
|
||||
|
||||
// if the start point is on that curve (within Point::tolerance), do the profile starting on that curve
|
||||
if (dmin < Point::tolerance) {
|
||||
// split the curve into two parts -- starting with this point, and ending with this point
|
||||
CCurve startCurve;
|
||||
CCurve endCurve;
|
||||
|
||||
std::list<Span> spans;
|
||||
curve_itmin->GetSpans(spans);
|
||||
int imin = -1;
|
||||
double dmin = std::numeric_limits<double>::max();
|
||||
Point nmin;
|
||||
Span smin;
|
||||
{
|
||||
int i = 0;
|
||||
for (auto it = spans.begin(); it != spans.end(); i++, it++) {
|
||||
const Point nearest = it->NearestPoint(start);
|
||||
const double dist = nearest.dist(start);
|
||||
if (dist < dmin) {
|
||||
dmin = dist;
|
||||
imin = i;
|
||||
nmin = nearest;
|
||||
smin = *it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
startCurve.append(CVertex(nmin));
|
||||
endCurve.append(curve_itmin->m_vertices.front());
|
||||
{
|
||||
int i =0;
|
||||
for (auto it = spans.begin(); it != spans.end(); i++, it++) {
|
||||
if (i < imin) {
|
||||
endCurve.append(it->m_v);
|
||||
} else if (i > imin) {
|
||||
startCurve.append(it->m_v);
|
||||
} else {
|
||||
if (nmin != endCurve.m_vertices.back().m_p) {
|
||||
endCurve.append(CVertex(smin.m_v.m_type, nmin, smin.m_v.m_c, smin.m_v.m_user_data));
|
||||
}
|
||||
if (nmin != it->m_v.m_p) {
|
||||
startCurve.append(CVertex(smin.m_v.m_type, it->m_v.m_p, smin.m_v.m_c, smin.m_v.m_user_data));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// append curves to the curve list: start curve, other curves wrapping around, end curve
|
||||
const auto appendCurve = [&curve_list](const CCurve &curve) {
|
||||
if (curve_list.size() > 0 && curve_list.back().m_vertices.back().m_p == curve.m_vertices.front().m_p) {
|
||||
auto it = curve.m_vertices.begin();
|
||||
for (it++; it != curve.m_vertices.end(); it++) {
|
||||
curve_list.back().append(*it);
|
||||
}
|
||||
} else {
|
||||
curve_list.push_back(curve);
|
||||
}
|
||||
};
|
||||
|
||||
if (startCurve.m_vertices.size() > 1) {
|
||||
appendCurve(startCurve);
|
||||
}
|
||||
{
|
||||
auto it = curve_itmin;
|
||||
for(it++; it != a_offset.m_curves.end(); it++) {
|
||||
appendCurve(*it);
|
||||
}
|
||||
}
|
||||
for(auto it = a_offset.m_curves.begin(); it != curve_itmin; it++) {
|
||||
appendCurve(*it);
|
||||
}
|
||||
if (endCurve.m_vertices.size() > 1) {
|
||||
appendCurve(endCurve);
|
||||
}
|
||||
|
||||
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
|
||||
// add the single offset too
|
||||
for(std::list<CCurve>::iterator It = a_offset.m_curves.begin(); It != a_offset.m_curves.end(); It++)
|
||||
if (!done)
|
||||
{
|
||||
CCurve& curve = *It;
|
||||
curve_list.push_back(curve);
|
||||
for(std::list<CCurve>::iterator It = a_offset.m_curves.begin(); It != a_offset.m_curves.end(); It++)
|
||||
{
|
||||
CCurve& curve = *It;
|
||||
curve_list.push_back(curve);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user