app: project properties dialog (File > Project Properties…)

The menu item was a placeholder print; it now opens a real dialog (the
C++ ProjectPropertiesDialog):

- Per-project OCIO config override with a 浏览… picker: validated on OK
  (an invalid config keeps the dialog open with the error shown, like
  the C++ accept()), persisted in the project settings, applied to the
  display color pipeline on accept and on project open, and reverted to
  the app default when the project closes. oak-render gains
  set_up_default_config_from for the explicit-path load.
- Disk-cache location (default / alongside the project / custom path):
  persisted through the OVE serializer (cachesetting/customcachepath
  round-trip the settings map, clamped on load) and honored by the
  thumbnail writer — the first live consumer of Project::cache_path.
- PathField gains an enabled state (the custom path field follows the
  combo selection).

The C++ color tab's Default Input Color Space and Reference Space
combos are intentionally absent: the Rust render pipeline has no
consumer for them today (decode performs no input transfer conversion),
so showing them would be dead settings.

Tests: dialog opens, OK applies the cache location, an invalid OCIO
config keeps the dialog open with the error row. i18n keys for all
eight packs.
This commit is contained in:
2026-08-25 00:45:04 +08:00
parent f7b5996032
commit 476114cec0
15 changed files with 809 additions and 18 deletions
+12 -4
View File
@@ -412,10 +412,18 @@ pub fn default_config() -> Option<std::sync::Arc<SafeConfig>> {
/// Load the process-wide default config from $OCIO or the bundled config
/// (C++ `ColorManager::SetUpDefaultConfig`).
pub fn set_up_default_config() -> Result<()> {
let config = match std::env::var("OCIO") {
Ok(path) if !path.is_empty() => ocio_rs::Config::from_file(&path)
.map_err(|e| Error::Failed(format!("load $OCIO config: {e}")))?,
_ => ocio_rs::Config::create_from_builtin_config("default")
let env = std::env::var("OCIO").ok().filter(|p| !p.is_empty());
set_up_default_config_from(env.as_deref())
}
/// Load the process-wide default config from an explicit path (`None` =
/// the bundled default config). The project-properties OCIO override and
/// the `$OCIO` startup path both land here.
pub fn set_up_default_config_from(path: Option<&str>) -> Result<()> {
let config = match path {
Some(path) => ocio_rs::Config::from_file(path)
.map_err(|e| Error::Failed(format!("load OCIO config \"{path}\": {e}")))?,
None => ocio_rs::Config::create_from_builtin_config("default")
.or_else(|_| ocio_rs::Config::create_from_builtin_config("ocio-2.2-default"))
.map_err(|e| Error::Failed(format!("load bundled config: {e}")))?,
};