ReaWwise

REAPER extension
Log | Files | Refs | Submodules

CustomLookAndFeel.cpp (13829B)


      1 /*----------------------------------------------------------------------------------------
      2 
      3 Copyright (c) 2023 AUDIOKINETIC Inc.
      4 
      5 The script in 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 "CustomLookAndFeel.h"
     17 
     18 #include "BinaryData.h"
     19 #include "UI/HierarchyMappingTable.h"
     20 #include "UI/ImportPreviewComponent.h"
     21 #include "UI/ValidatableTextEditor.h"
     22 
     23 namespace AK::WwiseTransfer
     24 {
     25 	CustomLookAndFeel::CustomLookAndFeel()
     26 		: windowBackgroundColor{0xff464646}
     27 		, widgetBackgroundColor{0xff373737}
     28 		, thickOutlineColor{0xff3a3a3a}
     29 		, textColor{0xfff9f9f9}
     30 		, textColorDisabled(0x99000000)
     31 		, highlightedTextColor{0xffbcbcbc}
     32 		, highlightedFillColor{0xff646464}
     33 		, buttonBackgroundColor{0xff5a5a5a}
     34 		, thinOutlineColor{0xff292929}
     35 		, focusedOutlineColor{0xffbc9770}
     36 		, tableHeaderBackgroundColor{0xff545454}
     37 		, previewItemNoChangeColor{0xff7d7d7d}
     38 		, previewItemNewColor{0xff29afff}
     39 		, previewItemReplacedColor{0xffda8e40}
     40 		, errorColor(0xffff3333)
     41 		, regularTypeFace{juce::Typeface::createSystemTypefaceFor(BinaryData::noto_universal_ttf, BinaryData::noto_universal_ttfSize)}
     42 	{
     43 		setColourScheme({
     44 			windowBackgroundColor,
     45 			widgetBackgroundColor,
     46 			widgetBackgroundColor,
     47 			thickOutlineColor,
     48 			textColor,
     49 			highlightedFillColor,
     50 			highlightedTextColor,
     51 			highlightedFillColor,
     52 			textColor,
     53 		});
     54 
     55 		setColour(juce::TextButton::buttonColourId, buttonBackgroundColor);
     56 		setColour(juce::TextEditor::outlineColourId, thinOutlineColor);
     57 		setColour(juce::TextEditor::focusedOutlineColourId, focusedOutlineColor);
     58 		setColour(juce::ComboBox::focusedOutlineColourId, focusedOutlineColor);
     59 		setColour(juce::ComboBox::outlineColourId, thinOutlineColor);
     60 		setColour(juce::TreeView::backgroundColourId, widgetBackgroundColor);
     61 		setColour(juce::TableHeaderComponent::backgroundColourId, tableHeaderBackgroundColor);
     62 		setColour(juce::TableHeaderComponent::textColourId, textColor);
     63 		setColour(juce::TableHeaderComponent::highlightColourId, highlightedFillColor);
     64 		setColour(juce::TableHeaderComponent::outlineColourId, thickOutlineColor);
     65 		setColour(juce::HyperlinkButton::textColourId, textColor);
     66 		setColour(juce::TooltipWindow::backgroundColourId, widgetBackgroundColor);
     67 
     68 		setColour(ValidatableTextEditor::errorOutlineColor, errorColor);
     69 		setColour(HierarchyMappingTable::errorOutlineColor, errorColor);
     70 		setColour(ValueTreeItem::errorOutlineColor, errorColor);
     71 	}
     72 
     73 	std::unique_ptr<juce::Drawable> CustomLookAndFeel::getIconForObjectType(Wwise::ObjectType objectType)
     74 	{
     75 		using namespace Wwise;
     76 
     77 		switch(objectType)
     78 		{
     79 		case ObjectType::ActorMixer:
     80 		{
     81 			return juce::Drawable::createFromImageData(BinaryData::ObjectIcons_ActorMixer_nor_svg, BinaryData::ObjectIcons_ActorMixer_nor_svgSize);
     82 		}
     83 		case ObjectType::AudioFileSource:
     84 		{
     85 			return juce::Drawable::createFromImageData(BinaryData::ObjectIcons_AudioObjectSound_nor_svg, BinaryData::ObjectIcons_AudioObjectSound_nor_svgSize);
     86 		}
     87 		case ObjectType::BlendContainer:
     88 		{
     89 			return juce::Drawable::createFromImageData(BinaryData::ObjectIcons_BlendContainer_nor_svg, BinaryData::ObjectIcons_BlendContainer_nor_svgSize);
     90 		}
     91 		case ObjectType::PhysicalFolder:
     92 		{
     93 			return juce::Drawable::createFromImageData(BinaryData::ObjectIcons_PhysicalFolder_nor_svg, BinaryData::ObjectIcons_PhysicalFolder_nor_svgSize);
     94 		}
     95 		case ObjectType::RandomContainer:
     96 		{
     97 			return juce::Drawable::createFromImageData(BinaryData::ObjectIcons_RandomContainer_nor_svg, BinaryData::ObjectIcons_RandomContainer_nor_svgSize);
     98 		}
     99 		case ObjectType::SequenceContainer:
    100 		{
    101 			return juce::Drawable::createFromImageData(BinaryData::ObjectIcons_SequenceContainer_nor_svg, BinaryData::ObjectIcons_SequenceContainer_nor_svgSize);
    102 		}
    103 		case ObjectType::SoundSFX:
    104 		{
    105 			return juce::Drawable::createFromImageData(BinaryData::ObjectIcons_SoundFX_nor_svg, BinaryData::ObjectIcons_SoundFX_nor_svgSize);
    106 		}
    107 		case ObjectType::SoundVoice:
    108 		{
    109 			return juce::Drawable::createFromImageData(BinaryData::ObjectIcons_SoundVoice_nor_svg, BinaryData::ObjectIcons_SoundVoice_nor_svgSize);
    110 		}
    111 		case ObjectType::SwitchContainer:
    112 		{
    113 			return juce::Drawable::createFromImageData(BinaryData::ObjectIcons_SwitchContainer_nor_svg, BinaryData::ObjectIcons_SwitchContainer_nor_svgSize);
    114 		}
    115 		case ObjectType::VirtualFolder:
    116 		{
    117 			return juce::Drawable::createFromImageData(BinaryData::ObjectIcons_Folder_nor_svg, BinaryData::ObjectIcons_Folder_nor_svgSize);
    118 		}
    119 		case ObjectType::WorkUnit:
    120 		{
    121 			return juce::Drawable::createFromImageData(BinaryData::ObjectIcons_Workunit_nor_svg, BinaryData::ObjectIcons_Workunit_nor_svgSize);
    122 		}
    123 		default:
    124 		{
    125 			return juce::Drawable::createFromImageData(BinaryData::ObjectIcons_Folder_nor_svg, BinaryData::ObjectIcons_Folder_nor_svgSize);
    126 		}
    127 		}
    128 	}
    129 
    130 	juce::Colour CustomLookAndFeel::getTextColourForObjectStatus(Import::ObjectStatus objectStatus)
    131 	{
    132 		if(objectStatus == Import::ObjectStatus::NoChange)
    133 			return previewItemNoChangeColor;
    134 		else if(objectStatus == Import::ObjectStatus::New || objectStatus == Import::ObjectStatus::NewRenamed)
    135 			return previewItemNewColor;
    136 		else if(objectStatus == Import::ObjectStatus::Replaced)
    137 			return previewItemReplacedColor;
    138 		else
    139 			return textColor;
    140 	}
    141 
    142 	void CustomLookAndFeel::drawTableHeaderColumn(juce::Graphics& g, juce::TableHeaderComponent& header,
    143 		const juce::String& columnName, int /*columnId*/,
    144 		int width, int height, bool isMouseOver, bool isMouseDown,
    145 		int columnFlags)
    146 	{
    147 		auto highlightColour = header.findColour(juce::TableHeaderComponent::highlightColourId);
    148 		auto columnDisabled = (columnFlags & 128) != 0;
    149 
    150 		if(isMouseDown && !columnDisabled)
    151 			g.fillAll(highlightColour);
    152 		else if(isMouseOver && !columnDisabled)
    153 			g.fillAll(highlightColour.withMultipliedAlpha(0.625f));
    154 
    155 		juce::Rectangle<int> area(width, height);
    156 		area.reduce(4, 0);
    157 
    158 		if((columnFlags & (juce::TableHeaderComponent::sortedForwards | juce::TableHeaderComponent::sortedBackwards)) != 0)
    159 		{
    160 			juce::Path sortArrow;
    161 			sortArrow.addTriangle(0.0f, 0.0f,
    162 				0.5f, (columnFlags & juce::TableHeaderComponent::sortedForwards) != 0 ? -0.8f : 0.8f,
    163 				1.0f, 0.0f);
    164 
    165 			g.setColour(textColorDisabled);
    166 			g.fillPath(sortArrow, sortArrow.getTransformToScaleToFit(area.removeFromRight(height / 2).reduced(2).toFloat(), true));
    167 		}
    168 
    169 		auto textColour = header.findColour(juce::TableHeaderComponent::textColourId)
    170 		                      .withAlpha(columnDisabled ? 0.5f : 1.0f);
    171 		g.setColour(textColour);
    172 		g.setFont(getTableHeaderFont());
    173 		g.drawFittedText(columnName, area, juce::Justification::centredLeft, 1);
    174 	}
    175 
    176 	void CustomLookAndFeel::drawGroupComponentOutline(juce::Graphics& g, int width, int height,
    177 		const juce::String& text, const juce::Justification& position,
    178 		juce::GroupComponent& group)
    179 	{
    180 		const float indent = 3.0f;
    181 		const float textEdgeGap = 4.0f;
    182 		auto constant = 5.0f;
    183 
    184 		juce::Font f(CustomLookAndFeelConstants::regularFontSize);
    185 
    186 		juce::Path p;
    187 		auto x = indent;
    188 		auto y = f.getAscent() - 3.0f;
    189 		auto w = juce::jmax(0.0f, (float)width - x * 2.0f);
    190 		auto h = juce::jmax(0.0f, (float)height - y - indent);
    191 		constant = juce::jmin(constant, w * 0.5f, h * 0.5f);
    192 		auto constant2 = 2.0f * constant;
    193 
    194 		auto textW = text.isEmpty() ? 0 : juce::jlimit(0.0f, juce::jmax(0.0f, w - constant2 - textEdgeGap * 2), (float)f.getStringWidth(text) + textEdgeGap * 2.0f);
    195 		auto textX = constant + textEdgeGap;
    196 
    197 		if(position.testFlags(juce::Justification::horizontallyCentred))
    198 			textX = constant + (w - constant2 - textW) * 0.5f;
    199 		else if(position.testFlags(juce::Justification::right))
    200 			textX = w - constant - textW - textEdgeGap;
    201 
    202 		p.startNewSubPath(x + textX + textW, y);
    203 		p.lineTo(x + w - constant, y);
    204 
    205 		p.addArc(x + w - constant2, y, constant2, constant2, 0, juce::MathConstants<float>::halfPi);
    206 		p.lineTo(x + w, y + h - constant);
    207 
    208 		p.addArc(x + w - constant2, y + h - constant2, constant2, constant2, juce::MathConstants<float>::halfPi, juce::MathConstants<float>::pi);
    209 		p.lineTo(x + constant, y + h);
    210 
    211 		p.addArc(x, y + h - constant2, constant2, constant2, juce::MathConstants<float>::pi, juce::MathConstants<float>::pi * 1.5f);
    212 		p.lineTo(x, y + constant);
    213 
    214 		p.addArc(x, y, constant2, constant2, juce::MathConstants<float>::pi * 1.5f, juce::MathConstants<float>::twoPi);
    215 		p.lineTo(x + textX, y);
    216 
    217 		auto alpha = group.isEnabled() ? 1.0f : 0.5f;
    218 
    219 		g.setColour(group.findColour(juce::GroupComponent::outlineColourId)
    220 						.withMultipliedAlpha(alpha));
    221 
    222 		g.strokePath(p, juce::PathStrokeType(2.0f));
    223 
    224 		g.setColour(group.findColour(juce::GroupComponent::textColourId)
    225 						.withMultipliedAlpha(alpha));
    226 		g.setFont(f);
    227 		g.drawText(text,
    228 			juce::roundToInt(x + textX), 0,
    229 			juce::roundToInt(textW),
    230 			juce::roundToInt(f.getHeight()),
    231 			juce::Justification::centred, true);
    232 	}
    233 
    234 	void CustomLookAndFeel::drawTextEditorOutline(juce::Graphics& g, int width, int height, juce::TextEditor& textEditor)
    235 	{
    236 		if(dynamic_cast<juce::AlertWindow*>(textEditor.getParentComponent()) != nullptr)
    237 			return;
    238 
    239 		const auto hasKeyboardFocus = textEditor.hasKeyboardFocus(true);
    240 
    241 		auto colour = textEditor.findColour(hasKeyboardFocus ? juce::TextEditor::focusedOutlineColourId : juce::TextEditor::outlineColourId);
    242 		auto lineThickness = 1;
    243 
    244 		g.setColour(colour);
    245 		g.drawRect(0, 0, width, height, lineThickness);
    246 	}
    247 
    248 	void CustomLookAndFeel::fillTextEditorBackground(juce::Graphics& g, int width, int height, juce::TextEditor& textEditor)
    249 	{
    250 		if(dynamic_cast<juce::AlertWindow*>(textEditor.getParentComponent()) == nullptr)
    251 		{
    252 			auto backgroundColor = textEditor.isReadOnly() ? windowBackgroundColor : textEditor.findColour(juce::TextEditor::backgroundColourId);
    253 
    254 			g.fillAll(backgroundColor);
    255 		}
    256 		else
    257 		{
    258 			LookAndFeel_V2::fillTextEditorBackground(g, width, height, textEditor);
    259 		}
    260 	}
    261 
    262 	void CustomLookAndFeel::drawTooltip(juce::Graphics& g, const juce::String& text, int width, int height)
    263 	{
    264 		juce::Rectangle<int> bounds(width, height);
    265 
    266 		g.setColour(findColour(juce::TooltipWindow::backgroundColourId));
    267 		g.fillRect(bounds.toFloat());
    268 
    269 		g.setColour(findColour(juce::TooltipWindow::outlineColourId));
    270 		g.drawRect(bounds.toFloat().reduced(0.5f, 0.5f), 1.0f);
    271 
    272 		const float tooltipFontSize = 13.0f;
    273 		const int maxToolTipWidth = 400;
    274 
    275 		juce::AttributedString s;
    276 		s.setJustification(juce::Justification::centred);
    277 		s.append(text, juce::Font(tooltipFontSize), findColour(juce::TooltipWindow::textColourId));
    278 
    279 		juce::TextLayout tl;
    280 		tl.createLayoutWithBalancedLineLengths(s, (float)maxToolTipWidth);
    281 
    282 		tl.draw(g, {static_cast<float>(width), static_cast<float>(height)});
    283 	}
    284 
    285 	void CustomLookAndFeel::drawComboBox(juce::Graphics& g, int width, int height, bool isButtonDown,
    286 		int buttonX, int buttonY, int buttonW, int buttonH, juce::ComboBox& box)
    287 	{
    288 		// Copied from juce_LookAndFeel_V4.cpp and added support for outlining the component when in focus
    289 
    290 		auto cornerSize = box.findParentComponentOfClass<juce::ChoicePropertyComponent>() != nullptr ? 0.0f : 3.0f;
    291 		juce::Rectangle<int> boxBounds(0, 0, width, height);
    292 
    293 		g.setColour(box.findColour(juce::ComboBox::backgroundColourId));
    294 		g.fillRoundedRectangle(boxBounds.toFloat(), cornerSize);
    295 
    296 		if(box.isEnabled() && box.hasKeyboardFocus(false))
    297 			g.setColour(box.findColour(juce::ComboBox::focusedOutlineColourId));
    298 		else
    299 			g.setColour(box.findColour(juce::ComboBox::outlineColourId));
    300 
    301 		g.drawRoundedRectangle(boxBounds.toFloat().reduced(0.5f, 0.5f), cornerSize, 1.0f);
    302 
    303 		juce::Rectangle<int> arrowZone(width - 30, 0, 20, height);
    304 		juce::Path path;
    305 		path.startNewSubPath((float)arrowZone.getX() + 3.0f, (float)arrowZone.getCentreY() - 2.0f);
    306 		path.lineTo((float)arrowZone.getCentreX(), (float)arrowZone.getCentreY() + 3.0f);
    307 		path.lineTo((float)arrowZone.getRight() - 3.0f, (float)arrowZone.getCentreY() - 2.0f);
    308 
    309 		g.setColour(box.findColour(juce::ComboBox::arrowColourId).withAlpha((box.isEnabled() ? 0.9f : 0.2f)));
    310 		g.strokePath(path, juce::PathStrokeType(2.0f));
    311 	}
    312 
    313 	juce::Typeface::Ptr CustomLookAndFeel::getTypefaceForFont(const juce::Font& font)
    314 	{
    315 		return regularTypeFace;
    316 	}
    317 
    318 	juce::Font CustomLookAndFeel::getLabelFont(juce::Label& label)
    319 	{
    320 		static auto defaultLabelFont = juce::Label().getFont();
    321 
    322 		if(label.getFont() != defaultLabelFont)
    323 			return label.getFont();
    324 
    325 		return juce::Font(CustomLookAndFeelConstants::regularFontSize);
    326 	}
    327 
    328 	juce::Font CustomLookAndFeel::getTextButtonFont(juce::TextButton&, int buttonHeight)
    329 	{
    330 		return juce::Font(CustomLookAndFeelConstants::smallFontSize);
    331 	}
    332 
    333 	juce::Font CustomLookAndFeel::getPopupMenuFont()
    334 	{
    335 		return juce::Font(CustomLookAndFeelConstants::regularFontSize);
    336 	}
    337 
    338 	juce::Font CustomLookAndFeel::getTableHeaderFont()
    339 	{
    340 		return juce::Font(CustomLookAndFeelConstants::extraSmallFontSize);
    341 	}
    342 
    343 	juce::Font CustomLookAndFeel::getAlertWindowFont()
    344 	{
    345 		return juce::Font(CustomLookAndFeelConstants::smallFontSize);
    346 	}
    347 
    348 	juce::Font CustomLookAndFeel::getAlertWindowMessageFont()
    349 	{
    350 		return juce::Font(CustomLookAndFeelConstants::smallFontSize);
    351 	}
    352 
    353 	juce::Font CustomLookAndFeel::getAlertWindowTitleFont()
    354 	{
    355 		return juce::Font(CustomLookAndFeelConstants::largeFontSize);
    356 	}
    357 
    358 	juce::Font CustomLookAndFeel::getComboBoxFont(juce::ComboBox&)
    359 	{
    360 		return juce::Font(CustomLookAndFeelConstants::smallFontSize);
    361 	}
    362 } // namespace AK::WwiseTransfer