diff --git a/pxr/usd/usdUI/CMakeLists.txt b/pxr/usd/usdUI/CMakeLists.txt index 9d10ca1915..cbf5f45b85 100644 --- a/pxr/usd/usdUI/CMakeLists.txt +++ b/pxr/usd/usdUI/CMakeLists.txt @@ -9,6 +9,7 @@ pxr_library(usdUI vt sdf usd + usdGeom PUBLIC_HEADERS api.h @@ -42,3 +43,17 @@ pxr_register_test(testUsdUISceneGraphPrim PYTHON COMMAND "${CMAKE_INSTALL_PREFIX}/tests/testUsdUISceneGraphPrim" ) + +pxr_build_test(testUsdUILocalizationAPI + LIBRARIES + tf + usd + usdUI + CPPFILES + testenv/testUsdUILocalizationAPI.cpp +) + +pxr_register_test(testUsdUILocalizationAPI + COMMAND "${CMAKE_INSTALL_PREFIX}/tests/testUsdUILocalizationAPI" + EXPECTED_RETURN_CODE 0 +) \ No newline at end of file diff --git a/pxr/usd/usdUI/generatedSchema.classes.txt b/pxr/usd/usdUI/generatedSchema.classes.txt index 30c2aa5bc7..13e164c2fd 100644 --- a/pxr/usd/usdUI/generatedSchema.classes.txt +++ b/pxr/usd/usdUI/generatedSchema.classes.txt @@ -2,6 +2,7 @@ # Public Classes backdrop +localizationAPI nodeGraphNodeAPI sceneGraphPrimAPI tokens @@ -9,6 +10,7 @@ tokens # Python Module Files module.cpp wrapBackdrop.cpp +wrapLocalizationAPI.cpp wrapNodeGraphNodeAPI.cpp wrapSceneGraphPrimAPI.cpp wrapTokens.cpp diff --git a/pxr/usd/usdUI/generatedSchema.module.h b/pxr/usd/usdUI/generatedSchema.module.h index 4688d2cbb3..21a5d28868 100644 --- a/pxr/usd/usdUI/generatedSchema.module.h +++ b/pxr/usd/usdUI/generatedSchema.module.h @@ -9,4 +9,5 @@ TF_WRAP(UsdUINodeGraphNodeAPI); TF_WRAP(UsdUISceneGraphPrimAPI); TF_WRAP(UsdUIBackdrop); +TF_WRAP(UsdUILocalizationAPI); TF_WRAP(UsdUITokens); diff --git a/pxr/usd/usdUI/generatedSchema.usda b/pxr/usd/usdUI/generatedSchema.usda index 7c4001ba46..c497c971f4 100644 --- a/pxr/usd/usdUI/generatedSchema.usda +++ b/pxr/usd/usdUI/generatedSchema.usda @@ -130,3 +130,64 @@ class Backdrop "Backdrop" ( ) } +class "LocalizationAPI" ( + doc = """This API describes em Language localization information for attributes. + + It may be used to provide alternate language definitions for content like strings and asset paths that are + displayed to a user. + Runtimes may present the best language for a given users preference with this information. + + OpenUSD leaves it up to the runtime that is consuming it to handle localized presentations. + As such, support for localization may vary across runtimes. + + \\important Lookup of localized attributes may be expensive, so are recommended to be used sparingly + It is recommended, but not enforced, to only use them for strings and asset paths. + Support for localization of different attributes may vary depending on the application runtime that + the data is brought into. + + + \\note Language identifiers must use the BCP-47 list of languages. However, since USD cannot currently use + hyphens in identifiers, any hyphens are replaced with underscores. This is similar in strategy to other + systems that adhere closely to the Unicode Identifier specification. e.g en-ca is en_CA . + Take care when converting language identifiers to a systems own formatting. + + A default language is specifiable on a prim. This is the language that is assumed when attributes do + not include their own identifier for language. The default language is explicitly inherited by + all prims under the current prims hierarchy. + + \\note Provide default localization information on the default prim of the layer, and any top level prims. + It is not recommended to keep declarations of the default localization to a minimum throughout the rest of + the hierarchy within a single layer. + + Attributes are suffixed with :lang: when expressing languages other than the default. + + For example, \"string text\" would implicitly be in the default localization language, but you may have + \"string text:lang:fr\" for French. + + Runtimes may provide their own logic for choosing which langauge to display, but following BCP-47, + a recommended logic set is: + + - If a preferred language is available within the set of declared languages, pick that language exactly. + e.g \"en_CA\" should not pick simply \"en\" if \"en_CA\" is available + + - If a preferred language isn't available, check for a more specific version of that language. + e.g \"de_DE\" may match to \"de_DE_u_co_phonebk\" + + - If a more specific language is not available, pick a less specific language. + e.g \"en_US\" may match to \"en\" + + - If a less specific language choice is not available, pick the attribute without language specification. + """ +) +{ + uniform token localization:__INSTANCE_NAME__:language ( + doc = """The default language for this prim hierarchy. This may only be created with the default instance name. + + \\note If no default localization language is provided, the runtime may optionally try and infer the + language of the text. + If the runtime does not infer the langauge, it should assume the language is in the users preferred language, + which may be derived from the system or current user context. + """ + ) +} + diff --git a/pxr/usd/usdUI/localizationAPI.cpp b/pxr/usd/usdUI/localizationAPI.cpp new file mode 100644 index 0000000000..a553dd981b --- /dev/null +++ b/pxr/usd/usdUI/localizationAPI.cpp @@ -0,0 +1,512 @@ +// +// Copyright 2016 Pixar +// +// Licensed under the terms set forth in the LICENSE.txt file available at +// https://openusd.org/license. +// +#include "pxr/usd/usdUI/localizationAPI.h" +#include "pxr/usd/usd/schemaRegistry.h" +#include "pxr/usd/usd/typed.h" + +#include "pxr/usd/sdf/types.h" +#include "pxr/usd/sdf/assetPath.h" + +PXR_NAMESPACE_OPEN_SCOPE + +// Register the schema with the TfType system. +TF_REGISTRY_FUNCTION(TfType) +{ + TfType::Define >(); + +} + +/* virtual */ +UsdUILocalizationAPI::~UsdUILocalizationAPI() +{ +} + +/* static */ +UsdUILocalizationAPI +UsdUILocalizationAPI::Get(const UsdStagePtr &stage, const SdfPath &path) +{ + if (!stage) { + TF_CODING_ERROR("Invalid stage"); + return UsdUILocalizationAPI(); + } + TfToken name; + if (!IsLocalizationAPIPath(path, &name)) { + TF_CODING_ERROR("Invalid localization path <%s>.", path.GetText()); + return UsdUILocalizationAPI(); + } + return UsdUILocalizationAPI(stage->GetPrimAtPath(path.GetPrimPath()), name); +} + +UsdUILocalizationAPI +UsdUILocalizationAPI::Get(const UsdPrim &prim, const TfToken &name) +{ + return UsdUILocalizationAPI(prim, name); +} + +/* static */ +std::vector +UsdUILocalizationAPI::GetAll(const UsdPrim &prim) +{ + std::vector schemas; + + for (const auto &schemaName : + UsdAPISchemaBase::_GetMultipleApplyInstanceNames(prim, _GetStaticTfType())) { + schemas.emplace_back(prim, schemaName); + } + + return schemas; +} + + +/* static */ +bool +UsdUILocalizationAPI::IsSchemaPropertyBaseName(const TfToken &baseName) +{ + static TfTokenVector attrsAndRels = { + UsdSchemaRegistry::GetMultipleApplyNameTemplateBaseName( + UsdUITokens->localization_MultipleApplyTemplate_Language), + }; + + return find(attrsAndRels.begin(), attrsAndRels.end(), baseName) + != attrsAndRels.end(); +} + +/* static */ +bool +UsdUILocalizationAPI::IsLocalizationAPIPath( + const SdfPath &path, TfToken *name) +{ + if (!path.IsPropertyPath()) { + return false; + } + + std::string propertyName = path.GetName(); + TfTokenVector tokens = SdfPath::TokenizeIdentifierAsTokens(propertyName); + + // The baseName of the path can't be one of the + // schema properties. We should validate this in the creation (or apply) + // API. + TfToken baseName = *tokens.rbegin(); + if (IsSchemaPropertyBaseName(baseName)) { + return false; + } + + if (tokens.size() >= 2 + && tokens[0] == UsdUITokens->localization) { + *name = TfToken(propertyName.substr( + UsdUITokens->localization.GetString().size() + 1)); + return true; + } + + return false; +} + +/* virtual */ +UsdSchemaKind UsdUILocalizationAPI::_GetSchemaKind() const +{ + return UsdUILocalizationAPI::schemaKind; +} + +/* static */ +bool +UsdUILocalizationAPI::CanApply( + const UsdPrim &prim, const TfToken &name, std::string *whyNot) +{ + return prim.CanApplyAPI(name, whyNot); +} + +/* static */ +UsdUILocalizationAPI +UsdUILocalizationAPI::Apply(const UsdPrim &prim, const TfToken &name) +{ + if (prim.ApplyAPI(name)) { + return UsdUILocalizationAPI(prim, name); + } + return UsdUILocalizationAPI(); +} + +/* static */ +const TfType & +UsdUILocalizationAPI::_GetStaticTfType() +{ + static TfType tfType = TfType::Find(); + return tfType; +} + +/* static */ +bool +UsdUILocalizationAPI::_IsTypedSchema() +{ + static bool isTyped = _GetStaticTfType().IsA(); + return isTyped; +} + +/* virtual */ +const TfType & +UsdUILocalizationAPI::_GetTfType() const +{ + return _GetStaticTfType(); +} + +/// Returns the property name prefixed with the correct namespace prefix, which +/// is composed of the the API's propertyNamespacePrefix metadata and the +/// instance name of the API. +static inline +TfToken +_GetNamespacedPropertyName(const TfToken instanceName, const TfToken propName) +{ + return UsdSchemaRegistry::MakeMultipleApplyNameInstance(propName, instanceName); +} + +UsdAttribute +UsdUILocalizationAPI::GetLanguageAttr() const +{ + return GetPrim().GetAttribute( + _GetNamespacedPropertyName( + GetName(), + UsdUITokens->localization_MultipleApplyTemplate_Language)); +} + +UsdAttribute +UsdUILocalizationAPI::CreateLanguageAttr(VtValue const &defaultValue, bool writeSparsely) const +{ + return UsdSchemaBase::_CreateAttr( + _GetNamespacedPropertyName( + GetName(), + UsdUITokens->localization_MultipleApplyTemplate_Language), + SdfValueTypeNames->Token, + /* custom = */ false, + SdfVariabilityUniform, + defaultValue, + writeSparsely); +} + +namespace { +static inline TfTokenVector +_ConcatenateAttributeNames(const TfTokenVector& left,const TfTokenVector& right) +{ + TfTokenVector result; + result.reserve(left.size() + right.size()); + result.insert(result.end(), left.begin(), left.end()); + result.insert(result.end(), right.begin(), right.end()); + return result; +} +} + +/*static*/ +const TfTokenVector& +UsdUILocalizationAPI::GetSchemaAttributeNames(bool includeInherited) +{ + static TfTokenVector localNames = { + UsdUITokens->localization_MultipleApplyTemplate_Language, + }; + static TfTokenVector allNames = + _ConcatenateAttributeNames( + UsdAPISchemaBase::GetSchemaAttributeNames(true), + localNames); + + if (includeInherited) + return allNames; + else + return localNames; +} + +/*static*/ +TfTokenVector +UsdUILocalizationAPI::GetSchemaAttributeNames( + bool includeInherited, const TfToken &instanceName) +{ + const TfTokenVector &attrNames = GetSchemaAttributeNames(includeInherited); + if (instanceName.IsEmpty()) { + return attrNames; + } + TfTokenVector result; + result.reserve(attrNames.size()); + for (const TfToken &attrName : attrNames) { + result.push_back( + UsdSchemaRegistry::MakeMultipleApplyNameInstance(attrName, instanceName)); + } + return result; +} + +PXR_NAMESPACE_CLOSE_SCOPE + +// ===================================================================== // +// Feel free to add custom code below this line. It will be preserved by +// the code generator. +// +// Just remember to wrap code in the appropriate delimiters: +// 'PXR_NAMESPACE_OPEN_SCOPE', 'PXR_NAMESPACE_CLOSE_SCOPE'. +// ===================================================================== // +// --(BEGIN CUSTOM CODE)-- + +#include "pxr/usd/usdGeom/primvar.h" + +PXR_NAMESPACE_OPEN_SCOPE + +/*static*/ +UsdUILocalizationAPI UsdUILocalizationAPI::CreateDefaultAPI(const UsdPrim& prim) { + return UsdUILocalizationAPI(prim, UsdUITokens->default_); +} + +/*static*/ +UsdUILocalizationAPI UsdUILocalizationAPI::CreateDefaultAPI(const UsdSchemaBase& schemaObj) { + return UsdUILocalizationAPI(schemaObj, UsdUITokens->default_); +} + +/*static*/ +UsdUILocalizationAPI UsdUILocalizationAPI::ApplyDefaultAPI(const UsdPrim& prim) { + return UsdUILocalizationAPI::Apply(prim, UsdUITokens->default_); +} + +/* static */ +bool UsdUILocalizationAPI::CanLocalize(UsdAttribute const &attribute) { + // Primvars are not allowed to be localized + if (UsdGeomPrimvar::IsPrimvar(attribute)) { + return false; + } + // Currently, only string types are localizable + return (attribute.GetTypeName() == SdfValueTypeNames->String); +} + +/* static */ +bool UsdUILocalizationAPI::CanLocalize([[maybe_unused]] UsdRelationship const &relationship) { + // Currently no relationships are allowed to be localized , but we provide the API for symmetry + return false; +} + +/* static */ +UsdProperty +UsdUILocalizationAPI::GetDefaultProperty(UsdProperty const &source) { + const auto nameTokens = source.SplitName(); + + if (nameTokens.size() < 3 || nameTokens[nameTokens.size() - 2] != UsdUITokens->lang) { + // The most minimal name would be foo:lang:en_us + // If we have less than 3 tokens, we assume it doesn't have a language specifier. + // If the second last element isn't lang, we can assume the same as well + return source; + } + + const auto defaultName = TfStringJoin(nameTokens.begin(), nameTokens.end()-2, ":"); + + const auto prim = source.GetPrim(); + if (!prim) { + TF_CODING_ERROR("Cannot find parent prim"); + return {}; + } + + auto prop = prim.GetProperty(TfToken(defaultName)); + return prop; +} + +/* static */ +TfToken UsdUILocalizationAPI::GetPropertyLanguage(UsdProperty const &prop) { + auto nameTokens = prop.SplitName(); + if (nameTokens.size() < 3 || nameTokens[nameTokens.size() - 2] != UsdUITokens->lang) { + return TfToken(); + } + + return TfToken(nameTokens[nameTokens.size() - 1]); +} + +/* static */ +TfToken +UsdUILocalizationAPI::GetLocalizedPropertyName(UsdProperty const &source, TfToken const &localization) { + // It's fastest to just get the default attribute + const auto defaultProp = GetDefaultProperty(source); + if (!defaultProp) { + TF_CODING_ERROR("Cannot find the default-localized attribute for this property"); + return {}; + } + + const auto sep = ":"; + const auto localizedName = ( + defaultProp.GetName().GetString() + sep + + UsdUITokens->lang.GetString() + sep + + localization.GetString() + ); + + return TfToken(localizedName); +} + +/* static */ +UsdProperty +UsdUILocalizationAPI::GetLocalizedProperty(UsdProperty const &source, TfToken const &localization) { + const auto prim = source.GetPrim(); + if (!prim) { + TF_CODING_ERROR("Cannot find attributes parent prim"); + return {}; + } + + const auto localizedAttrName = GetLocalizedPropertyName(source, localization); + return prim.GetProperty(TfToken(localizedAttrName)); +} + +UsdProperty +UsdUILocalizationAPI::GetLocalizedProperty(UsdProperty const &source) const { + return GetLocalizedProperty(source, GetName()); +} + +/* static */ +UsdAttribute +UsdUILocalizationAPI::CreateLocalizedAttribute(UsdAttribute const &source, TfToken const &localization, + VtValue const &defaultValue, bool writeSparsely, bool validate) { + const auto prim = source.GetPrim(); + if (!prim) { + TF_CODING_ERROR("Cannot find attributes parent prim"); + return {}; + } + + if (writeSparsely) { + // From UsdSchemaBase::_CreateAttr + auto prop = GetLocalizedProperty(source, localization); + if (prop) { + UsdAttribute attr = prim.GetAttributeAtPath(prop.GetPath()); + if (!attr) { + TF_CODING_ERROR("Could not construct attribute from property"); + return {}; + } + + VtValue fallback; + if (defaultValue.IsEmpty() || + (!attr.HasAuthoredValue() + && attr.Get(&fallback) + && fallback == defaultValue)){ + return attr; + } + } + } + + auto property = UsdUILocalizationAPI::GetDefaultProperty(source); + if (!property) { + TF_CODING_ERROR("Could not find default property"); + return {}; + } + + UsdAttribute defaultAttr = prim.GetAttributeAtPath(property.GetPath()); + if (!defaultAttr) { + TF_CODING_ERROR("Could not construct attribute from property"); + return {}; + } + + if (validate && !UsdUILocalizationAPI::CanLocalize(defaultAttr)) { + TF_CODING_ERROR("This attribute type is not meant to be localized."); + return {}; + } + + UsdAttribute attr(prim.CreateAttribute( + UsdUILocalizationAPI::GetLocalizedPropertyName(source, localization), + defaultAttr.GetTypeName(), + defaultAttr.IsCustom(), + defaultAttr.GetVariability() + )); + + if (attr && !defaultValue.IsEmpty()) { + attr.Set(defaultValue); + } + + return attr; +} + +UsdAttribute +UsdUILocalizationAPI::CreateLocalizedAttribute(UsdAttribute const &source, VtValue const &defaultValue, + bool writeSparsely, bool validate) const { + return CreateLocalizedAttribute(source, GetName(), defaultValue, writeSparsely, validate); +} + +UsdRelationship +UsdUILocalizationAPI::CreateLocalizedRelationship(UsdRelationship const &source, TfToken const &localization, bool validate) { + const auto prim = source.GetPrim(); + if (!prim) { + TF_CODING_ERROR("Cannot find attributes parent prim"); + return {}; + } + + auto property = UsdUILocalizationAPI::GetDefaultProperty(source); + if (!property) { + TF_CODING_ERROR("Could not find default property"); + return {}; + } + + UsdRelationship defaultRel = prim.GetRelationshipAtPath(property.GetPath()); + if (!defaultRel) { + TF_CODING_ERROR("Could not construct relationship from property"); + return {}; + } + + if (validate && !UsdUILocalizationAPI::CanLocalize(defaultRel)) { + TF_CODING_ERROR("This relationship is not meant to be localized."); + return {}; + } + + UsdRelationship rel(prim.CreateRelationship( + UsdUILocalizationAPI::GetLocalizedPropertyName(source, localization), + defaultRel.IsCustom() + )); + + return rel; +} + + +UsdRelationship +UsdUILocalizationAPI::CreateLocalizedRelationship(UsdRelationship const &source, bool validate) const{ + return UsdUILocalizationAPI::CreateLocalizedRelationship(source, GetName(), validate); +} + +/* static */ +UsdProperty +UsdUILocalizationAPI::GetAppliedPropertyLocalizations(UsdProperty const &source, + std::map &localizations) { + auto prim = source.GetPrim(); + if (!prim) { + TF_CODING_ERROR("Cannot find parent prim"); + return {}; + } + auto apis = UsdUILocalizationAPI::GetAll(source.GetPrim()); + auto defaultAttr = UsdUILocalizationAPI::GetDefaultProperty(source); + auto defaultAttrName = defaultAttr.GetName().GetString(); + const auto sep = ":"; + for (const auto &api: apis) { + auto attrName = defaultAttrName + sep + UsdUITokens->lang.GetString() + sep + api.GetName().GetString(); + auto attr = prim.GetAttribute(TfToken(attrName)); + if (attr) { + localizations.insert({api.GetName(), attr}); + } + } + return defaultAttr; +} + +/* static */ +UsdProperty +UsdUILocalizationAPI::GetAllPropertyLocalizations(UsdProperty const &source, + std::map &localizations) { + auto prim = source.GetPrim(); + if (!prim) { + TF_CODING_ERROR("Cannot find parent prim"); + return {}; + } + auto defaultAttr = UsdUILocalizationAPI::GetDefaultProperty(source); + + const auto sep = ":"; + auto prefix = defaultAttr.GetName().GetString() + sep + UsdUITokens->lang.GetString() + sep; + for (const auto &props: prim.GetProperties()) { + auto attrName = props.GetName().GetString(); + if (!TfStringStartsWith(attrName, prefix)) { + continue; + } + + auto locale = UsdUILocalizationAPI::GetPropertyLanguage(props); + if (!locale.IsEmpty()) { + localizations.insert({locale, props}); + } + } + + return defaultAttr; +} + +PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/usd/usdUI/localizationAPI.h b/pxr/usd/usdUI/localizationAPI.h new file mode 100644 index 0000000000..7cfdfc8277 --- /dev/null +++ b/pxr/usd/usdUI/localizationAPI.h @@ -0,0 +1,401 @@ +// +// Copyright 2016 Pixar +// +// Licensed under the terms set forth in the LICENSE.txt file available at +// https://openusd.org/license. +// +#ifndef USDUI_GENERATED_LOCALIZATIONAPI_H +#define USDUI_GENERATED_LOCALIZATIONAPI_H + +/// \file usdUI/localizationAPI.h + +#include "pxr/pxr.h" +#include "pxr/usd/usdUI/api.h" +#include "pxr/usd/usd/apiSchemaBase.h" +#include "pxr/usd/usd/prim.h" +#include "pxr/usd/usd/stage.h" +#include "pxr/usd/usdUI/tokens.h" + +#include "pxr/base/vt/value.h" + +#include "pxr/base/gf/vec3d.h" +#include "pxr/base/gf/vec3f.h" +#include "pxr/base/gf/matrix4d.h" + +#include "pxr/base/tf/token.h" +#include "pxr/base/tf/type.h" + +PXR_NAMESPACE_OPEN_SCOPE + +class SdfAssetPath; + +// -------------------------------------------------------------------------- // +// LOCALIZATIONAPI // +// -------------------------------------------------------------------------- // + +/// \class UsdUILocalizationAPI +/// +/// This API describes em Language localization information for attributes. +/// +/// It may be used to provide alternate language definitions for content like strings and asset paths that are +/// displayed to a user. +/// Runtimes may present the best language for a given users preference with this information. +/// +/// OpenUSD leaves it up to the runtime that is consuming it to handle localized presentations. +/// As such, support for localization may vary across runtimes. +/// +/// \important Lookup of localized attributes may be expensive, so are recommended to be used sparingly +/// It is recommended, but not enforced, to only use them for strings and asset paths. +/// Support for localization of different attributes may vary depending on the application runtime that +/// the data is brought into. +/// +/// +/// \note Language identifiers must use the BCP-47 list of languages. However, since USD cannot currently use +/// hyphens in identifiers, any hyphens are replaced with underscores. This is similar in strategy to other +/// systems that adhere closely to the Unicode Identifier specification. e.g en-ca is en_CA . +/// Take care when converting language identifiers to a systems own formatting. +/// +/// A default language is specifiable on a prim. This is the language that is assumed when attributes do +/// not include their own identifier for language. The default language is explicitly inherited by +/// all prims under the current prims hierarchy. +/// +/// \note Provide default localization information on the default prim of the layer, and any top level prims. +/// It is not recommended to keep declarations of the default localization to a minimum throughout the rest of +/// the hierarchy within a single layer. +/// +/// Attributes are suffixed with \em :lang: when expressing languages other than the default. +/// +/// For example, "string text" would implicitly be in the default localization language, but you may have +/// "string text:lang:fr" for French. +/// +/// Runtimes may provide their own logic for choosing which langauge to display, but following BCP-47, +/// a recommended logic set is: +/// +/// \li If a preferred language is available within the set of declared languages, pick that language exactly. +/// e.g "en_CA" should not pick simply "en" if "en_CA" is available +/// +/// \li If a preferred language isn't available, check for a more specific version of that language. +/// e.g "de_DE" may match to "de_DE_u_co_phonebk" +/// +/// \li If a more specific language is not available, pick a less specific language. +/// e.g "en_US" may match to "en" +/// +/// \li If a less specific language choice is not available, pick the attribute without language specification. +/// +/// +/// For any described attribute \em Fallback \em Value or \em Allowed \em Values below +/// that are text/tokens, the actual token is published and defined in \ref UsdUITokens. +/// So to set an attribute to the value "rightHanded", use UsdUITokens->rightHanded +/// as the value. +/// +class UsdUILocalizationAPI : public UsdAPISchemaBase +{ +public: + /// Compile time constant representing what kind of schema this class is. + /// + /// \sa UsdSchemaKind + static const UsdSchemaKind schemaKind = UsdSchemaKind::MultipleApplyAPI; + + /// Construct a UsdUILocalizationAPI on UsdPrim \p prim with + /// name \p name . Equivalent to + /// UsdUILocalizationAPI::Get( + /// prim.GetStage(), + /// prim.GetPath().AppendProperty( + /// "localization:name")); + /// + /// for a \em valid \p prim, but will not immediately throw an error for + /// an invalid \p prim + explicit UsdUILocalizationAPI( + const UsdPrim& prim=UsdPrim(), const TfToken &name=TfToken()) + : UsdAPISchemaBase(prim, /*instanceName*/ name) + { } + + /// Construct a UsdUILocalizationAPI on the prim held by \p schemaObj with + /// name \p name. Should be preferred over + /// UsdUILocalizationAPI(schemaObj.GetPrim(), name), as it preserves + /// SchemaBase state. + explicit UsdUILocalizationAPI( + const UsdSchemaBase& schemaObj, const TfToken &name) + : UsdAPISchemaBase(schemaObj, /*instanceName*/ name) + { } + + /// Destructor. + USDUI_API + virtual ~UsdUILocalizationAPI(); + + /// Return a vector of names of all pre-declared attributes for this schema + /// class and all its ancestor classes. Does not include attributes that + /// may be authored by custom/extended methods of the schemas involved. + USDUI_API + static const TfTokenVector & + GetSchemaAttributeNames(bool includeInherited=true); + + /// Return a vector of names of all pre-declared attributes for this schema + /// class and all its ancestor classes for a given instance name. Does not + /// include attributes that may be authored by custom/extended methods of + /// the schemas involved. The names returned will have the proper namespace + /// prefix. + USDUI_API + static TfTokenVector + GetSchemaAttributeNames(bool includeInherited, const TfToken &instanceName); + + /// Returns the name of this multiple-apply schema instance + TfToken GetName() const { + return _GetInstanceName(); + } + + /// Return a UsdUILocalizationAPI holding the prim adhering to this + /// schema at \p path on \p stage. If no prim exists at \p path on + /// \p stage, or if the prim at that path does not adhere to this schema, + /// return an invalid schema object. \p path must be of the format + /// .localization:name . + /// + /// This is shorthand for the following: + /// + /// \code + /// TfToken name = SdfPath::StripNamespace(path.GetToken()); + /// UsdUILocalizationAPI( + /// stage->GetPrimAtPath(path.GetPrimPath()), name); + /// \endcode + /// + USDUI_API + static UsdUILocalizationAPI + Get(const UsdStagePtr &stage, const SdfPath &path); + + /// Return a UsdUILocalizationAPI with name \p name holding the + /// prim \p prim. Shorthand for UsdUILocalizationAPI(prim, name); + USDUI_API + static UsdUILocalizationAPI + Get(const UsdPrim &prim, const TfToken &name); + + /// Return a vector of all named instances of UsdUILocalizationAPI on the + /// given \p prim. + USDUI_API + static std::vector + GetAll(const UsdPrim &prim); + + /// Checks if the given name \p baseName is the base name of a property + /// of LocalizationAPI. + USDUI_API + static bool + IsSchemaPropertyBaseName(const TfToken &baseName); + + /// Checks if the given path \p path is of an API schema of type + /// LocalizationAPI. If so, it stores the instance name of + /// the schema in \p name and returns true. Otherwise, it returns false. + USDUI_API + static bool + IsLocalizationAPIPath(const SdfPath &path, TfToken *name); + + /// Returns true if this multiple-apply API schema can be applied, + /// with the given instance name, \p name, to the given \p prim. If this + /// schema can not be a applied the prim, this returns false and, if + /// provided, populates \p whyNot with the reason it can not be applied. + /// + /// Note that if CanApply returns false, that does not necessarily imply + /// that calling Apply will fail. Callers are expected to call CanApply + /// before calling Apply if they want to ensure that it is valid to + /// apply a schema. + /// + /// \sa UsdPrim::GetAppliedSchemas() + /// \sa UsdPrim::HasAPI() + /// \sa UsdPrim::CanApplyAPI() + /// \sa UsdPrim::ApplyAPI() + /// \sa UsdPrim::RemoveAPI() + /// + USDUI_API + static bool + CanApply(const UsdPrim &prim, const TfToken &name, + std::string *whyNot=nullptr); + + /// Applies this multiple-apply API schema to the given \p prim + /// along with the given instance name, \p name. + /// + /// This information is stored by adding "LocalizationAPI:name" + /// to the token-valued, listOp metadata \em apiSchemas on the prim. + /// For example, if \p name is 'instance1', the token + /// 'LocalizationAPI:instance1' is added to 'apiSchemas'. + /// + /// \return A valid UsdUILocalizationAPI object is returned upon success. + /// An invalid (or empty) UsdUILocalizationAPI object is returned upon + /// failure. See \ref UsdPrim::ApplyAPI() for + /// conditions resulting in failure. + /// + /// \sa UsdPrim::GetAppliedSchemas() + /// \sa UsdPrim::HasAPI() + /// \sa UsdPrim::CanApplyAPI() + /// \sa UsdPrim::ApplyAPI() + /// \sa UsdPrim::RemoveAPI() + /// + USDUI_API + static UsdUILocalizationAPI + Apply(const UsdPrim &prim, const TfToken &name); + +protected: + /// Returns the kind of schema this class belongs to. + /// + /// \sa UsdSchemaKind + USDUI_API + UsdSchemaKind _GetSchemaKind() const override; + +private: + // needs to invoke _GetStaticTfType. + friend class UsdSchemaRegistry; + USDUI_API + static const TfType &_GetStaticTfType(); + + static bool _IsTypedSchema(); + + // override SchemaBase virtuals. + USDUI_API + const TfType &_GetTfType() const override; + +public: + // --------------------------------------------------------------------- // + // LANGUAGE + // --------------------------------------------------------------------- // + /// The default language for this prim hierarchy. This may only be created with the default instance name. + /// + /// \note If no default localization language is provided, the runtime may optionally try and infer the + /// language of the text. + /// If the runtime does not infer the langauge, it should assume the language is in the users preferred language, + /// which may be derived from the system or current user context. + /// + /// + /// | || + /// | -- | -- | + /// | Declaration | `uniform token language` | + /// | C++ Type | TfToken | + /// | \ref Usd_Datatypes "Usd Type" | SdfValueTypeNames->Token | + /// | \ref SdfVariability "Variability" | SdfVariabilityUniform | + USDUI_API + UsdAttribute GetLanguageAttr() const; + + /// See GetLanguageAttr(), and also + /// \ref Usd_Create_Or_Get_Property for when to use Get vs Create. + /// If specified, author \p defaultValue as the attribute's default, + /// sparsely (when it makes sense to do so) if \p writeSparsely is \c true - + /// the default for \p writeSparsely is \c false. + USDUI_API + UsdAttribute CreateLanguageAttr(VtValue const &defaultValue = VtValue(), bool writeSparsely=false) const; + +public: + // ===================================================================== // + // Feel free to add custom code below this line, it will be preserved by + // the code generator. + // + // Just remember to: + // - Close the class declaration with }; + // - Close the namespace with PXR_NAMESPACE_CLOSE_SCOPE + // - Close the include guard with #endif + // ===================================================================== // + // --(BEGIN CUSTOM CODE)-- + + /// Creates an instance of the API with the default instance name. + /// /sa UsdUILocalizationAPI + static UsdUILocalizationAPI CreateDefaultAPI(const UsdPrim& prim); + + /// Creates an instance of the API with a schema object using the default instance name. + /// /sa UsdUILocalizationAPI + static UsdUILocalizationAPI CreateDefaultAPI(const UsdSchemaBase& schemaObj); + + /// Applies an instance of the API with the default instance name. + /// /sa Apply + static UsdUILocalizationAPI ApplyDefaultAPI(const UsdPrim& prim); + + /// Returns a boolean for whether an attribute can be localized or not. + /// The rules that govern this may be subject to change in the future. + USDUI_API + static bool CanLocalize(UsdAttribute const &attribute); + + /// Returns a boolean for whether a relationship can be localized or not. + /// The rules that govern this may be subject to change in the future. + USDUI_API + static bool CanLocalize(UsdRelationship const &relationship); + + /// Returns the version of the property that has no localization specifics. + /// If this cannot be found, a default constructed property is returned. + /// If the input property doesn't specify a localization, it will be returned itself. + USDUI_API + static UsdProperty GetDefaultProperty(UsdProperty const &source); + + /// Returns a TfToken that represents the localization of the property + /// If a localization is not found, an empty token is returned + USDUI_API + static TfToken GetPropertyLanguage(UsdProperty const &prop); + + /// Gets the name of a sibling property with a matching localization + USDUI_API + static TfToken + GetLocalizedPropertyName(UsdProperty const &source, TfToken const &localization); + + /// Finds and returns a sibling proprety that has the specified localization. + /// If it is not found, a default constructed property is returned. + /// Only localizations that have been applied on the prim will be returned. + USDUI_API + static UsdProperty + GetLocalizedProperty(UsdProperty const &source, TfToken const &localization); + + /// A convenience method for calling the static version of this method with the localization derived + /// from the instance name + USDUI_API + UsdProperty GetLocalizedProperty(UsdProperty const &source) const; + + /// Creates an attribute with the given localization, or returns the attribute if one already exists + /// \p validate will check if localization can be applied to this attribute. Exercise caution when skipping + /// validation. \sa CanLocalize + /// \note It is up to the developer to apply the localization API for this locale to the prim + USDUI_API + static UsdAttribute + CreateLocalizedAttribute(UsdAttribute const &source, TfToken const &localization, VtValue const &defaultValue, + bool writeSparsely=false, bool validate=true); + + /// A convenience method for calling the static version of this method with the localization derived + /// from the instance name + /// \p validate will check if localization can be applied to this attribute. Exercise caution when skipping + /// validation. \sa CanLocalize + USDUI_API + UsdAttribute CreateLocalizedAttribute(UsdAttribute const &source, VtValue const &defaultValue, + bool writeSparsely=false, bool validate=true) const; + + /// Creates a relationship with the given localization, or returns the relationship if one already exists + /// \p validate will check if localization can be applied to this attribute. Exercise caution when skipping + /// validation. \sa CanLocalize + /// \note It is up to the developer to apply the localization API for this locale to the prim + USDUI_API + static UsdRelationship + CreateLocalizedRelationship(UsdRelationship const &source, TfToken const &localization, bool validate=true); + + /// A convenience method for calling the static version of this method with the localization derived + /// \p validate will check if localization can be applied to this attribute. Exercise caution when skipping + /// validation. \sa CanLocalize + /// from the instance name + USDUI_API + UsdRelationship CreateLocalizedRelationship(UsdRelationship const &source, bool validate=true) const; + + + /// Fills the map with all localized versions of the properties that have Applied schemas on the prim. + /// The version of the property without a localization specifier will be returned directly from the method + /// and not populated into the map. + /// It is upto the developer to infer the localization using the rules as described in the schema + /// In Python, the results are returned as a tuple of the property return and the dictionary. + /// /sa GetAllPropertyLocalizations + USDUI_API + static UsdProperty + GetAppliedPropertyLocalizations(UsdProperty const &source, std::map &localizations); + + /// Fills the map with all localized versions of the property, regardless of whether they are applied. + /// The version of the property without a localization specifier will be returned directly from the method + /// and not populated into the map. + /// It is upto the developer to infer the localization using the rules as described in the schema + /// In Python, the results are returned as a tuple of the property return and the dictionary. + /// /sa GetAppliedPropertyLocalizations + USDUI_API + static UsdProperty + GetAllPropertyLocalizations(UsdProperty const &source, std::map &localizations); + +}; + +PXR_NAMESPACE_CLOSE_SCOPE + +#endif diff --git a/pxr/usd/usdUI/plugInfo.json b/pxr/usd/usdUI/plugInfo.json index 0b7960052e..b94fc9a6ff 100644 --- a/pxr/usd/usdUI/plugInfo.json +++ b/pxr/usd/usdUI/plugInfo.json @@ -17,6 +17,17 @@ "schemaIdentifier": "Backdrop", "schemaKind": "concreteTyped" }, + "UsdUILocalizationAPI": { + "alias": { + "UsdSchemaBase": "LocalizationAPI" + }, + "autoGenerated": true, + "bases": [ + "UsdAPISchemaBase" + ], + "schemaIdentifier": "LocalizationAPI", + "schemaKind": "multipleApplyAPI" + }, "UsdUINodeGraphNodeAPI": { "alias": { "UsdSchemaBase": "NodeGraphNodeAPI" diff --git a/pxr/usd/usdUI/schema.usda b/pxr/usd/usdUI/schema.usda index c8b9571883..e90ecd5e65 100644 --- a/pxr/usd/usdUI/schema.usda +++ b/pxr/usd/usdUI/schema.usda @@ -10,7 +10,12 @@ over "GLOBAL" ( customData = { string libraryName = "usdUI" string libraryPath = "pxr/usd/usdUI" - # dictionary libraryTokens = {} + dictionary libraryTokens = { + dictionary default_ = { + string value = "default" + string doc = """Name of the anonymous instance name for the schema""" + } + } } ) { @@ -183,3 +188,75 @@ class Backdrop "Backdrop" ( ) } + +class "LocalizationAPI" ( + inherits = + doc = """This API describes \em Language localization information for attributes. + + It may be used to provide alternate language definitions for content like strings and asset paths that are + displayed to a user. + Runtimes may present the best language for a given users preference with this information. + + OpenUSD leaves it up to the runtime that is consuming it to handle localized presentations. + As such, support for localization may vary across runtimes. + + \\important Lookup of localized attributes may be expensive, so are recommended to be used sparingly + It is recommended, but not enforced, to only use them for strings and asset paths. + Support for localization of different attributes may vary depending on the application runtime that + the data is brought into. + + + \\note Language identifiers must use the BCP-47 list of languages. However, since USD cannot currently use + hyphens in identifiers, any hyphens are replaced with underscores. This is similar in strategy to other + systems that adhere closely to the Unicode Identifier specification. e.g en-ca is en_CA . + Take care when converting language identifiers to a systems own formatting. + + A default language is specifiable on a prim. This is the language that is assumed when attributes do + not include their own identifier for language. The default language is explicitly inherited by + all prims under the current prims hierarchy. + + \\note Provide default localization information on the default prim of the layer, and any top level prims. + It is not recommended to keep declarations of the default localization to a minimum throughout the rest of + the hierarchy within a single layer. + + Attributes are suffixed with \\em :lang: when expressing languages other than the default. + + For example, "string text" would implicitly be in the default localization language, but you may have + "string text:lang:fr" for French. + + Runtimes may provide their own logic for choosing which langauge to display, but following BCP-47, + a recommended logic set is: + + \\li If a preferred language is available within the set of declared languages, pick that language exactly. + e.g "en_CA" should not pick simply "en" if "en_CA" is available + + \\li If a preferred language isn't available, check for a more specific version of that language. + e.g "de_DE" may match to "de_DE_u_co_phonebk" + + \\li If a more specific language is not available, pick a less specific language. + e.g "en_US" may match to "en" + + \\li If a less specific language choice is not available, pick the attribute without language specification. + """ + + customData = { + token apiSchemaType = "multipleApply" + token propertyNamespacePrefix = "localization" + dictionary schemaTokens = { + dictionary lang = { + string value = "lang" + string doc = """The token that delineates language specifications on an attribute""" + } + } + } +) { + uniform token language ( + doc = """The default language for this prim hierarchy. This may only be created with the default instance name. + + \\note If no default localization language is provided, the runtime may optionally try and infer the + language of the text. + If the runtime does not infer the langauge, it should assume the language is in the users preferred language, + which may be derived from the system or current user context. + """ + ) +} \ No newline at end of file diff --git a/pxr/usd/usdUI/testenv/testUsdUILocalizationAPI.cpp b/pxr/usd/usdUI/testenv/testUsdUILocalizationAPI.cpp new file mode 100644 index 0000000000..b25343e3b7 --- /dev/null +++ b/pxr/usd/usdUI/testenv/testUsdUILocalizationAPI.cpp @@ -0,0 +1,73 @@ +// +// Copyright 2016 Pixar +// +// Licensed under the terms set forth in the LICENSE.txt file available at +// https://openusd.org/license. +// + +#include "pxr/pxr.h" +#include "pxr/usd/usd/stage.h" +#include "pxr/usd/usd/prim.h" +#include "pxr/usd/usdUI/localizationAPI.h" +#include "pxr/usd/usdUI/tokens.h" +#include "pxr/base/tf/diagnostic.h" +#include +PXR_NAMESPACE_USING_DIRECTIVE + +int +main(int argc, char* argv[]) +{ + auto en_us = TfToken("en_US"); + auto fr_ca = TfToken("fr_CA"); + auto hi_in = TfToken("hi_IN"); + + auto stage = UsdStage::CreateInMemory(); + auto prim = stage->DefinePrim(SdfPath("/Root")); + + auto api = UsdUILocalizationAPI::ApplyDefaultAPI(prim); + auto frAPI = UsdUILocalizationAPI::Apply(prim, fr_ca); + + auto language = api.CreateLanguageAttr(VtValue(en_us)); + TF_AXIOM(language.GetName() == TfToken("localization:default:language")); + + + // Test Default localization + auto baseName = TfToken("foo"); + auto baseAttr = prim.CreateAttribute(baseName, SdfValueTypeNames->String); + baseAttr.Set(VtValue("Hello")); + TF_AXIOM(baseAttr.GetPath() == UsdUILocalizationAPI::GetDefaultProperty(baseAttr).GetPath()); + + auto frAttrName = TfToken("foo:lang:fr_CA"); + auto frAttr = frAPI.CreateLocalizedAttribute(baseAttr, VtValue("Bonjour")); + TF_AXIOM(frAttr.GetName() == frAttrName); + TF_AXIOM(UsdUILocalizationAPI::GetDefaultProperty(frAttr).GetPath() == baseAttr.GetPath()); + TF_AXIOM(UsdUILocalizationAPI::GetPropertyLanguage(frAttr) == fr_ca); + TF_AXIOM(UsdUILocalizationAPI::GetLocalizedProperty(baseAttr, fr_ca).GetPath() == frAttr.GetPath()); + TF_AXIOM(frAttr.IsCustom() == baseAttr.IsCustom()); + TF_AXIOM(frAttr.GetTypeName() == baseAttr.GetTypeName()); + + auto hiAttrName = TfToken("foo:lang:hi_IN"); + auto hiAttr = UsdUILocalizationAPI::CreateLocalizedAttribute(baseAttr, hi_in, VtValue("नमस्ते")); + TF_AXIOM(hiAttr.GetName() == hiAttrName); + TF_AXIOM(UsdUILocalizationAPI::GetLocalizedProperty(baseAttr, hi_in).GetPath() == hiAttr.GetPath()); + + std::map properties; + auto defaultAttr = UsdUILocalizationAPI::GetAppliedPropertyLocalizations(baseAttr, properties); + TF_AXIOM(properties.size() == 1); + TF_AXIOM(properties.count(fr_ca) == 1); + TF_AXIOM(defaultAttr.GetPath() == baseAttr.GetPath()); + + properties.clear(); + defaultAttr = UsdUILocalizationAPI::GetAllPropertyLocalizations(baseAttr, properties); + TF_AXIOM(properties.size() == 2); + TF_AXIOM(properties.count(fr_ca) == 1); + TF_AXIOM(properties.count(hi_in) == 1); + TF_AXIOM(defaultAttr.GetPath() == baseAttr.GetPath()); + + auto baseRelName = TfToken("spam"); + auto baseRel = prim.CreateRelationship(baseRelName, false); + auto frRel = frAPI.CreateLocalizedRelationship(baseRel, false); + TF_AXIOM(UsdUILocalizationAPI::GetLocalizedProperty(baseRel, fr_ca).GetPath() == frRel.GetPath()); + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/pxr/usd/usdUI/tokens.cpp b/pxr/usd/usdUI/tokens.cpp index f55805a669..014fbb9f7a 100644 --- a/pxr/usd/usdUI/tokens.cpp +++ b/pxr/usd/usdUI/tokens.cpp @@ -10,6 +10,10 @@ PXR_NAMESPACE_OPEN_SCOPE UsdUITokensType::UsdUITokensType() : closed("closed", TfToken::Immortal), + default_("default", TfToken::Immortal), + lang("lang", TfToken::Immortal), + localization("localization", TfToken::Immortal), + localization_MultipleApplyTemplate_Language("localization:__INSTANCE_NAME__:language", TfToken::Immortal), minimized("minimized", TfToken::Immortal), open("open", TfToken::Immortal), uiDescription("ui:description", TfToken::Immortal), @@ -23,10 +27,15 @@ UsdUITokensType::UsdUITokensType() : uiNodegraphNodeSize("ui:nodegraph:node:size", TfToken::Immortal), uiNodegraphNodeStackingOrder("ui:nodegraph:node:stackingOrder", TfToken::Immortal), Backdrop("Backdrop", TfToken::Immortal), + LocalizationAPI("LocalizationAPI", TfToken::Immortal), NodeGraphNodeAPI("NodeGraphNodeAPI", TfToken::Immortal), SceneGraphPrimAPI("SceneGraphPrimAPI", TfToken::Immortal), allTokens({ closed, + default_, + lang, + localization, + localization_MultipleApplyTemplate_Language, minimized, open, uiDescription, @@ -40,6 +49,7 @@ UsdUITokensType::UsdUITokensType() : uiNodegraphNodeSize, uiNodegraphNodeStackingOrder, Backdrop, + LocalizationAPI, NodeGraphNodeAPI, SceneGraphPrimAPI }) diff --git a/pxr/usd/usdUI/tokens.h b/pxr/usd/usdUI/tokens.h index ec080e33dc..93714247d5 100644 --- a/pxr/usd/usdUI/tokens.h +++ b/pxr/usd/usdUI/tokens.h @@ -49,6 +49,22 @@ struct UsdUITokensType { /// /// Possible value for UsdUINodeGraphNodeAPI::GetExpansionStateAttr() const TfToken closed; + /// \brief "default" + /// + /// Name of the anonymous instance name for the schema + const TfToken default_; + /// \brief "lang" + /// + /// The token that delineates language specifications on an attribute + const TfToken lang; + /// \brief "localization" + /// + /// Property namespace prefix for the UsdUILocalizationAPI schema. + const TfToken localization; + /// \brief "localization:__INSTANCE_NAME__:language" + /// + /// UsdUILocalizationAPI + const TfToken localization_MultipleApplyTemplate_Language; /// \brief "minimized" /// /// Possible value for UsdUINodeGraphNodeAPI::GetExpansionStateAttr() @@ -101,6 +117,10 @@ struct UsdUITokensType { /// /// Schema identifer and family for UsdUIBackdrop const TfToken Backdrop; + /// \brief "LocalizationAPI" + /// + /// Schema identifer and family for UsdUILocalizationAPI + const TfToken LocalizationAPI; /// \brief "NodeGraphNodeAPI" /// /// Schema identifer and family for UsdUINodeGraphNodeAPI diff --git a/pxr/usd/usdUI/wrapLocalizationAPI.cpp b/pxr/usd/usdUI/wrapLocalizationAPI.cpp new file mode 100644 index 0000000000..f8f01bc846 --- /dev/null +++ b/pxr/usd/usdUI/wrapLocalizationAPI.cpp @@ -0,0 +1,186 @@ +// +// Copyright 2016 Pixar +// +// Licensed under the terms set forth in the LICENSE.txt file available at +// https://openusd.org/license. +// +#include "pxr/usd/usdUI/localizationAPI.h" +#include "pxr/usd/usd/schemaBase.h" + +#include "pxr/usd/sdf/primSpec.h" + +#include "pxr/usd/usd/pyConversions.h" +#include "pxr/base/tf/pyAnnotatedBoolResult.h" +#include "pxr/base/tf/pyContainerConversions.h" +#include "pxr/base/tf/pyResultConversions.h" +#include "pxr/base/tf/pyUtils.h" +#include "pxr/base/tf/wrapTypeHelpers.h" + +#include "pxr/external/boost/python.hpp" + +#include + +PXR_NAMESPACE_USING_DIRECTIVE + +using namespace pxr_boost::python; + +namespace { + +#define WRAP_CUSTOM \ + template static void _CustomWrapCode(Cls &_class) + +// fwd decl. +WRAP_CUSTOM; + + +static UsdAttribute +_CreateLanguageAttr(UsdUILocalizationAPI &self, + object defaultVal, bool writeSparsely) { + return self.CreateLanguageAttr( + UsdPythonToSdfType(defaultVal, SdfValueTypeNames->Token), writeSparsely); +} + +static bool _WrapIsLocalizationAPIPath(const SdfPath &path) { + TfToken collectionName; + return UsdUILocalizationAPI::IsLocalizationAPIPath( + path, &collectionName); +} + +static std::string +_Repr(const UsdUILocalizationAPI &self) +{ + std::string primRepr = TfPyRepr(self.GetPrim()); + std::string instanceName = TfPyRepr(self.GetName()); + return TfStringPrintf( + "UsdUI.LocalizationAPI(%s, '%s')", + primRepr.c_str(), instanceName.c_str()); +} + +struct UsdUILocalizationAPI_CanApplyResult : + public TfPyAnnotatedBoolResult +{ + UsdUILocalizationAPI_CanApplyResult(bool val, std::string const &msg) : + TfPyAnnotatedBoolResult(val, msg) {} +}; + +static UsdUILocalizationAPI_CanApplyResult +_WrapCanApply(const UsdPrim& prim, const TfToken& name) +{ + std::string whyNot; + bool result = UsdUILocalizationAPI::CanApply(prim, name, &whyNot); + return UsdUILocalizationAPI_CanApplyResult(result, whyNot); +} + +} // anonymous namespace + +void wrapUsdUILocalizationAPI() +{ + typedef UsdUILocalizationAPI This; + + UsdUILocalizationAPI_CanApplyResult::Wrap( + "_CanApplyResult", "whyNot"); + + class_ > + cls("LocalizationAPI"); + + cls + .def(init((arg("prim"), arg("name")))) + .def(init((arg("schemaObj"), arg("name")))) + .def(TfTypePythonClass()) + + .def("Get", + (UsdUILocalizationAPI(*)(const UsdStagePtr &stage, + const SdfPath &path)) + &This::Get, + (arg("stage"), arg("path"))) + .def("Get", + (UsdUILocalizationAPI(*)(const UsdPrim &prim, + const TfToken &name)) + &This::Get, + (arg("prim"), arg("name"))) + .staticmethod("Get") + + .def("GetAll", + (std::vector(*)(const UsdPrim &prim)) + &This::GetAll, + arg("prim"), + return_value_policy()) + .staticmethod("GetAll") + + .def("CanApply", &_WrapCanApply, (arg("prim"), arg("name"))) + .staticmethod("CanApply") + + .def("Apply", &This::Apply, (arg("prim"), arg("name"))) + .staticmethod("Apply") + + .def("GetSchemaAttributeNames", + (const TfTokenVector &(*)(bool))&This::GetSchemaAttributeNames, + arg("includeInherited")=true, + return_value_policy()) + .def("GetSchemaAttributeNames", + (TfTokenVector(*)(bool, const TfToken &)) + &This::GetSchemaAttributeNames, + arg("includeInherited"), + arg("instanceName"), + return_value_policy()) + .staticmethod("GetSchemaAttributeNames") + + .def("_GetStaticTfType", (TfType const &(*)()) TfType::Find, + return_value_policy()) + .staticmethod("_GetStaticTfType") + + .def(!self) + + + .def("GetLanguageAttr", + &This::GetLanguageAttr) + .def("CreateLanguageAttr", + &_CreateLanguageAttr, + (arg("defaultValue")=object(), + arg("writeSparsely")=false)) + + .def("IsLocalizationAPIPath", _WrapIsLocalizationAPIPath) + .staticmethod("IsLocalizationAPIPath") + .def("__repr__", ::_Repr) + ; + + _CustomWrapCode(cls); +} + +// ===================================================================== // +// Feel free to add custom code below this line, it will be preserved by +// the code generator. The entry point for your custom code should look +// minimally like the following: +// +// WRAP_CUSTOM { +// _class +// .def("MyCustomMethod", ...) +// ; +// } +// +// Of course any other ancillary or support code may be provided. +// +// Just remember to wrap code in the appropriate delimiters: +// 'namespace {', '}'. +// +// ===================================================================== // +// --(BEGIN CUSTOM CODE)-- + +namespace { + +WRAP_CUSTOM { + _class + .def("GetDefaultProperty", &UsdUILocalizationAPI::GetDefaultProperty, (arg("source"))) + .staticmethod("GetDefaultProperty") + + .def("GetPropertyLanguage", &UsdUILocalizationAPI::GetPropertyLanguage, (arg("prop"))) + .staticmethod("GetPropertyLanguage") + + .def("GetLocalizedPropertyName", + &UsdUILocalizationAPI::GetLocalizedPropertyName, + (arg("source"), arg("localization"))) + .staticmethod("GetLocalizedPropertyName") + ; +} + +} diff --git a/pxr/usd/usdUI/wrapTokens.cpp b/pxr/usd/usdUI/wrapTokens.cpp index 3fba90281f..4256a8ebd1 100644 --- a/pxr/usd/usdUI/wrapTokens.cpp +++ b/pxr/usd/usdUI/wrapTokens.cpp @@ -18,6 +18,10 @@ void wrapUsdUITokens() pxr_boost::python::class_ cls("Tokens", pxr_boost::python::no_init); _ADD_TOKEN(cls, closed); + _ADD_TOKEN(cls, default_); + _ADD_TOKEN(cls, lang); + _ADD_TOKEN(cls, localization); + _ADD_TOKEN(cls, localization_MultipleApplyTemplate_Language); _ADD_TOKEN(cls, minimized); _ADD_TOKEN(cls, open); _ADD_TOKEN(cls, uiDescription); @@ -31,6 +35,7 @@ void wrapUsdUITokens() _ADD_TOKEN(cls, uiNodegraphNodeSize); _ADD_TOKEN(cls, uiNodegraphNodeStackingOrder); _ADD_TOKEN(cls, Backdrop); + _ADD_TOKEN(cls, LocalizationAPI); _ADD_TOKEN(cls, NodeGraphNodeAPI); _ADD_TOKEN(cls, SceneGraphPrimAPI); }