/* ============================================================================== This file is part of the JUCE 6 technical preview. Copyright (c) 2017 - ROLI Ltd. You may use this code under the terms of the GPL v3 (see www.gnu.org/licenses). For this technical preview, this file is not subject to commercial licensing. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE DISCLAIMED. ============================================================================== */ #pragma once //============================================================================== class FileGroupInformationComponent : public Component, private ListBoxModel, private ValueTree::Listener { public: FileGroupInformationComponent (const Project::Item& group) : item (group), header (item.getName(), { getIcons().openFolder, Colours::transparentBlack }) { list.setHeaderComponent (std::make_unique (Array { "File", "Binary Resource", "Xcode Resource", "Compile", "Compiler Flag Scheme" }, Array { 0.3f, 0.15f, 0.15f, 0.15f, 0.25f })); list.setModel (this); list.setColour (ListBox::backgroundColourId, Colours::transparentBlack); addAndMakeVisible (list); list.updateContent(); list.setRowHeight (30); item.state.addListener (this); lookAndFeelChanged(); addAndMakeVisible (header); } ~FileGroupInformationComponent() override { item.state.removeListener (this); } //============================================================================== void paint (Graphics& g) override { g.setColour (findColour (secondaryBackgroundColourId)); g.fillRect (getLocalBounds().reduced (12, 0)); } void resized() override { auto bounds = getLocalBounds().reduced (12, 0); header.setBounds (bounds.removeFromTop (40)); list.setBounds (bounds.reduced (10, 4)); } void parentSizeChanged() override { setSize (jmax (550, getParentWidth()), getParentHeight()); } int getNumRows() override { return item.getNumChildren(); } void paintListBoxItem (int rowNumber, Graphics& g, int width, int height, bool /*rowIsSelected*/) override { g.setColour (findColour (rowNumber % 2 == 0 ? widgetBackgroundColourId : secondaryWidgetBackgroundColourId)); g.fillRect (0, 0, width, height - 1); } Component* refreshComponentForRow (int rowNumber, bool /*isRowSelected*/, Component* existingComponentToUpdate) override { std::unique_ptr existing (existingComponentToUpdate); if (rowNumber < getNumRows()) { auto child = item.getChild (rowNumber); if (existingComponentToUpdate == nullptr || dynamic_cast (existing.get())->item != child) { existing.reset(); existing.reset (new FileOptionComponent (child, dynamic_cast (list.getHeaderComponent()))); } } return existing.release(); } String getGroupPath() const { return item.getFile().getFullPathName(); } //============================================================================== void valueTreePropertyChanged (ValueTree&, const Identifier&) override { itemChanged(); } void valueTreeChildAdded (ValueTree&, ValueTree&) override { itemChanged(); } void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override { itemChanged(); } void valueTreeChildOrderChanged (ValueTree&, int, int) override { itemChanged(); } void valueTreeParentChanged (ValueTree&) override { itemChanged(); } private: Project::Item item; ListBox list; ContentViewHeader header; void itemChanged() { list.updateContent(); repaint(); } //============================================================================== class FileOptionComponent : public Component { public: FileOptionComponent (const Project::Item& fileItem, ListBoxHeader* listBoxHeader) : item (fileItem), header (listBoxHeader), compilerFlagSchemeSelector (item) { if (item.isFile()) { addAndMakeVisible (compileButton); compileButton.getToggleStateValue().referTo (item.getShouldCompileValue()); compileButton.onStateChange = [this] { compilerFlagSchemeSelector.setVisible (compileButton.getToggleState()); }; addAndMakeVisible (binaryResourceButton); binaryResourceButton.getToggleStateValue().referTo (item.getShouldAddToBinaryResourcesValue()); addAndMakeVisible (xcodeResourceButton); xcodeResourceButton.getToggleStateValue().referTo (item.getShouldAddToXcodeResourcesValue()); addChildComponent (compilerFlagSchemeSelector); compilerFlagSchemeSelector.setVisible (compileButton.getToggleState()); } } void paint (Graphics& g) override { if (header != nullptr) { auto textBounds = getLocalBounds().removeFromLeft (roundToInt (header->getProportionAtIndex (0) * getWidth())); auto iconBounds = textBounds.removeFromLeft (25); if (item.isImageFile()) iconBounds.reduce (5, 5); item.getIcon().withColour (findColour (treeIconColourId)).draw (g, iconBounds.toFloat(), item.isIconCrossedOut()); g.setColour (findColour (widgetTextColourId)); g.drawText (item.getName(), textBounds, Justification::centredLeft); } } void resized() override { if (header != nullptr) { auto bounds = getLocalBounds(); auto width = getWidth(); bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (0) * width)); binaryResourceButton.setBounds (bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (1) * width))); xcodeResourceButton.setBounds (bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (2) * width))); compileButton.setBounds (bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (3) * width))); compilerFlagSchemeSelector.setBounds (bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (4) * width))); } } Project::Item item; private: //============================================================================== class CompilerFlagSchemeSelector : public Component, private Value::Listener { public: CompilerFlagSchemeSelector (Project::Item& it) : item (it) { schemeBox.setTextWhenNothingSelected ("None"); updateCompilerFlagSchemeComboBox(); schemeBox.onChange = [this] { handleComboBoxSelection(); }; addAndMakeVisible (schemeBox); addChildComponent (newSchemeLabel); newSchemeLabel.setEditable (true); newSchemeLabel.setJustificationType (Justification::centredLeft); newSchemeLabel.onEditorHide = [this] { newSchemeLabel.setVisible (false); schemeBox.setVisible (true); auto newScheme = newSchemeLabel.getText(); item.project.addCompilerFlagScheme (newScheme); if (item.getCompilerFlagSchemeString().isEmpty()) item.setCompilerFlagScheme (newScheme); updateCompilerFlagSchemeComboBox(); }; selectScheme (item.getCompilerFlagSchemeString()); projectCompilerFlagSchemesValue = item.project.getProjectValue (Ids::compilerFlagSchemes); projectCompilerFlagSchemesValue.addListener (this); lookAndFeelChanged(); } void resized() override { auto b = getLocalBounds(); schemeBox.setBounds (b); newSchemeLabel.setBounds (b); } private: void valueChanged (Value&) override { updateCompilerFlagSchemeComboBox(); } void lookAndFeelChanged() override { schemeBox.setColour (ComboBox::outlineColourId, Colours::transparentBlack); schemeBox.setColour (ComboBox::textColourId, findColour (defaultTextColourId)); } void updateCompilerFlagSchemeComboBox() { auto itemScheme = item.getCompilerFlagSchemeString(); auto allSchemes = item.project.getCompilerFlagSchemes(); if (itemScheme.isNotEmpty() && ! allSchemes.contains (itemScheme)) { item.clearCurrentCompilerFlagScheme(); itemScheme = {}; } schemeBox.clear(); schemeBox.addItemList (allSchemes, 1); schemeBox.addSeparator(); schemeBox.addItem ("Add a new scheme...", -1); schemeBox.addItem ("Delete selected scheme", -2); schemeBox.addItem ("Clear", -3); selectScheme (itemScheme); } void handleComboBoxSelection() { auto selectedID = schemeBox.getSelectedId(); if (selectedID > 0) { item.setCompilerFlagScheme (schemeBox.getItemText (selectedID - 1)); } else if (selectedID == -1) { newSchemeLabel.setText ("NewScheme", dontSendNotification); schemeBox.setVisible (false); newSchemeLabel.setVisible (true); newSchemeLabel.showEditor(); if (auto* ed = newSchemeLabel.getCurrentTextEditor()) ed->setInputRestrictions (64, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_"); } else if (selectedID == -2) { auto currentScheme = item.getCompilerFlagSchemeString(); if (currentScheme.isNotEmpty()) { item.project.removeCompilerFlagScheme (currentScheme); item.clearCurrentCompilerFlagScheme(); } updateCompilerFlagSchemeComboBox(); } else if (selectedID == -3) { schemeBox.setSelectedId (0); item.clearCurrentCompilerFlagScheme(); } } void selectScheme (const String& schemeToSelect) { if (schemeToSelect.isNotEmpty()) { for (int i = 0; i < schemeBox.getNumItems(); ++i) { if (schemeBox.getItemText (i) == schemeToSelect) { schemeBox.setSelectedItemIndex (i); return; } } } schemeBox.setSelectedId (0); } Project::Item& item; Value projectCompilerFlagSchemesValue; ComboBox schemeBox; Label newSchemeLabel; }; //============================================================================== ListBoxHeader* header; ToggleButton compileButton, binaryResourceButton, xcodeResourceButton; CompilerFlagSchemeSelector compilerFlagSchemeSelector; }; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileGroupInformationComponent) };