Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions src/optiverse/core/raytracing_math.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@

NUMBA_AVAILABLE = True
except ImportError:
# Fallback: no-op decorator if numba isn't available
def jit(*args, **kwargs):
# Fallback: no-op decorator if numba isn't available. mypy flags the
# redefinition differently depending on whether numba is importable in the
# analysis environment (no-redef when it is, misc when it isn't), so both
# codes are suppressed for cross-platform CI. warn_unused_ignores is off,
# so the code that doesn't apply on a given platform is harmless.
def jit(*args, **kwargs): # type: ignore[no-redef,misc]
def decorator(func):
return func

Expand Down Expand Up @@ -561,7 +565,7 @@ def compute_dichroic_reflectance(

def refract_vector_snell(
v_in: np.ndarray, n_hat: np.ndarray, n1: float, n2: float
) -> tuple[np.ndarray | None, bool]:
) -> tuple[np.ndarray, bool]:
"""
Apply Snell's law to refract a ray at an interface.

Expand All @@ -572,9 +576,9 @@ def refract_vector_snell(
n2: Refractive index of transmitted medium

Returns:
Tuple of (refracted_direction, is_total_reflection)
- refracted_direction: Refracted ray direction (normalized),
or None if total internal reflection
Tuple of (direction, is_total_reflection)
- direction: Refracted ray direction (normalized), or the reflected
direction if total internal reflection occurs
- is_total_reflection: True if total internal reflection occurs

Physics:
Expand Down
9 changes: 7 additions & 2 deletions src/optiverse/objects/definitions_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,17 @@ def _iter_component_json_files(library_path: Path | None = None) -> list[Path]:
library_path: Optional custom library path. If None, uses built-in library.

Returns:
List of Path objects to component folders
List of Path objects to component folders, sorted by folder name for a
deterministic order across platforms (iterdir() order is filesystem
dependent and differs between Linux/Windows/macOS).
"""
root = library_path if library_path else _library_root()
if not root.exists():
return []
return [p for p in root.iterdir() if p.is_dir() and (p / "component.json").exists()]
folders = [
p for p in root.iterdir() if p.is_dir() and (p / "component.json").exists()
]
return sorted(folders, key=lambda p: p.name)


def load_component_records(
Expand Down
35 changes: 35 additions & 0 deletions src/optiverse/ui/theme_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,31 @@ def _create_light_palette() -> QtGui.QPalette:
return palette


def _set_color_scheme(dark_mode: bool) -> None:
"""
Pin Qt's color scheme so native styling matches the applied theme.

Uses ``QStyleHints.setColorScheme`` (Qt 6.8+). On older Qt versions the
setter is unavailable and this is a no-op - the QSS/palette still apply,
only the native-chrome override (e.g. Windows dark-mode menu bar) is
unavailable to correct.

Args:
dark_mode: True to request the dark color scheme, False for light.
"""
style_hints = QtGui.QGuiApplication.styleHints()
setter = getattr(style_hints, "setColorScheme", None)
if setter is None:
return
scheme = (
QtCore.Qt.ColorScheme.Dark if dark_mode else QtCore.Qt.ColorScheme.Light
)
try:
setter(scheme)
except (AttributeError, TypeError) as e:
_logger.debug("Could not set color scheme: %s", e)


def apply_theme(dark_mode: bool) -> None:
"""
Apply the appropriate theme (stylesheet + palette) based on dark mode setting.
Expand All @@ -225,6 +250,16 @@ def apply_theme(dark_mode: bool) -> None:
_logger.warning("No QApplication instance - cannot apply theme")
return

# Pin Qt's color scheme to match the app theme.
#
# On Windows 11, Qt's native style paints native chrome (most visibly the
# QMenuBar's top-level items) using the OS color scheme, ignoring the QSS
# `color` and the application palette. When the OS is in dark mode but the
# app is in light mode, this renders white menu-bar text on the light
# menu-bar background - i.e. invisible. Forcing the color scheme keeps the
# native rendering consistent with the theme we actually applied.
_set_color_scheme(dark_mode)

# Apply stylesheet and palette
if dark_mode:
app.setStyleSheet(get_dark_stylesheet())
Expand Down
Loading