Files
LorenEteval_Furious/Furious/Service/PluginUIManager.py
T
2026-09-09 15:58:31 +08:00

148 lines
4.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Copyright (C) 2024present Loren Eteval & contributors <loren.eteval@proton.me>
#
# This file is part of Furious.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
"""Bridge plugin UI capabilities into the host's page-based interface."""
from __future__ import annotations
from Furious.Frozenlib import AppConnectionController
from Furious.Plugins import (
CapabilityKind,
NavigationPageDescriptor,
getPluginRegistry,
)
from PySide6 import QtCore
from PySide6.QtWidgets import QWidget
from shiboken6 import isValid
import logging
__all__ = ['PluginNavigationManager', 'isCoreActive']
logger = logging.getLogger(__name__)
def isCoreActive(coreType) -> bool:
"""Return whether the active connection owns a core of ``coreType``."""
try:
controller = AppConnectionController()
return controller.isConnected() and any(
isinstance(runtime, coreType) for runtime in controller.runtimes
)
except (AttributeError, RuntimeError):
return False
class PluginNavigationManager:
"""Validate, construct, and retain plugin-contributed navigation pages."""
def __init__(self, registry=None):
"""Initialize with the process-wide registry unless one is supplied."""
self.registry = registry or getPluginRegistry()
self._pages = []
self._registrationAttempted = False
def registerPages(self, navigationView):
"""Register plugin pages in stable plugin/provider/order sequence."""
if self._registrationAttempted:
return tuple(self._pages)
self._registrationAttempted = True
contributions = []
for plugin in self.registry.plugins():
metadata = self.registry.metadataFor(plugin)
for provider in self.registry.capabilities(
CapabilityKind.NavigationPage,
plugin,
):
try:
descriptors = tuple(provider.pageDescriptors())
except Exception as ex:
# Any non-exit exceptions
logger.error(
f'plugin navigation provider '
f'{provider.capabilityId!r} failed: {ex}'
)
continue
for descriptor in descriptors:
if not isinstance(descriptor, NavigationPageDescriptor):
logger.error(
f'plugin {metadata.id!r} returned an invalid '
f'navigation-page descriptor'
)
continue
contributions.append((plugin, metadata, descriptor))
contributions.sort(key=lambda item: item[2].order)
for _plugin, metadata, descriptor in contributions:
pageId = f'plugin:{metadata.id}:{descriptor.id}'
try:
page = descriptor.factory(parent=navigationView)
except Exception as ex:
# Any non-exit exceptions
logger.error(f'failed to create plugin page {pageId!r}: {ex}')
continue
if not isinstance(page, QWidget) or not isValid(page):
logger.error(f'plugin page {pageId!r} did not create a valid QWidget')
if isinstance(page, QtCore.QObject) and isValid(page):
page.deleteLater()
continue
try:
navigationView.addPage(
pageId,
page,
descriptor.title,
descriptor.iconFileName,
translatable=descriptor.translatable,
)
except Exception as ex:
# Any non-exit exceptions
logger.error(f'failed to register plugin page {pageId!r}: {ex}')
if isValid(page):
page.deleteLater()
continue
self._pages.append((pageId, page))
return tuple(self._pages)
def pages(self):
"""Return successfully registered plugin pages."""
return tuple(self._pages)