ReaWwise

REAPER extension
Log | Files | Refs | Submodules

SelectedRowPropertiesComponent.cpp (13938B)


      1 /*----------------------------------------------------------------------------------------
      2 
      3 Copyright (c) 2023 AUDIOKINETIC Inc.
      4 
      5 This file is licensed to use under the license available at:
      6 https://github.com/audiokinetic/ReaWwise/blob/main/License.txt (the "License").
      7 You may not use this file except in compliance with the License.
      8 
      9 Unless required by applicable law or agreed to in writing, software distributed
     10 under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
     11 CONDITIONS OF ANY KIND, either express or implied.  See the License for the
     12 specific language governing permissions and limitations under the License.
     13 
     14 ----------------------------------------------------------------------------------------*/
     15 
     16 #include "SelectedRowPropertiesComponent.h"
     17 
     18 #include "BinaryData.h"
     19 #include "Helpers/ImportHelper.h"
     20 #include "Helpers/WwiseHelper.h"
     21 #include "Model/IDs.h"
     22 #include "Model/Wwise.h"
     23 #include "Theme/CustomLookAndFeel.h"
     24 
     25 namespace AK::WwiseTransfer
     26 {
     27 	namespace SelectedRowPropertiesComponentConstants
     28 	{
     29 		constexpr int margin = 10;
     30 		constexpr int spacing = 4;
     31 		constexpr int bottomMargin = 14;
     32 		constexpr int labelHeight = 26;
     33 		constexpr int labelWidth = 170;
     34 		constexpr int wildcardsButtonWidth = 100;
     35 		constexpr int syncButtonWidth = 36;
     36 		constexpr int propertyTemplateToggleButtonWidth = 22;
     37 		constexpr int lineThickness = 2;
     38 		constexpr std::initializer_list<Wwise::ObjectType> objectTypes{Wwise::ObjectType::ActorMixer, Wwise::ObjectType::BlendContainer,
     39 			Wwise::ObjectType::PhysicalFolder, Wwise::ObjectType::RandomContainer, Wwise::ObjectType::SequenceContainer, Wwise::ObjectType::SoundSFX,
     40 			Wwise::ObjectType::SoundVoice, Wwise::ObjectType::SwitchContainer, Wwise::ObjectType::VirtualFolder, Wwise::ObjectType::WorkUnit};
     41 		const juce::String pastePropertiesToolTip = "Paste properties are only available when connected to Wwise 2022+";
     42 	} // namespace SelectedRowPropertiesComponentConstants
     43 
     44 	SelectedRowPropertiesComponent::SelectedRowPropertiesComponent(juce::ValueTree appState, WaapiClient& waapiClient)
     45 		: applicationState(appState)
     46 		, hierarchyMapping(applicationState.getChildWithName(IDs::hierarchyMapping))
     47 		, languages(applicationState.getChildWithName(IDs::languages))
     48 		, selectedRow(hierarchyMapping, IDs::selectedRow, nullptr)
     49 		, emptyHierarchyMappingNode(ImportHelper::hierarchyMappingNodeToValueTree(Import::HierarchyMappingNode("", Wwise::ObjectType::Unknown)))
     50 		, waapiClient(waapiClient)
     51 		, propertyTemplatePathSyncButton("PropertyTemplatePathSyncButton", juce::Drawable::createFromImageData(BinaryData::General_GetFromWwise_Normal_svg, BinaryData::General_GetFromWwise_Normal_svgSize))
     52 	{
     53 		using namespace SelectedRowPropertiesComponentConstants;
     54 
     55 		setText("Selected Row Properties");
     56 
     57 		auto featureSupport = applicationState.getChildWithName(IDs::featureSupport);
     58 		applyTemplateFeatureEnabled.referTo(featureSupport, IDs::applyTemplateFeatureEnabled, nullptr);
     59 
     60 		objectTypeLabel.setText("Object Type", juce::dontSendNotification);
     61 		objectTypeLabel.setBorderSize(juce::BorderSize(0));
     62 		objectTypeLabel.setMinimumHorizontalScale(1.0f);
     63 		objectTypeLabel.setJustificationType(juce::Justification::right);
     64 
     65 		objectNameLabel.setText("Object Name", juce::dontSendNotification);
     66 		objectNameLabel.setBorderSize(juce::BorderSize(0));
     67 		objectNameLabel.setMinimumHorizontalScale(1.0f);
     68 		objectNameLabel.setJustificationType(juce::Justification::right);
     69 
     70 		propertyTemplatePathLabel.setText("Use Property Template", juce::dontSendNotification);
     71 		propertyTemplatePathLabel.setBorderSize(juce::BorderSize(0));
     72 		propertyTemplatePathLabel.setMinimumHorizontalScale(1.0f);
     73 		propertyTemplatePathLabel.setJustificationType(juce::Justification::right);
     74 
     75 		propertyTemplatePathSyncButton.setTooltip("Sync with selected object in Wwise");
     76 
     77 		objectNameEditor.setFont(CustomLookAndFeelConstants::smallFontSize);
     78 		propertyTemplatePathEditor.setFont(CustomLookAndFeelConstants::smallFontSize);
     79 
     80 		objectTypeComboBox.setTextWhenNothingSelected("Select Object Type");
     81 
     82 		for(auto& objectType : objectTypes)
     83 			objectTypeComboBox.addItem(WwiseHelper::objectTypeToReadableString(objectType), static_cast<int>(objectType));
     84 
     85 		objectLanguageComboBox.setTextWhenNothingSelected("Language");
     86 		objectLanguageComboBox.setTextWhenNoChoicesAvailable("No Languages loaded");
     87 
     88 		addAndMakeVisible(objectTypeLabel);
     89 		addAndMakeVisible(objectNameLabel);
     90 		addAndMakeVisible(propertyTemplatePathLabel);
     91 		addAndMakeVisible(objectTypeComboBox);
     92 		addAndMakeVisible(objectNameEditor);
     93 		addAndMakeVisible(propertyTemplatePathEditor);
     94 		addAndMakeVisible(propertyTemplateToggleButton);
     95 		addAndMakeVisible(wildcardSelector);
     96 		addAndMakeVisible(propertyTemplatePathSyncButton);
     97 		addChildComponent(objectLanguageComboBox);
     98 
     99 		refreshComponent();
    100 
    101 		objectTypeComboBox.onChange = [this]
    102 		{
    103 			objectType = static_cast<Wwise::ObjectType>(objectTypeComboBox.getSelectedId());
    104 		};
    105 
    106 		wildcardSelector.onItemSelected = [this](const juce::String& wildcard)
    107 		{
    108 			objectName = objectName + wildcard;
    109 		};
    110 
    111 		propertyTemplatePathSyncButton.onClick = [this]
    112 		{
    113 			updatePropertyTemplatePath();
    114 		};
    115 
    116 		objectLanguageComboBox.onChange = [this]
    117 		{
    118 			objectLanguage = objectLanguageComboBox.getText();
    119 		};
    120 
    121 		applicationState.addListener(this);
    122 	}
    123 
    124 	SelectedRowPropertiesComponent::~SelectedRowPropertiesComponent()
    125 	{
    126 		applicationState.removeListener(this);
    127 	}
    128 
    129 	void SelectedRowPropertiesComponent::refreshComponent()
    130 	{
    131 		hierarchyMappingNode = selectedRow >= 0 ? hierarchyMapping.getChild(selectedRow) : emptyHierarchyMappingNode;
    132 
    133 		objectName.referTo(hierarchyMappingNode, IDs::objectName, nullptr);
    134 		objectNameValid.referTo(hierarchyMappingNode, IDs::objectNameValid, nullptr);
    135 		objectNameErrorMessage.referTo(hierarchyMappingNode, IDs::objectNameErrorMessage, nullptr);
    136 		objectType.referTo(hierarchyMappingNode, IDs::objectType, nullptr);
    137 
    138 		objectLanguage.referTo(hierarchyMappingNode, IDs::objectLanguage, nullptr);
    139 
    140 		propertyTemplatePath.referTo(hierarchyMappingNode, IDs::propertyTemplatePath, nullptr);
    141 		propertyTemplatePathEnabled.referTo(hierarchyMappingNode, IDs::propertyTemplatePathEnabled, nullptr);
    142 		propertyTemplatePathValid.referTo(hierarchyMappingNode, IDs::propertyTemplatePathValid, nullptr);
    143 		propertyTemplatePathType.referTo(hierarchyMappingNode, IDs::propertyTemplatePathType, nullptr);
    144 
    145 		// Bind components that support cached value binding
    146 		objectNameEditor.getTextValue().referTo(objectName.getPropertyAsValue());
    147 		objectNameEditor.getValidationValue().referTo(objectNameValid.getPropertyAsValue());
    148 		objectNameEditor.getErrorMessageValue().referTo(objectNameErrorMessage.getPropertyAsValue());
    149 		objectTypeComboBox.getSelectedIdAsValue().referTo(objectType.getPropertyAsValue());
    150 		propertyTemplatePathEditor.getTextValue().referTo(propertyTemplatePath.getPropertyAsValue());
    151 		propertyTemplatePathEditor.getValidationValue().referTo(propertyTemplatePathValid.getPropertyAsValue());
    152 		propertyTemplatePathEditor.getErrorMessageValue().referTo(hierarchyMappingNode.getPropertyAsValue(IDs::propertyTemplatePathErrorMessage, nullptr));
    153 		propertyTemplateToggleButton.getToggleStateValue().referTo(propertyTemplatePathEnabled.getPropertyAsValue());
    154 
    155 		updatePropertyTemplateSection();
    156 
    157 		objectLanguageComboBox.setVisible(objectType == Wwise::ObjectType::SoundVoice);
    158 		objectLanguageComboBox.clear();
    159 
    160 		auto languageList = WwiseHelper::valueTreeToLanguages(languages);
    161 		for(int i = 0; i < languageList.size(); ++i)
    162 		{
    163 			objectLanguageComboBox.addItem(languageList[i], i + 1);
    164 			if(languageList[i] == objectLanguage)
    165 				objectLanguageComboBox.setSelectedId(i + 1);
    166 		}
    167 	}
    168 
    169 	void SelectedRowPropertiesComponent::updatePropertyTemplatePath()
    170 	{
    171 		// TODO: show loading state
    172 		auto onGetSelectedObject = [this](const Waapi::Response<Waapi::ObjectResponse>& response)
    173 		{
    174 			if(response.result.path.isEmpty())
    175 				return juce::AlertWindow::showMessageBoxAsync(juce::MessageBoxIconType::InfoIcon, "Property Template Path", "No object is selected in Wwise. Please select one and try again.");
    176 
    177 			propertyTemplatePath = response.result.path;
    178 			propertyTemplatePathType = response.result.type;
    179 
    180 			// TODO: end loading state
    181 		};
    182 
    183 		waapiClient.getSelectedObjectAsync(onGetSelectedObject);
    184 	}
    185 
    186 	void SelectedRowPropertiesComponent::updatePropertyTemplateSection()
    187 	{
    188 		using namespace SelectedRowPropertiesComponentConstants;
    189 
    190 		auto isEnabled = propertyTemplatePathEnabled && applyTemplateFeatureEnabled;
    191 
    192 		propertyTemplatePathSyncButton.setEnabled(isEnabled);
    193 		propertyTemplatePathEditor.setEnabled(isEnabled);
    194 		propertyTemplatePathEditor.setAlpha(isEnabled ? 1 : 0.5f);
    195 		propertyTemplatePathEditor.setMouseCursor(isEnabled ? juce::MouseCursor::IBeamCursor : juce::MouseCursor::NormalCursor);
    196 
    197 		// Not using isEnabled because these fields depend on propertyTemplatePathEnabled.
    198 		propertyTemplateToggleButton.setEnabled(applyTemplateFeatureEnabled);
    199 		propertyTemplatePathLabel.setEnabled(applyTemplateFeatureEnabled);
    200 
    201 		propertyTemplatePathEditor.setTooltip(applyTemplateFeatureEnabled ? "" : pastePropertiesToolTip);
    202 		propertyTemplatePathSyncButton.setTooltip(applyTemplateFeatureEnabled ? "Sync with selected object in Wwise" : pastePropertiesToolTip);
    203 		propertyTemplateToggleButton.setTooltip(applyTemplateFeatureEnabled ? "" : pastePropertiesToolTip);
    204 		propertyTemplatePathLabel.setTooltip(applyTemplateFeatureEnabled ? "" : pastePropertiesToolTip);
    205 	}
    206 
    207 	void SelectedRowPropertiesComponent::paint(juce::Graphics& g)
    208 	{
    209 		juce::Font f(CustomLookAndFeelConstants::regularFontSize);
    210 
    211 		const float textH = f.getHeight();
    212 		const float indent = 3.0f;
    213 		const float textEdgeGap = 4.0f;
    214 		auto constant = 5.0f;
    215 
    216 		auto x = indent;
    217 		auto y = f.getAscent() - 3.0f;
    218 		auto w = juce::jmax(0.0f, (float)getWidth() - x * 2.0f);
    219 		auto h = juce::jmax(0.0f, (float)getHeight() - y - indent);
    220 		constant = juce::jmin(constant, w * 0.5f, h * 0.5f);
    221 		auto constant2 = 2.0f * constant;
    222 
    223 		auto textW = getText().isEmpty() ? 0
    224 		                                 : juce::jlimit(0.0f,
    225 											   juce::jmax(0.0f, w - constant2 - textEdgeGap * 2),
    226 											   (float)f.getStringWidth(getText()) + textEdgeGap * 2.0f);
    227 		auto textX = constant + textEdgeGap;
    228 
    229 		juce::Path p;
    230 		p.startNewSubPath(0, y);
    231 		p.lineTo(constant2, y);
    232 
    233 		p.startNewSubPath(x + textX + textW, y);
    234 		p.lineTo(x + w, y);
    235 
    236 		auto alpha = 1.0f;
    237 
    238 		g.setColour(findColour(juce::GroupComponent::outlineColourId)
    239 						.withMultipliedAlpha(alpha));
    240 
    241 		g.strokePath(p, juce::PathStrokeType(2.0f));
    242 
    243 		g.setColour(findColour(juce::GroupComponent::textColourId)
    244 						.withMultipliedAlpha(alpha));
    245 
    246 		g.setFont(f);
    247 
    248 		g.drawText(getText(),
    249 			juce::roundToInt(x + textX), 0,
    250 			juce::roundToInt(textW),
    251 			juce::roundToInt(textH),
    252 			juce::Justification::centred, true);
    253 	}
    254 
    255 	void SelectedRowPropertiesComponent::resized()
    256 	{
    257 		using namespace SelectedRowPropertiesComponentConstants;
    258 
    259 		auto area = getLocalBounds();
    260 		area.removeFromTop(labelHeight);
    261 
    262 		auto objectTypeArea = area.removeFromTop(labelHeight);
    263 		{
    264 			objectTypeLabel.setBounds(objectTypeArea.removeFromLeft(labelWidth));
    265 
    266 			objectLanguageComboBox.setBounds(objectTypeArea.removeFromRight(wildcardsButtonWidth));
    267 
    268 			objectTypeArea.removeFromRight(spacing);
    269 
    270 			objectTypeArea.removeFromLeft(margin);
    271 			objectTypeComboBox.setBounds(objectTypeArea);
    272 		}
    273 
    274 		area.removeFromTop(margin);
    275 
    276 		auto objectNameArea = area.removeFromTop(labelHeight);
    277 		{
    278 			objectNameLabel.setBounds(objectNameArea.removeFromLeft(labelWidth));
    279 
    280 			wildcardSelector.setBounds(objectNameArea.removeFromRight(wildcardsButtonWidth));
    281 			objectNameArea.removeFromRight(spacing);
    282 
    283 			objectNameArea.removeFromLeft(margin);
    284 			objectNameEditor.setBounds(objectNameArea);
    285 		}
    286 
    287 		area.removeFromTop(margin);
    288 
    289 		auto propertyTemplatePathArea = area.removeFromTop(labelHeight);
    290 		{
    291 			propertyTemplateToggleButton.setBounds(propertyTemplatePathArea.removeFromLeft(propertyTemplateToggleButtonWidth));
    292 			propertyTemplatePathLabel.setBounds(propertyTemplatePathArea.removeFromLeft(labelWidth - propertyTemplateToggleButtonWidth));
    293 
    294 			propertyTemplatePathArea.removeFromRight(wildcardsButtonWidth - syncButtonWidth);
    295 			propertyTemplatePathSyncButton.setBounds(propertyTemplatePathArea.removeFromRight(syncButtonWidth));
    296 			propertyTemplatePathArea.removeFromRight(spacing);
    297 
    298 			propertyTemplatePathArea.removeFromLeft(margin);
    299 			propertyTemplatePathEditor.setBounds(propertyTemplatePathArea);
    300 		}
    301 	}
    302 
    303 	void SelectedRowPropertiesComponent::valueTreePropertyChanged(juce::ValueTree& treeWhosePropertyHasChanged, const juce::Identifier& property)
    304 	{
    305 		auto treeType = treeWhosePropertyHasChanged.getType();
    306 
    307 		if(treeType == IDs::hierarchyMapping && property == IDs::selectedRow ||
    308 			treeType == IDs::hierarchyMappingNode && (property == IDs::objectType || property == IDs::propertyTemplatePathEnabled) ||
    309 			treeType == IDs::featureSupport && property == IDs::applyTemplateFeatureEnabled)
    310 		{
    311 			triggerAsyncUpdate();
    312 		}
    313 	}
    314 
    315 	void SelectedRowPropertiesComponent::valueTreeChildAdded(juce::ValueTree& parentTree, juce::ValueTree& childWhichHasBeenAdded)
    316 	{
    317 		auto treeType = parentTree.getType();
    318 
    319 		if(treeType == IDs::hierarchyMapping || treeType == IDs::languages)
    320 		{
    321 			triggerAsyncUpdate();
    322 		}
    323 	}
    324 
    325 	void SelectedRowPropertiesComponent::valueTreeChildRemoved(juce::ValueTree& parentTree, juce::ValueTree& childWhichHasBeenRemoved, int indexFromWhichChildWasRemoved)
    326 	{
    327 		if(parentTree.getType() == IDs::hierarchyMapping)
    328 		{
    329 			triggerAsyncUpdate();
    330 		}
    331 	}
    332 
    333 	void SelectedRowPropertiesComponent::valueTreeChildOrderChanged(juce::ValueTree& parentTreeWhoseChildrenHaveMoved, int oldIndex, int newIndex)
    334 	{
    335 		if(parentTreeWhoseChildrenHaveMoved.getType() == IDs::hierarchyMapping)
    336 		{
    337 			triggerAsyncUpdate();
    338 		}
    339 	}
    340 
    341 	void SelectedRowPropertiesComponent::handleAsyncUpdate()
    342 	{
    343 		refreshComponent();
    344 	}
    345 } // namespace AK::WwiseTransfer