| @@ -254,6 +254,9 @@ public: | |||||
| header (metadata[Ids::name].toString(), metadata[Ids::description].toString(), BinaryData::background_logo_svg), | header (metadata[Ids::name].toString(), metadata[Ids::description].toString(), BinaryData::background_logo_svg), | ||||
| codeViewer (doc, &cppTokeniser) | codeViewer (doc, &cppTokeniser) | ||||
| { | { | ||||
| setTitle (exampleFile.getFileName()); | |||||
| setFocusContainerType (FocusContainerType::focusContainer); | |||||
| addAndMakeVisible (header); | addAndMakeVisible (header); | ||||
| openExampleButton.onClick = [this] { exampleSelectedCallback (exampleFile); }; | openExampleButton.onClick = [this] { exampleSelectedCallback (exampleFile); }; | ||||
| @@ -286,6 +289,7 @@ private: | |||||
| codeViewer.setScrollbarThickness (6); | codeViewer.setScrollbarThickness (6); | ||||
| codeViewer.setReadOnly (true); | codeViewer.setReadOnly (true); | ||||
| codeViewer.setTitle ("Code"); | |||||
| getAppSettings().appearance.applyToCodeEditor (codeViewer); | getAppSettings().appearance.applyToCodeEditor (codeViewer); | ||||
| codeViewer.scrollToLine (findBestLineToScrollToForClass (StringArray::fromLines (fileString), | codeViewer.scrollToLine (findBestLineToScrollToForClass (StringArray::fromLines (fileString), | ||||
| @@ -34,6 +34,12 @@ | |||||
| //============================================================================== | //============================================================================== | ||||
| struct ContentComponent : public Component | struct ContentComponent : public Component | ||||
| { | { | ||||
| ContentComponent() | |||||
| { | |||||
| setTitle ("Content"); | |||||
| setFocusContainerType (FocusContainerType::focusContainer); | |||||
| } | |||||
| void resized() override | void resized() override | ||||
| { | { | ||||
| if (content != nullptr) | if (content != nullptr) | ||||
| @@ -88,7 +94,8 @@ static std::unique_ptr<Component> createExampleProjectsTab (ContentComponent& co | |||||
| content.setContent (std::make_unique<ExampleComponent> (findExampleFile (category, index), cb)); | content.setContent (std::make_unique<ExampleComponent> (findExampleFile (category, index), cb)); | ||||
| }; | }; | ||||
| return std::make_unique<StartPageTreeHolder> (exampleCategories, | |||||
| return std::make_unique<StartPageTreeHolder> ("Examples", | |||||
| exampleCategories, | |||||
| examples, | examples, | ||||
| std::move (selectedCallback), | std::move (selectedCallback), | ||||
| StartPageTreeHolder::Open::no); | StartPageTreeHolder::Open::no); | ||||
| @@ -144,7 +151,8 @@ static std::unique_ptr<Component> createProjectTemplatesTab (ContentComponent& c | |||||
| content.setContent (std::make_unique<TemplateComponent> (templates[(size_t) index], std::move (cb))); | content.setContent (std::make_unique<TemplateComponent> (templates[(size_t) index], std::move (cb))); | ||||
| }; | }; | ||||
| auto holder = std::make_unique<StartPageTreeHolder> (categories, | |||||
| auto holder = std::make_unique<StartPageTreeHolder> ("Templates", | |||||
| categories, | |||||
| templateNames, | templateNames, | ||||
| std::move (selectedCallback), | std::move (selectedCallback), | ||||
| StartPageTreeHolder::Open::yes); | StartPageTreeHolder::Open::yes); | ||||
| @@ -165,7 +173,14 @@ struct ProjectTemplatesAndExamples : public TabbedComponent | |||||
| content (c), | content (c), | ||||
| exampleSelectedCallback (std::move (exampleCb)) | exampleSelectedCallback (std::move (exampleCb)) | ||||
| { | { | ||||
| addTab ("New Project", Colours::transparentBlack, createProjectTemplatesTab (content, std::move (newProjectCb)).release(), true); | |||||
| setTitle ("Templates and Examples"); | |||||
| setFocusContainerType (FocusContainerType::focusContainer); | |||||
| addTab ("New Project", | |||||
| Colours::transparentBlack, | |||||
| createProjectTemplatesTab (content, std::move (newProjectCb)).release(), | |||||
| true); | |||||
| refreshExamplesTab(); | refreshExamplesTab(); | ||||
| } | } | ||||
| @@ -177,8 +192,9 @@ struct ProjectTemplatesAndExamples : public TabbedComponent | |||||
| auto exampleTabs = createExampleProjectsTab (content, exampleSelectedCallback); | auto exampleTabs = createExampleProjectsTab (content, exampleSelectedCallback); | ||||
| addTab ("Open Example", Colours::transparentBlack, exampleTabs == nullptr ? new SetJUCEPathComponent (*this) | |||||
| : exampleTabs.release(), | |||||
| addTab ("Open Example", | |||||
| Colours::transparentBlack, | |||||
| exampleTabs == nullptr ? new SetJUCEPathComponent (*this) : exampleTabs.release(), | |||||
| true); | true); | ||||
| if (wasOpen) | if (wasOpen) | ||||
| @@ -31,14 +31,18 @@ class StartPageTreeHolder : public Component | |||||
| public: | public: | ||||
| enum class Open { no, yes }; | enum class Open { no, yes }; | ||||
| StartPageTreeHolder (const StringArray& headerNames, const std::vector<StringArray>& itemNames, | |||||
| std::function<void (int, int)>&& selectedCallback, Open shouldBeOpen) | |||||
| StartPageTreeHolder (const String& title, | |||||
| const StringArray& headerNames, | |||||
| const std::vector<StringArray>& itemNames, | |||||
| std::function<void (int, int)>&& selectedCallback, | |||||
| Open shouldBeOpen) | |||||
| : headers (headerNames), | : headers (headerNames), | ||||
| items (itemNames), | items (itemNames), | ||||
| itemSelectedCallback (std::move (selectedCallback)) | itemSelectedCallback (std::move (selectedCallback)) | ||||
| { | { | ||||
| jassert (headers.size() == (int) items.size()); | jassert (headers.size() == (int) items.size()); | ||||
| tree.setTitle (title); | |||||
| tree.setRootItem (new TreeRootItem (*this)); | tree.setRootItem (new TreeRootItem (*this)); | ||||
| tree.setRootItemVisible (false); | tree.setRootItemVisible (false); | ||||
| tree.setIndentSize (15); | tree.setIndentSize (15); | ||||
| @@ -88,13 +92,14 @@ private: | |||||
| addSubItem (new TreeSubItem (owner, s, {})); | addSubItem (new TreeSubItem (owner, s, {})); | ||||
| } | } | ||||
| bool mightContainSubItems() override { return isHeader; } | |||||
| bool canBeSelected() const override { return ! isHeader; } | |||||
| bool mightContainSubItems() override { return isHeader; } | |||||
| bool canBeSelected() const override { return ! isHeader; } | |||||
| int getItemWidth() const override { return -1; } | |||||
| int getItemHeight() const override { return 25; } | |||||
| int getItemWidth() const override { return -1; } | |||||
| int getItemHeight() const override { return 25; } | |||||
| String getUniqueName() const override { return name; } | |||||
| String getUniqueName() const override { return name; } | |||||
| String getAccessibilityName() override { return getUniqueName(); } | |||||
| void paintOpenCloseButton (Graphics& g, const Rectangle<float>& area, Colour, bool isMouseOver) override | void paintOpenCloseButton (Graphics& g, const Rectangle<float>& area, Colour, bool isMouseOver) override | ||||
| { | { | ||||
| @@ -122,10 +127,13 @@ private: | |||||
| g.drawFittedText (name, bounds.reduced (5).withTrimmedLeft (10), Justification::centredLeft, 1); | g.drawFittedText (name, bounds.reduced (5).withTrimmedLeft (10), Justification::centredLeft, 1); | ||||
| } | } | ||||
| void itemClicked (const MouseEvent&) override | |||||
| void itemClicked (const MouseEvent& e) override | |||||
| { | { | ||||
| if (isSelected()) | if (isSelected()) | ||||
| itemSelectionChanged (true); | itemSelectionChanged (true); | ||||
| if (e.mods.isPopupMenu() && mightContainSubItems()) | |||||
| setOpen (! isOpen()); | |||||
| } | } | ||||
| void itemSelectionChanged (bool isNowSelected) override | void itemSelectionChanged (bool isNowSelected) override | ||||
| @@ -35,16 +35,21 @@ public: | |||||
| LoginFormComponent (MainWindow& window) | LoginFormComponent (MainWindow& window) | ||||
| : mainWindow (window) | : mainWindow (window) | ||||
| { | { | ||||
| setTitle ("Login"); | |||||
| setFocusContainerType (FocusContainerType::focusContainer); | |||||
| addAndMakeVisible (emailBox); | addAndMakeVisible (emailBox); | ||||
| emailBox.setTextToShowWhenEmpty ("Email", Colours::black.withAlpha (0.2f)); | emailBox.setTextToShowWhenEmpty ("Email", Colours::black.withAlpha (0.2f)); | ||||
| emailBox.setJustification (Justification::centredLeft); | emailBox.setJustification (Justification::centredLeft); | ||||
| emailBox.onReturnKey = [this] { submitDetails(); }; | emailBox.onReturnKey = [this] { submitDetails(); }; | ||||
| emailBox.setTitle ("Email"); | |||||
| addAndMakeVisible (passwordBox); | addAndMakeVisible (passwordBox); | ||||
| passwordBox.setTextToShowWhenEmpty ("Password", Colours::black.withAlpha (0.2f)); | passwordBox.setTextToShowWhenEmpty ("Password", Colours::black.withAlpha (0.2f)); | ||||
| passwordBox.setPasswordCharacter ((juce_wchar) 0x2022); | passwordBox.setPasswordCharacter ((juce_wchar) 0x2022); | ||||
| passwordBox.setJustification (Justification::centredLeft); | passwordBox.setJustification (Justification::centredLeft); | ||||
| passwordBox.onReturnKey = [this] { submitDetails(); }; | passwordBox.onReturnKey = [this] { submitDetails(); }; | ||||
| passwordBox.setTitle ("Password"); | |||||
| addAndMakeVisible (logInButton); | addAndMakeVisible (logInButton); | ||||
| logInButton.onClick = [this] { submitDetails(); }; | logInButton.onClick = [this] { submitDetails(); }; | ||||
| @@ -72,6 +77,7 @@ public: | |||||
| dismissButton.setShape (getLookAndFeel().getCrossShape (1.0f), false, true, false); | dismissButton.setShape (getLookAndFeel().getCrossShape (1.0f), false, true, false); | ||||
| addAndMakeVisible (dismissButton); | addAndMakeVisible (dismissButton); | ||||
| dismissButton.onClick = [this] { mainWindow.hideLoginFormOverlay(); }; | dismissButton.onClick = [this] { mainWindow.hideLoginFormOverlay(); }; | ||||
| dismissButton.setTitle ("Dismiss"); | |||||
| setWantsKeyboardFocus (true); | setWantsKeyboardFocus (true); | ||||
| setOpaque (true); | setOpaque (true); | ||||
| @@ -127,9 +127,9 @@ public: | |||||
| PropertyComponent* jucePathPropertyComponent = nullptr; | PropertyComponent* jucePathPropertyComponent = nullptr; | ||||
| for (auto* prop : propertyGroup.properties) | |||||
| for (const auto& prop : propertyGroup.getProperties()) | |||||
| if (prop->getName() == "Path to JUCE") | if (prop->getName() == "Path to JUCE") | ||||
| jucePathPropertyComponent = prop; | |||||
| jucePathPropertyComponent = prop.get(); | |||||
| if (jucePathPropertyComponent != nullptr) | if (jucePathPropertyComponent != nullptr) | ||||
| { | { | ||||
| @@ -487,6 +487,10 @@ void MainWindow::showLoginFormOverlay() | |||||
| { | { | ||||
| blurOverlayComponent = std::make_unique<BlurOverlayWithComponent> (*this, std::make_unique<LoginFormComponent> (*this)); | blurOverlayComponent = std::make_unique<BlurOverlayWithComponent> (*this, std::make_unique<LoginFormComponent> (*this)); | ||||
| loginFormOpen = true; | loginFormOpen = true; | ||||
| if (auto* loginForm = blurOverlayComponent->getChildComponent (0)) | |||||
| if (auto* handler = loginForm->getAccessibilityHandler()) | |||||
| handler->grabFocus(); | |||||
| } | } | ||||
| void MainWindow::hideLoginFormOverlay() | void MainWindow::hideLoginFormOverlay() | ||||
| @@ -708,17 +708,16 @@ public: | |||||
| } | } | ||||
| }; | }; | ||||
| Component* createEditor() override | |||||
| std::unique_ptr<Component> createEditor() override | |||||
| { | { | ||||
| SourceCodeEditor* e = nullptr; | |||||
| if (fileNeedsCppSyntaxHighlighting (getFile())) | |||||
| e = new SourceCodeEditor (this, new LiveBuildCodeEditor (*this, getCodeDocument())); | |||||
| else | |||||
| e = new SourceCodeEditor (this, getCodeDocument()); | |||||
| auto e = fileNeedsCppSyntaxHighlighting (getFile()) ? std::make_unique<SourceCodeEditor> (this, new LiveBuildCodeEditor (*this, getCodeDocument())) | |||||
| : std::make_unique<SourceCodeEditor> (this, getCodeDocument()); | |||||
| applyLastState (*(e->editor)); | applyLastState (*(e->editor)); | ||||
| return e; | |||||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wredundant-move") | |||||
| return std::move (e); | |||||
| JUCE_END_IGNORE_WARNINGS_GCC_LIKE | |||||
| } | } | ||||
| // override save() to make a few more attempts at saving if it fails, since on Windows | // override save() to make a few more attempts at saving if it fails, since on Windows | ||||
| @@ -59,8 +59,8 @@ public: | |||||
| void reloadFromFile() override { fileModificationTime = file.getLastModificationTime(); } | void reloadFromFile() override { fileModificationTime = file.getLastModificationTime(); } | ||||
| String getName() const override { return file.getFileName(); } | String getName() const override { return file.getFileName(); } | ||||
| File getFile() const override { return file; } | File getFile() const override { return file; } | ||||
| Component* createEditor() override { return new ItemPreviewComponent (file); } | |||||
| Component* createViewer() override { return createEditor(); } | |||||
| std::unique_ptr<Component> createEditor() override { return std::make_unique<ItemPreviewComponent> (file); } | |||||
| std::unique_ptr<Component> createViewer() override { return createEditor(); } | |||||
| void fileHasBeenRenamed (const File& newFile) override { file = newFile; } | void fileHasBeenRenamed (const File& newFile) override { file = newFile; } | ||||
| String getState() const override { return {}; } | String getState() const override { return {}; } | ||||
| void restoreState (const String&) override {} | void restoreState (const String&) override {} | ||||
| @@ -55,8 +55,8 @@ public: | |||||
| virtual bool saveAs() = 0; | virtual bool saveAs() = 0; | ||||
| virtual bool hasFileBeenModifiedExternally() = 0; | virtual bool hasFileBeenModifiedExternally() = 0; | ||||
| virtual void reloadFromFile() = 0; | virtual void reloadFromFile() = 0; | ||||
| virtual Component* createEditor() = 0; | |||||
| virtual Component* createViewer() = 0; | |||||
| virtual std::unique_ptr<Component> createEditor() = 0; | |||||
| virtual std::unique_ptr<Component> createViewer() = 0; | |||||
| virtual void fileHasBeenRenamed (const File& newFile) = 0; | virtual void fileHasBeenRenamed (const File& newFile) = 0; | ||||
| virtual String getState() const = 0; | virtual String getState() const = 0; | ||||
| virtual void restoreState (const String& state) = 0; | virtual void restoreState (const String& state) = 0; | ||||
| @@ -45,11 +45,14 @@ CodeDocument& SourceCodeDocument::getCodeDocument() | |||||
| return *codeDoc; | return *codeDoc; | ||||
| } | } | ||||
| Component* SourceCodeDocument::createEditor() | |||||
| std::unique_ptr<Component> SourceCodeDocument::createEditor() | |||||
| { | { | ||||
| auto* e = new SourceCodeEditor (this, getCodeDocument()); | |||||
| auto e = std::make_unique<SourceCodeEditor> (this, getCodeDocument()); | |||||
| applyLastState (*(e->editor)); | applyLastState (*(e->editor)); | ||||
| return e; | |||||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wredundant-move") | |||||
| return std::move (e); | |||||
| JUCE_END_IGNORE_WARNINGS_GCC_LIKE | |||||
| } | } | ||||
| void SourceCodeDocument::reloadFromFile() | void SourceCodeDocument::reloadFromFile() | ||||
| @@ -380,7 +383,7 @@ public: | |||||
| addAndMakeVisible (findNext); | addAndMakeVisible (findNext); | ||||
| setWantsKeyboardFocus (false); | setWantsKeyboardFocus (false); | ||||
| setFocusContainer (true); | |||||
| setFocusContainerType (FocusContainerType::keyboardFocusContainer); | |||||
| findPrev.setWantsKeyboardFocus (false); | findPrev.setWantsKeyboardFocus (false); | ||||
| findNext.setWantsKeyboardFocus (false); | findNext.setWantsKeyboardFocus (false); | ||||
| @@ -83,8 +83,8 @@ public: | |||||
| bool save() override; | bool save() override; | ||||
| bool saveAs() override; | bool saveAs() override; | ||||
| Component* createEditor() override; | |||||
| Component* createViewer() override { return createEditor(); } | |||||
| std::unique_ptr<Component> createEditor() override; | |||||
| std::unique_ptr<Component> createViewer() override { return createEditor(); } | |||||
| void updateLastState (CodeEditorComponent&); | void updateLastState (CodeEditorComponent&); | ||||
| void applyLastState (CodeEditorComponent&) const; | void applyLastState (CodeEditorComponent&) const; | ||||
| @@ -41,7 +41,7 @@ public: | |||||
| { | { | ||||
| setInterceptsMouseClicks (false, false); | setInterceptsMouseClicks (false, false); | ||||
| setWantsKeyboardFocus (false); | setWantsKeyboardFocus (false); | ||||
| setFocusContainer (true); | |||||
| setFocusContainerType (FocusContainerType::keyboardFocusContainer); | |||||
| } | } | ||||
| void paint (Graphics& g) override | void paint (Graphics& g) override | ||||
| @@ -254,7 +254,7 @@ void ComponentLayoutEditor::refreshAllComponents() | |||||
| lastComp = c; | lastComp = c; | ||||
| c->setWantsKeyboardFocus (false); | c->setWantsKeyboardFocus (false); | ||||
| c->setFocusContainer (true); | |||||
| c->setFocusContainerType (FocusContainerType::keyboardFocusContainer); | |||||
| if (isNewOverlay) | if (isNewOverlay) | ||||
| overlay->updateBoundsToMatchTarget(); | overlay->updateBoundsToMatchTarget(); | ||||
| @@ -713,14 +713,14 @@ public: | |||||
| return false; | return false; | ||||
| } | } | ||||
| Component* createEditor() override | |||||
| std::unique_ptr<Component> createEditor() override | |||||
| { | { | ||||
| if (ProjucerApplication::getApp().isGUIEditorEnabled()) | if (ProjucerApplication::getApp().isGUIEditorEnabled()) | ||||
| { | { | ||||
| std::unique_ptr<JucerDocument> jucerDoc (JucerDocument::createForCppFile (getProject(), getFile())); | std::unique_ptr<JucerDocument> jucerDoc (JucerDocument::createForCppFile (getProject(), getFile())); | ||||
| if (jucerDoc != nullptr) | if (jucerDoc != nullptr) | ||||
| return new JucerDocumentEditor (jucerDoc.release()); | |||||
| return std::make_unique<JucerDocumentEditor> (jucerDoc.release()); | |||||
| } | } | ||||
| return SourceCodeDocument::createEditor(); | return SourceCodeDocument::createEditor(); | ||||
| @@ -158,15 +158,19 @@ private: | |||||
| bool mightContainSubItems() override { return false; } | bool mightContainSubItems() override { return false; } | ||||
| String getUniqueName() const override { return comp.getName(); } | String getUniqueName() const override { return comp.getName(); } | ||||
| int getRightHandButtonSpace() override { return canBeLaunched() ? 60 : 40; } | int getRightHandButtonSpace() override { return canBeLaunched() ? 60 : 40; } | ||||
| Component* createItemComponent() override | |||||
| std::unique_ptr<Component> createItemComponent() override | |||||
| { | { | ||||
| auto* content = new TreeItemComponent (*this); | |||||
| auto content = std::make_unique<TreeItemComponent> (*this); | |||||
| content->addRightHandButton (new ClassItemButton (*this, true)); | content->addRightHandButton (new ClassItemButton (*this, true)); | ||||
| if (canBeLaunched()) | if (canBeLaunched()) | ||||
| content->addRightHandButton (new ClassItemButton (*this, false)); | content->addRightHandButton (new ClassItemButton (*this, false)); | ||||
| return content; | |||||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wredundant-move") | |||||
| return std::move (content); | |||||
| JUCE_END_IGNORE_WARNINGS_GCC_LIKE | |||||
| } | } | ||||
| Colour getContentColour (bool isIcon) const override | Colour getContentColour (bool isIcon) const override | ||||
| @@ -274,11 +274,11 @@ private: | |||||
| : defaultTextColourId); | : defaultTextColourId); | ||||
| } | } | ||||
| void showPopupMenu() override | |||||
| void showPopupMenu (Point<int> p) override | |||||
| { | { | ||||
| PopupMenu menu; | PopupMenu menu; | ||||
| menu.addItem (1, "Copy"); | menu.addItem (1, "Copy"); | ||||
| launchPopupMenu (menu); | |||||
| launchPopupMenu (menu, p); | |||||
| } | } | ||||
| void handlePopupMenuResult (int resultCode) override | void handlePopupMenuResult (int resultCode) override | ||||
| @@ -294,17 +294,12 @@ private: | |||||
| SourceCodeEditor* getEditor() | SourceCodeEditor* getEditor() | ||||
| { | { | ||||
| if (ProjectContentComponent* pcc = getProjectContentComponent()) | |||||
| if (auto* pcc = getProjectContentComponent()) | |||||
| { | { | ||||
| const File file (File::createFileWithoutCheckingPath (message.range.file)); | |||||
| const auto file = File::createFileWithoutCheckingPath (message.range.file); | |||||
| if (message.range.isValid() && file.exists() && pcc->showEditorForFile (file, false)) | if (message.range.isValid() && file.exists() && pcc->showEditorForFile (file, false)) | ||||
| { | |||||
| if (SourceCodeEditor* ed = dynamic_cast<SourceCodeEditor*> (pcc->getEditorComponent())) | |||||
| { | |||||
| return ed; | |||||
| } | |||||
| } | |||||
| return dynamic_cast<SourceCodeEditor*> (pcc->getEditorComponent()); | |||||
| } | } | ||||
| return nullptr; | return nullptr; | ||||
| @@ -337,7 +332,7 @@ private: | |||||
| if (ProjectContentComponent* pcc = getProjectContentComponent()) | if (ProjectContentComponent* pcc = getProjectContentComponent()) | ||||
| { | { | ||||
| if (SourceCodeEditor* ed = dynamic_cast<SourceCodeEditor*> (pcc->getEditorComponent())) | |||||
| if (auto* ed = dynamic_cast<SourceCodeEditor*> (pcc->getEditorComponent())) | |||||
| { | { | ||||
| auto start = CodeDocument::Position (ed->editor->getDocument(), message.range.range.getStart()); | auto start = CodeDocument::Position (ed->editor->getDocument(), message.range.range.getStart()); | ||||
| auto end = CodeDocument::Position (ed->editor->getDocument(), message.range.range.getEnd()); | auto end = CodeDocument::Position (ed->editor->getDocument(), message.range.range.getEnd()); | ||||
| @@ -93,7 +93,7 @@ public: | |||||
| addSubItem (new ConfigItem (config.config, *exporter)); | addSubItem (new ConfigItem (config.config, *exporter)); | ||||
| } | } | ||||
| void showPopupMenu() override | |||||
| void showPopupMenu (Point<int> p) override | |||||
| { | { | ||||
| PopupMenu menu; | PopupMenu menu; | ||||
| menu.addItem (1, "Add a new configuration", exporter->supportsUserDefinedConfigurations()); | menu.addItem (1, "Add a new configuration", exporter->supportsUserDefinedConfigurations()); | ||||
| @@ -101,15 +101,15 @@ public: | |||||
| menu.addSeparator(); | menu.addSeparator(); | ||||
| menu.addItem (3, "Delete this exporter"); | menu.addItem (3, "Delete this exporter"); | ||||
| launchPopupMenu (menu); | |||||
| launchPopupMenu (menu, p); | |||||
| } | } | ||||
| void showAddMenu() override | |||||
| void showAddMenu (Point<int> p) override | |||||
| { | { | ||||
| PopupMenu menu; | PopupMenu menu; | ||||
| menu.addItem (1, "Add a new configuration", exporter->supportsUserDefinedConfigurations()); | menu.addItem (1, "Add a new configuration", exporter->supportsUserDefinedConfigurations()); | ||||
| launchPopupMenu (menu); | |||||
| launchPopupMenu (menu, p); | |||||
| } | } | ||||
| void handlePopupMenuResult (int resultCode) override | void handlePopupMenuResult (int resultCode) override | ||||
| @@ -239,7 +239,7 @@ public: | |||||
| } | } | ||||
| } | } | ||||
| void showPopupMenu() override | |||||
| void showPopupMenu (Point<int> p) override | |||||
| { | { | ||||
| bool enabled = exporter.supportsUserDefinedConfigurations(); | bool enabled = exporter.supportsUserDefinedConfigurations(); | ||||
| @@ -248,7 +248,7 @@ public: | |||||
| menu.addSeparator(); | menu.addSeparator(); | ||||
| menu.addItem (2, "Delete this configuration", enabled); | menu.addItem (2, "Delete this configuration", enabled); | ||||
| launchPopupMenu (menu); | |||||
| launchPopupMenu (menu, p); | |||||
| } | } | ||||
| void handlePopupMenuResult (int resultCode) override | void handlePopupMenuResult (int resultCode) override | ||||
| @@ -320,7 +320,7 @@ public: | |||||
| void setName (const String&) override {} | void setName (const String&) override {} | ||||
| Icon getIcon() const override { return project.getMainGroup().getIcon (isOpen()).withColour (getContentColour (true)); } | Icon getIcon() const override { return project.getMainGroup().getIcon (isOpen()).withColour (getContentColour (true)); } | ||||
| void showPopupMenu() override | |||||
| void showPopupMenu (Point<int>) override | |||||
| { | { | ||||
| if (auto* pcc = getProjectContentComponent()) | if (auto* pcc = getProjectContentComponent()) | ||||
| pcc->showNewExporterMenu(); | pcc->showNewExporterMenu(); | ||||
| @@ -131,7 +131,7 @@ public: | |||||
| { | { | ||||
| if (auto* pcc = treeRootItem->getProjectContentComponent()) | if (auto* pcc = treeRootItem->getProjectContentComponent()) | ||||
| { | { | ||||
| if (auto* fileInfoComp = dynamic_cast<FileGroupInformationComponent*> (pcc->getEditorComponentContent())) | |||||
| if (auto* fileInfoComp = dynamic_cast<FileGroupInformationComponent*> (pcc->getEditorComponent())) | |||||
| if (fileInfoComp->getGroupPath() == itemToRemove->getFile().getFullPathName()) | if (fileInfoComp->getGroupPath() == itemToRemove->getFile().getFullPathName()) | ||||
| pcc->hideEditor(); | pcc->hideEditor(); | ||||
| } | } | ||||
| @@ -197,12 +197,12 @@ public: | |||||
| jassertfalse; | jassertfalse; | ||||
| } | } | ||||
| void showMultiSelectionPopupMenu() override | |||||
| void showMultiSelectionPopupMenu (Point<int> p) override | |||||
| { | { | ||||
| PopupMenu m; | PopupMenu m; | ||||
| m.addItem (1, "Delete"); | m.addItem (1, "Delete"); | ||||
| m.showMenuAsync (PopupMenu::Options(), | |||||
| m.showMenuAsync (PopupMenu::Options().withTargetScreenArea ({ p.x, p.y, 1, 1 }), | |||||
| ModalCallbackFunction::create (treeViewMultiSelectItemChosen, this)); | ModalCallbackFunction::create (treeViewMultiSelectItemChosen, this)); | ||||
| } | } | ||||
| @@ -548,7 +548,7 @@ public: | |||||
| pcc->showEditorForFile (f, false); | pcc->showEditorForFile (f, false); | ||||
| } | } | ||||
| void showPopupMenu() override | |||||
| void showPopupMenu (Point<int> p) override | |||||
| { | { | ||||
| PopupMenu m; | PopupMenu m; | ||||
| @@ -571,13 +571,13 @@ public: | |||||
| m.addItem (3, "Delete"); | m.addItem (3, "Delete"); | ||||
| launchPopupMenu (m); | |||||
| launchPopupMenu (m, p); | |||||
| } | } | ||||
| void showAddMenu() override | |||||
| void showAddMenu (Point<int> p) override | |||||
| { | { | ||||
| if (auto* group = dynamic_cast<GroupItem*> (getParentItem())) | if (auto* group = dynamic_cast<GroupItem*> (getParentItem())) | ||||
| group->showAddMenu(); | |||||
| group->showAddMenu (p); | |||||
| } | } | ||||
| void handlePopupMenuResult (int resultCode) override | void handlePopupMenuResult (int resultCode) override | ||||
| @@ -696,7 +696,7 @@ public: | |||||
| void showDocument() override | void showDocument() override | ||||
| { | { | ||||
| if (auto* pcc = getProjectContentComponent()) | if (auto* pcc = getProjectContentComponent()) | ||||
| pcc->setEditorComponent (new FileGroupInformationComponent (item), nullptr); | |||||
| pcc->setScrollableEditorComponent (std::make_unique<FileGroupInformationComponent> (item)); | |||||
| } | } | ||||
| static void openAllGroups (TreeViewItem* root) | static void openAllGroups (TreeViewItem* root) | ||||
| @@ -731,7 +731,7 @@ public: | |||||
| setFilesToCompile (projectItem.getChild (i), shouldCompile); | setFilesToCompile (projectItem.getChild (i), shouldCompile); | ||||
| } | } | ||||
| void showPopupMenu() override | |||||
| void showPopupMenu (Point<int> p) override | |||||
| { | { | ||||
| PopupMenu m; | PopupMenu m; | ||||
| addCreateFileMenuItems (m); | addCreateFileMenuItems (m); | ||||
| @@ -764,15 +764,15 @@ public: | |||||
| m.addItem (10, "Delete"); | m.addItem (10, "Delete"); | ||||
| } | } | ||||
| launchPopupMenu (m); | |||||
| launchPopupMenu (m, p); | |||||
| } | } | ||||
| void showAddMenu() override | |||||
| void showAddMenu (Point<int> p) override | |||||
| { | { | ||||
| PopupMenu m; | PopupMenu m; | ||||
| addCreateFileMenuItems (m); | addCreateFileMenuItems (m); | ||||
| launchPopupMenu (m); | |||||
| launchPopupMenu (m, p); | |||||
| } | } | ||||
| void handlePopupMenuResult (int resultCode) override | void handlePopupMenuResult (int resultCode) override | ||||
| @@ -78,17 +78,17 @@ public: | |||||
| return Icon (getIcons().singleModule, iconColour); | return Icon (getIcons().singleModule, iconColour); | ||||
| } | } | ||||
| void showAddMenu() override | |||||
| void showAddMenu (Point<int> p) override | |||||
| { | { | ||||
| if (auto* parent = dynamic_cast<EnabledModulesItem*> (getParentItem())) | if (auto* parent = dynamic_cast<EnabledModulesItem*> (getParentItem())) | ||||
| parent->showPopupMenu(); | |||||
| parent->showPopupMenu (p); | |||||
| } | } | ||||
| void showPopupMenu() override | |||||
| void showPopupMenu (Point<int> p) override | |||||
| { | { | ||||
| PopupMenu menu; | PopupMenu menu; | ||||
| menu.addItem (1, "Remove this module"); | menu.addItem (1, "Remove this module"); | ||||
| launchPopupMenu (menu); | |||||
| launchPopupMenu (menu, p); | |||||
| } | } | ||||
| void handlePopupMenuResult (int resultCode) override | void handlePopupMenuResult (int resultCode) override | ||||
| @@ -149,7 +149,7 @@ private: | |||||
| if (modules.doesModuleHaveHigherCppStandardThanProject (moduleID)) | if (modules.doesModuleHaveHigherCppStandardThanProject (moduleID)) | ||||
| props.add (new CppStandardWarningComponent()); | props.add (new CppStandardWarningComponent()); | ||||
| group.properties.clear(); | |||||
| group.clearProperties(); | |||||
| exporterModulePathDefaultValues.clear(); | exporterModulePathDefaultValues.clear(); | ||||
| exporterModulePathValues.clear(); | exporterModulePathValues.clear(); | ||||
| globalPathValues.clear(); | globalPathValues.clear(); | ||||
| @@ -471,7 +471,7 @@ public: | |||||
| void showDocument() override | void showDocument() override | ||||
| { | { | ||||
| if (auto* pcc = getProjectContentComponent()) | if (auto* pcc = getProjectContentComponent()) | ||||
| pcc->setEditorComponent (new ModulesInformationComponent (project), nullptr); | |||||
| pcc->setScrollableEditorComponent (std::make_unique<ModulesInformationComponent> (project)); | |||||
| } | } | ||||
| static File getModuleFolder (const File& draggedFile) | static File getModuleFolder (const File& draggedFile) | ||||
| @@ -515,7 +515,7 @@ public: | |||||
| addSubItem (new ModuleItem (project, project.getEnabledModules().getModuleID (i))); | addSubItem (new ModuleItem (project, project.getEnabledModules().getModuleID (i))); | ||||
| } | } | ||||
| void showPopupMenu() override | |||||
| void showPopupMenu (Point<int> p) override | |||||
| { | { | ||||
| auto& enabledModules = project.getEnabledModules(); | auto& enabledModules = project.getEnabledModules(); | ||||
| PopupMenu allModules; | PopupMenu allModules; | ||||
| @@ -563,7 +563,7 @@ public: | |||||
| menu.addSeparator(); | menu.addSeparator(); | ||||
| menu.addItem (1001, "Add a module from a specified folder..."); | menu.addItem (1001, "Add a module from a specified folder..."); | ||||
| launchPopupMenu (menu); | |||||
| launchPopupMenu (menu, p); | |||||
| } | } | ||||
| void handlePopupMenuResult (int resultCode) override | void handlePopupMenuResult (int resultCode) override | ||||
| @@ -35,6 +35,9 @@ struct ProjectSettingsComponent : public Component, | |||||
| group (project.getProjectFilenameRootString(), | group (project.getProjectFilenameRootString(), | ||||
| Icon (getIcons().settings, Colours::transparentBlack)) | Icon (getIcons().settings, Colours::transparentBlack)) | ||||
| { | { | ||||
| setTitle ("Project Settings"); | |||||
| setFocusContainerType (FocusContainerType::focusContainer); | |||||
| addAndMakeVisible (group); | addAndMakeVisible (group); | ||||
| updatePropertyList(); | updatePropertyList(); | ||||
| @@ -238,16 +241,34 @@ private: | |||||
| headers.clear(); | headers.clear(); | ||||
| if (project != nullptr) | |||||
| auto addPanel = [this] (const String& name, | |||||
| TreePanelBase* tree, | |||||
| ConcertinaTreeComponent::AdditionalComponents components, | |||||
| const Path& icon) | |||||
| { | { | ||||
| concertinaPanel.addPanel (0, new ConcertinaTreeComponent (new FileTreePanel (*project), true, false, true), true); | |||||
| concertinaPanel.addPanel (1, new ConcertinaTreeComponent (new ModuleTreePanel (*project), true, true), true); | |||||
| concertinaPanel.addPanel (2, new ConcertinaTreeComponent (new ExportersTreePanel (*project), true), true); | |||||
| } | |||||
| if (project != nullptr) | |||||
| concertinaPanel.addPanel (-1, new ConcertinaTreeComponent (name, tree, components), true); | |||||
| headers.add (new ConcertinaHeader (name, icon)); | |||||
| }; | |||||
| using AdditionalComponents = ConcertinaTreeComponent::AdditionalComponents; | |||||
| addPanel ("File Explorer", new FileTreePanel (*project), | |||||
| AdditionalComponents{} | |||||
| .with (AdditionalComponents::addButton) | |||||
| .with (AdditionalComponents::findPanel), | |||||
| getIcons().fileExplorer); | |||||
| addPanel ("Modules", new ModuleTreePanel (*project), | |||||
| AdditionalComponents{} | |||||
| .with (AdditionalComponents::addButton) | |||||
| .with (AdditionalComponents::settingsButton), | |||||
| getIcons().modules); | |||||
| headers.add (new ConcertinaHeader ("File explorer", getIcons().fileExplorer)); | |||||
| headers.add (new ConcertinaHeader ("Modules", getIcons().modules)); | |||||
| headers.add (new ConcertinaHeader ("Exporters", getIcons().exporter)); | |||||
| addPanel ("Exporters", new ExportersTreePanel (*project), | |||||
| AdditionalComponents{}.with (AdditionalComponents::addButton), | |||||
| getIcons().exporter); | |||||
| for (int i = 0; i < concertinaPanel.getNumPanels(); ++i) | for (int i = 0; i < concertinaPanel.getNumPanels(); ++i) | ||||
| { | { | ||||
| @@ -35,17 +35,16 @@ struct ProjectTreeItemBase : public JucerTreeViewBase, | |||||
| void showSettingsPage (Component* content) | void showSettingsPage (Component* content) | ||||
| { | { | ||||
| content->setComponentID (getUniqueName()); | content->setComponentID (getUniqueName()); | ||||
| std::unique_ptr<Component> comp (content); | std::unique_ptr<Component> comp (content); | ||||
| if (ProjectContentComponent* pcc = getProjectContentComponent()) | |||||
| pcc->setEditorComponent (comp.release(), nullptr); | |||||
| if (auto* pcc = getProjectContentComponent()) | |||||
| pcc->setScrollableEditorComponent (std::move (comp)); | |||||
| } | } | ||||
| void closeSettingsPage() | void closeSettingsPage() | ||||
| { | { | ||||
| if (auto* pcc = getProjectContentComponent()) | if (auto* pcc = getProjectContentComponent()) | ||||
| if (auto* content = pcc->getEditorComponentContent()) | |||||
| if (auto* content = pcc->getEditorComponent()) | |||||
| if (content->getComponentID() == getUniqueName()) | if (content->getComponentID() == getUniqueName()) | ||||
| pcc->hideEditor(); | pcc->hideEditor(); | ||||
| } | } | ||||
| @@ -34,11 +34,14 @@ public: | |||||
| ConcertinaHeader (String n, Path p) | ConcertinaHeader (String n, Path p) | ||||
| : Component (n), name (n), iconPath (p) | : Component (n), name (n), iconPath (p) | ||||
| { | { | ||||
| setTitle (getName()); | |||||
| panelIcon = Icon (iconPath, Colours::white); | panelIcon = Icon (iconPath, Colours::white); | ||||
| nameLabel.setText (name, dontSendNotification); | nameLabel.setText (name, dontSendNotification); | ||||
| nameLabel.setJustificationType (Justification::centredLeft); | nameLabel.setJustificationType (Justification::centredLeft); | ||||
| nameLabel.setInterceptsMouseClicks (false, false); | nameLabel.setInterceptsMouseClicks (false, false); | ||||
| nameLabel.setAccessible (false); | |||||
| nameLabel.setColour (Label::textColourId, Colours::white); | nameLabel.setColour (Label::textColourId, Colours::white); | ||||
| addAndMakeVisible (nameLabel); | addAndMakeVisible (nameLabel); | ||||
| @@ -72,6 +75,14 @@ public: | |||||
| sendChangeMessage(); | sendChangeMessage(); | ||||
| } | } | ||||
| std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override | |||||
| { | |||||
| return std::make_unique<AccessibilityHandler> (*this, | |||||
| AccessibilityRole::button, | |||||
| AccessibilityActions().addAction (AccessibilityActionType::press, | |||||
| [this] { sendChangeMessage(); })); | |||||
| } | |||||
| int direction = 0; | int direction = 0; | ||||
| int yPosition = 0; | int yPosition = 0; | ||||
| @@ -166,25 +177,57 @@ private: | |||||
| class ConcertinaTreeComponent : public Component | class ConcertinaTreeComponent : public Component | ||||
| { | { | ||||
| public: | public: | ||||
| ConcertinaTreeComponent (TreePanelBase* tree, bool hasAddButton = false, | |||||
| bool hasSettingsButton = false, bool hasFindPanel = false) | |||||
| : treeToDisplay (tree) | |||||
| class AdditionalComponents | |||||
| { | { | ||||
| if (hasAddButton) | |||||
| public: | |||||
| enum Type | |||||
| { | |||||
| addButton = (1 << 0), | |||||
| settingsButton = (1 << 1), | |||||
| findPanel = (1 << 2) | |||||
| }; | |||||
| AdditionalComponents with (Type t) | |||||
| { | |||||
| auto copy = *this; | |||||
| copy.componentTypes |= t; | |||||
| return copy; | |||||
| } | |||||
| bool has (Type t) const noexcept | |||||
| { | |||||
| return (componentTypes & t) != 0; | |||||
| } | |||||
| private: | |||||
| int componentTypes = 0; | |||||
| }; | |||||
| ConcertinaTreeComponent (const String& name, | |||||
| TreePanelBase* tree, | |||||
| AdditionalComponents additionalComponents) | |||||
| : Component (name), | |||||
| treeToDisplay (tree) | |||||
| { | |||||
| setTitle (getName()); | |||||
| setFocusContainerType (FocusContainerType::focusContainer); | |||||
| if (additionalComponents.has (AdditionalComponents::addButton)) | |||||
| { | { | ||||
| addButton = std::make_unique<IconButton> ("Add", getIcons().plus); | addButton = std::make_unique<IconButton> ("Add", getIcons().plus); | ||||
| addAndMakeVisible (addButton.get()); | addAndMakeVisible (addButton.get()); | ||||
| addButton->onClick = [this] { showAddMenu(); }; | addButton->onClick = [this] { showAddMenu(); }; | ||||
| } | } | ||||
| if (hasSettingsButton) | |||||
| if (additionalComponents.has (AdditionalComponents::settingsButton)) | |||||
| { | { | ||||
| settingsButton = std::make_unique<IconButton> ("Settings", getIcons().settings); | settingsButton = std::make_unique<IconButton> ("Settings", getIcons().settings); | ||||
| addAndMakeVisible (settingsButton.get()); | addAndMakeVisible (settingsButton.get()); | ||||
| settingsButton->onClick = [this] { showSettings(); }; | settingsButton->onClick = [this] { showSettings(); }; | ||||
| } | } | ||||
| if (hasFindPanel) | |||||
| if (additionalComponents.has (AdditionalComponents::findPanel)) | |||||
| { | { | ||||
| findPanel = std::make_unique<FindPanel> ([this] (const String& filter) { treeToDisplay->rootItem->setSearchFilter (filter); }); | findPanel = std::make_unique<FindPanel> ([this] (const String& filter) { treeToDisplay->rootItem->setSearchFilter (filter); }); | ||||
| addAndMakeVisible (findPanel.get()); | addAndMakeVisible (findPanel.get()); | ||||
| @@ -232,12 +275,12 @@ private: | |||||
| if (numSelected == 0) | if (numSelected == 0) | ||||
| { | { | ||||
| if (auto* root = dynamic_cast<JucerTreeViewBase*> (treeToDisplay->tree.getRootItem())) | if (auto* root = dynamic_cast<JucerTreeViewBase*> (treeToDisplay->tree.getRootItem())) | ||||
| root->showPopupMenu(); | |||||
| root->showPopupMenu (addButton->getScreenBounds().getCentre()); | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| if (auto* item = dynamic_cast<JucerTreeViewBase*> (treeToDisplay->tree.getSelectedItem (0))) | if (auto* item = dynamic_cast<JucerTreeViewBase*> (treeToDisplay->tree.getSelectedItem (0))) | ||||
| item->showAddMenu(); | |||||
| item->showAddMenu (addButton->getScreenBounds().getCentre()); | |||||
| } | } | ||||
| } | } | ||||
| @@ -0,0 +1,112 @@ | |||||
| /* | |||||
| ============================================================================== | |||||
| This file is part of the JUCE library. | |||||
| Copyright (c) 2020 - Raw Material Software Limited | |||||
| JUCE is an open source library subject to commercial or open-source | |||||
| licensing. | |||||
| By using JUCE, you agree to the terms of both the JUCE 6 End-User License | |||||
| Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). | |||||
| End User License Agreement: www.juce.com/juce-6-licence | |||||
| Privacy Policy: www.juce.com/juce-privacy-policy | |||||
| Or: You may also use this code under the terms of the GPL v3 (see | |||||
| www.gnu.org/licenses). | |||||
| 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 ContentViewComponent : public Component | |||||
| { | |||||
| public: | |||||
| ContentViewComponent() | |||||
| { | |||||
| setTitle ("Content"); | |||||
| setFocusContainerType (Component::FocusContainerType::focusContainer); | |||||
| addAndMakeVisible (logoComponent); | |||||
| addAndMakeVisible (fileNameLabel); | |||||
| fileNameLabel.setJustificationType (Justification::centred); | |||||
| } | |||||
| void resized() override | |||||
| { | |||||
| auto bounds = getLocalBounds(); | |||||
| fileNameLabel.setBounds (bounds.removeFromTop (15)); | |||||
| if (content != nullptr) | |||||
| content->setBounds (bounds); | |||||
| else | |||||
| logoComponent.setBounds (bounds); | |||||
| } | |||||
| Component* getCurrentComponent() noexcept | |||||
| { | |||||
| return content.get(); | |||||
| } | |||||
| void setContent (std::unique_ptr<Component> newContent, | |||||
| const String& labelText) | |||||
| { | |||||
| content = std::move (newContent); | |||||
| addAndMakeVisible (content.get()); | |||||
| fileNameLabel.setVisible (labelText.isNotEmpty()); | |||||
| fileNameLabel.setText (labelText, dontSendNotification); | |||||
| resized(); | |||||
| } | |||||
| private: | |||||
| class LogoComponent : public Component | |||||
| { | |||||
| public: | |||||
| void paint (Graphics& g) override | |||||
| { | |||||
| g.setColour (findColour (defaultTextColourId)); | |||||
| auto bounds = getLocalBounds(); | |||||
| bounds.reduce (bounds.getWidth() / 6, bounds.getHeight() / 6); | |||||
| g.setFont (15.0f); | |||||
| g.drawFittedText (versionInfo, bounds.removeFromBottom (50), Justification::centredBottom, 3); | |||||
| if (logo != nullptr) | |||||
| logo->drawWithin (g, bounds.withTrimmedBottom (bounds.getHeight() / 4).toFloat(), | |||||
| RectanglePlacement (RectanglePlacement::centred), 1.0f); | |||||
| } | |||||
| private: | |||||
| std::unique_ptr<Drawable> logo = []() -> std::unique_ptr<Drawable> | |||||
| { | |||||
| if (auto svg = parseXML (BinaryData::background_logo_svg)) | |||||
| return Drawable::createFromSVG (*svg); | |||||
| jassertfalse; | |||||
| return {}; | |||||
| }(); | |||||
| String versionInfo = SystemStats::getJUCEVersion() | |||||
| + newLine | |||||
| + ProjucerApplication::getApp().getVersionDescription(); | |||||
| }; | |||||
| std::unique_ptr<Component> content; | |||||
| LogoComponent logoComponent; | |||||
| Label fileNameLabel; | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ContentViewComponent) | |||||
| }; | |||||
| @@ -25,6 +25,7 @@ | |||||
| #pragma once | #pragma once | ||||
| #include "../../Utility/UI/PropertyComponents/jucer_LabelPropertyComponent.h" | |||||
| //============================================================================== | //============================================================================== | ||||
| struct ContentViewHeader : public Component | struct ContentViewHeader : public Component | ||||
| @@ -32,6 +33,7 @@ struct ContentViewHeader : public Component | |||||
| ContentViewHeader (String headerName, Icon headerIcon) | ContentViewHeader (String headerName, Icon headerIcon) | ||||
| : name (headerName), icon (headerIcon) | : name (headerName), icon (headerIcon) | ||||
| { | { | ||||
| setTitle (name); | |||||
| } | } | ||||
| void paint (Graphics& g) override | void paint (Graphics& g) override | ||||
| @@ -152,14 +154,18 @@ private: | |||||
| }; | }; | ||||
| //============================================================================== | //============================================================================== | ||||
| class InfoButton : public Button | |||||
| class InfoButton : public Button | |||||
| { | { | ||||
| public: | public: | ||||
| InfoButton (const String& infoToDisplay = {}) | InfoButton (const String& infoToDisplay = {}) | ||||
| : Button ({}) | : Button ({}) | ||||
| { | { | ||||
| setTitle ("Info"); | |||||
| if (infoToDisplay.isNotEmpty()) | if (infoToDisplay.isNotEmpty()) | ||||
| setInfoToDisplay (infoToDisplay); | setInfoToDisplay (infoToDisplay); | ||||
| setSize (20, 20); | |||||
| } | } | ||||
| void paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown) override | void paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown) override | ||||
| @@ -196,6 +202,8 @@ public: | |||||
| width = jmin (300, stringWidth); | width = jmin (300, stringWidth); | ||||
| numLines += static_cast<int> (stringWidth / width); | numLines += static_cast<int> (stringWidth / width); | ||||
| setHelpText (info); | |||||
| } | } | ||||
| } | } | ||||
| @@ -242,37 +250,48 @@ public: | |||||
| description (desc) | description (desc) | ||||
| { | { | ||||
| addAndMakeVisible (header); | addAndMakeVisible (header); | ||||
| description.setFont ({ 16.0f }); | |||||
| description.setColour (getLookAndFeel().findColour (defaultTextColourId)); | |||||
| description.setLineSpacing (5.0f); | |||||
| description.setJustification (Justification::centredLeft); | |||||
| } | } | ||||
| void setProperties (const PropertyListBuilder& newProps) | void setProperties (const PropertyListBuilder& newProps) | ||||
| { | { | ||||
| infoButtons.clear(); | |||||
| properties.clear(); | |||||
| properties.addArray (newProps.components); | |||||
| clearProperties(); | |||||
| for (auto* prop : properties) | |||||
| if (description.isNotEmpty()) | |||||
| properties.push_back (std::make_unique<LabelPropertyComponent> (description, 16, Font (16.0f), | |||||
| Justification::centredLeft)); | |||||
| for (auto* comp : newProps.components) | |||||
| properties.push_back (std::unique_ptr<PropertyComponent> (comp)); | |||||
| for (auto& prop : properties) | |||||
| { | { | ||||
| addAndMakeVisible (prop); | |||||
| const auto propertyTooltip = prop->getTooltip(); | |||||
| if (propertyTooltip.isNotEmpty()) | |||||
| { | |||||
| // set the tooltip to empty so it only displays when its button is clicked | |||||
| prop->setTooltip ({}); | |||||
| auto infoButton = std::make_unique<InfoButton> (propertyTooltip); | |||||
| infoButton->setAssociatedComponent (prop.get()); | |||||
| if (! prop->getTooltip().isEmpty()) | |||||
| auto propertyAndInfoWrapper = std::make_unique<PropertyAndInfoWrapper> (*prop, *infoButton.get()); | |||||
| addAndMakeVisible (propertyAndInfoWrapper.get()); | |||||
| propertyComponentsWithInfo.push_back (std::move (propertyAndInfoWrapper)); | |||||
| infoButtons.push_back (std::move (infoButton)); | |||||
| } | |||||
| else | |||||
| { | { | ||||
| addAndMakeVisible (infoButtons.add (new InfoButton (prop->getTooltip()))); | |||||
| infoButtons.getLast()->setAssociatedComponent (prop); | |||||
| prop->setTooltip ({}); // set the tooltip to empty so it only displays when its button is clicked | |||||
| addAndMakeVisible (prop.get()); | |||||
| } | } | ||||
| if (auto* multiChoice = dynamic_cast<MultiChoicePropertyComponent*> (prop)) | |||||
| if (auto* multiChoice = dynamic_cast<MultiChoicePropertyComponent*> (prop.get())) | |||||
| multiChoice->onHeightChange = [this] { updateSize(); }; | multiChoice->onHeightChange = [this] { updateSize(); }; | ||||
| if (auto* text = dynamic_cast<TextPropertyComponent*> (prop)) | |||||
| if (auto* text = dynamic_cast<TextPropertyComponent*> (prop.get())) | |||||
| if (text->isTextEditorMultiLine()) | if (text->isTextEditorMultiLine()) | ||||
| text->addListener (this); | text->addListener (this); | ||||
| } | } | ||||
| } | } | ||||
| @@ -281,31 +300,21 @@ public: | |||||
| header.setBounds (0, 0, width, headerSize); | header.setBounds (0, 0, width, headerSize); | ||||
| auto height = header.getBottom() + 10; | auto height = header.getBottom() + 10; | ||||
| descriptionLayout.createLayout (description, (float) (width - 40)); | |||||
| auto descriptionHeight = (int) descriptionLayout.getHeight(); | |||||
| if (descriptionHeight > 0) | |||||
| height += (int) descriptionLayout.getHeight() + 25; | |||||
| for (auto* pp : properties) | |||||
| for (auto& pp : properties) | |||||
| { | { | ||||
| auto propertyHeight = pp->getPreferredHeight() + (getHeightMultiplier (pp) * pp->getPreferredHeight()); | |||||
| const auto propertyHeight = pp->getPreferredHeight() | |||||
| + (getHeightMultiplier (pp.get()) * pp->getPreferredHeight()); | |||||
| InfoButton* buttonToUse = nullptr; | |||||
| for (auto* b : infoButtons) | |||||
| if (b->getAssociatedComponent() == pp) | |||||
| buttonToUse = b; | |||||
| auto iter = std::find_if (propertyComponentsWithInfo.begin(), propertyComponentsWithInfo.end(), | |||||
| [&pp] (const std::unique_ptr<PropertyAndInfoWrapper>& w) { return &w->propertyComponent == pp.get(); }); | |||||
| if (buttonToUse != nullptr) | |||||
| { | |||||
| buttonToUse->setSize (20, 20); | |||||
| buttonToUse->setCentrePosition (20, height + (propertyHeight / 2)); | |||||
| } | |||||
| pp->setBounds (40, height, width - 50, propertyHeight); | |||||
| if (iter != propertyComponentsWithInfo.end()) | |||||
| (*iter)->setBounds (0, height, width - 10, propertyHeight); | |||||
| else | |||||
| pp->setBounds (40, height, width - 50, propertyHeight); | |||||
| if (shouldResizePropertyComponent (pp)) | |||||
| resizePropertyComponent (pp); | |||||
| if (shouldResizePropertyComponent (pp.get())) | |||||
| resizePropertyComponent (pp.get()); | |||||
| height += pp->getHeight() + 10; | height += pp->getHeight() + 10; | ||||
| } | } | ||||
| @@ -319,19 +328,51 @@ public: | |||||
| void paint (Graphics& g) override | void paint (Graphics& g) override | ||||
| { | { | ||||
| g.setColour (findColour (secondaryBackgroundColourId)); | |||||
| g.fillRect (getLocalBounds()); | |||||
| auto textArea = getLocalBounds().toFloat() | |||||
| .withTop ((float) headerSize) | |||||
| .reduced (20.0f, 10.0f) | |||||
| .withHeight (descriptionLayout.getHeight()); | |||||
| descriptionLayout.draw (g, textArea); | |||||
| g.fillAll (findColour (secondaryBackgroundColourId)); | |||||
| } | |||||
| const std::vector<std::unique_ptr<PropertyComponent>>& getProperties() const noexcept | |||||
| { | |||||
| return properties; | |||||
| } | } | ||||
| OwnedArray<PropertyComponent> properties; | |||||
| void clearProperties() | |||||
| { | |||||
| propertyComponentsWithInfo.clear(); | |||||
| infoButtons.clear(); | |||||
| properties.clear(); | |||||
| } | |||||
| private: | private: | ||||
| //============================================================================== | |||||
| struct PropertyAndInfoWrapper : public Component | |||||
| { | |||||
| PropertyAndInfoWrapper (PropertyComponent& c, InfoButton& i) | |||||
| : propertyComponent (c), | |||||
| infoButton (i) | |||||
| { | |||||
| setFocusContainerType (FocusContainerType::focusContainer); | |||||
| setTitle (propertyComponent.getName()); | |||||
| addAndMakeVisible (propertyComponent); | |||||
| addAndMakeVisible (infoButton); | |||||
| } | |||||
| void resized() override | |||||
| { | |||||
| auto bounds = getLocalBounds(); | |||||
| bounds.removeFromLeft (40); | |||||
| bounds.removeFromRight (10); | |||||
| propertyComponent.setBounds (bounds); | |||||
| infoButton.setCentrePosition (20, bounds.getHeight() / 2); | |||||
| } | |||||
| PropertyComponent& propertyComponent; | |||||
| InfoButton& infoButton; | |||||
| }; | |||||
| //============================================================================== | //============================================================================== | ||||
| void textPropertyComponentChanged (TextPropertyComponent* comp) override | void textPropertyComponentChanged (TextPropertyComponent* comp) override | ||||
| { | { | ||||
| @@ -348,7 +389,6 @@ private: | |||||
| updateSize(); | updateSize(); | ||||
| } | } | ||||
| //============================================================================== | |||||
| void updateSize() | void updateSize() | ||||
| { | { | ||||
| updateSize (getX(), getY(), getWidth()); | updateSize (getX(), getY(), getWidth()); | ||||
| @@ -357,7 +397,6 @@ private: | |||||
| parent->parentSizeChanged(); | parent->parentSizeChanged(); | ||||
| } | } | ||||
| //============================================================================== | |||||
| bool shouldResizePropertyComponent (PropertyComponent* p) | bool shouldResizePropertyComponent (PropertyComponent* p) | ||||
| { | { | ||||
| if (auto* textComp = dynamic_cast<TextPropertyComponent*> (p)) | if (auto* textComp = dynamic_cast<TextPropertyComponent*> (p)) | ||||
| @@ -392,11 +431,15 @@ private: | |||||
| return static_cast<int> (nameWidth / (float) availableTextWidth); | return static_cast<int> (nameWidth / (float) availableTextWidth); | ||||
| } | } | ||||
| OwnedArray<InfoButton> infoButtons; | |||||
| //============================================================================== | |||||
| static constexpr int headerSize = 40; | |||||
| std::vector<std::unique_ptr<PropertyComponent>> properties; | |||||
| std::vector<std::unique_ptr<InfoButton>> infoButtons; | |||||
| std::vector<std::unique_ptr<PropertyAndInfoWrapper>> propertyComponentsWithInfo; | |||||
| ContentViewHeader header; | ContentViewHeader header; | ||||
| AttributedString description; | |||||
| TextLayout descriptionLayout; | |||||
| int headerSize = 40; | |||||
| String description; | |||||
| //============================================================================== | //============================================================================== | ||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PropertyGroupComponent) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PropertyGroupComponent) | ||||
| @@ -40,6 +40,9 @@ | |||||
| HeaderComponent::HeaderComponent (ProjectContentComponent* pcc) | HeaderComponent::HeaderComponent (ProjectContentComponent* pcc) | ||||
| : projectContentComponent (pcc) | : projectContentComponent (pcc) | ||||
| { | { | ||||
| setTitle ("Header"); | |||||
| setFocusContainerType (FocusContainerType::focusContainer); | |||||
| addAndMakeVisible (configLabel); | addAndMakeVisible (configLabel); | ||||
| addAndMakeVisible (exporterBox); | addAndMakeVisible (exporterBox); | ||||
| @@ -34,63 +34,23 @@ | |||||
| NewFileWizard::Type* createGUIComponentWizard(); | NewFileWizard::Type* createGUIComponentWizard(); | ||||
| //============================================================================== | |||||
| ProjectContentComponent::LogoComponent::LogoComponent() | |||||
| { | |||||
| if (auto svg = parseXML (BinaryData::background_logo_svg)) | |||||
| logo = Drawable::createFromSVG (*svg); | |||||
| } | |||||
| void ProjectContentComponent::LogoComponent::paint (Graphics& g) | |||||
| { | |||||
| g.setColour (findColour (defaultTextColourId)); | |||||
| auto r = getLocalBounds(); | |||||
| g.setFont (15.0f); | |||||
| g.drawFittedText (getVersionInfo(), r.removeFromBottom (50), Justification::centredBottom, 3); | |||||
| logo->drawWithin (g, r.withTrimmedBottom (r.getHeight() / 4).toFloat(), | |||||
| RectanglePlacement (RectanglePlacement::centred), 1.0f); | |||||
| } | |||||
| String ProjectContentComponent::LogoComponent::getVersionInfo() | |||||
| { | |||||
| return SystemStats::getJUCEVersion() | |||||
| + newLine | |||||
| + ProjucerApplication::getApp().getVersionDescription(); | |||||
| } | |||||
| //============================================================================== | |||||
| ProjectContentComponent::ContentViewport::ContentViewport (Component* content) | |||||
| { | |||||
| addAndMakeVisible (viewport); | |||||
| viewport.setViewedComponent (content, true); | |||||
| } | |||||
| void ProjectContentComponent::ContentViewport::resized() | |||||
| { | |||||
| viewport.setBounds (getLocalBounds()); | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| ProjectContentComponent::ProjectContentComponent() | ProjectContentComponent::ProjectContentComponent() | ||||
| { | { | ||||
| setOpaque (true); | setOpaque (true); | ||||
| setWantsKeyboardFocus (true); | setWantsKeyboardFocus (true); | ||||
| addAndMakeVisible (logoComponent); | |||||
| addAndMakeVisible (headerComponent); | addAndMakeVisible (headerComponent); | ||||
| addAndMakeVisible (projectMessagesComponent); | addAndMakeVisible (projectMessagesComponent); | ||||
| addAndMakeVisible (fileNameLabel); | |||||
| fileNameLabel.setJustificationType (Justification::centred); | |||||
| addAndMakeVisible (contentViewComponent); | |||||
| sidebarSizeConstrainer.setMinimumWidth (200); | sidebarSizeConstrainer.setMinimumWidth (200); | ||||
| sidebarSizeConstrainer.setMaximumWidth (500); | sidebarSizeConstrainer.setMaximumWidth (500); | ||||
| sidebarTabs.setOutline (0); | sidebarTabs.setOutline (0); | ||||
| sidebarTabs.getTabbedButtonBar().setMinimumTabScaleFactor (0.5); | sidebarTabs.getTabbedButtonBar().setMinimumTabScaleFactor (0.5); | ||||
| sidebarTabs.setTitle ("Sidebar"); | |||||
| sidebarTabs.setFocusContainerType (FocusContainerType::focusContainer); | |||||
| ProjucerApplication::getApp().openDocumentManager.addListener (this); | ProjucerApplication::getApp().openDocumentManager.addListener (this); | ||||
| @@ -140,15 +100,9 @@ void ProjectContentComponent::resized() | |||||
| if (resizerBar != nullptr) | if (resizerBar != nullptr) | ||||
| resizerBar->setBounds (r.withWidth (4)); | resizerBar->setBounds (r.withWidth (4)); | ||||
| headerComponent.sidebarTabsWidthChanged (sidebarTabs.getWidth()); | |||||
| if (contentView != nullptr) | |||||
| { | |||||
| fileNameLabel.setBounds (r.removeFromTop (15)); | |||||
| contentView->setBounds (r); | |||||
| } | |||||
| contentViewComponent.setBounds (r); | |||||
| logoComponent.setBounds (r.reduced (r.getWidth() / 6, r.getHeight() / 6)); | |||||
| headerComponent.sidebarTabsWidthChanged (sidebarTabs.getWidth()); | |||||
| } | } | ||||
| void ProjectContentComponent::lookAndFeelChanged() | void ProjectContentComponent::lookAndFeelChanged() | ||||
| @@ -175,8 +129,8 @@ void ProjectContentComponent::setProject (Project* newProject) | |||||
| if (project != nullptr) | if (project != nullptr) | ||||
| project->removeChangeListener (this); | project->removeChangeListener (this); | ||||
| contentView.reset(); | |||||
| resizerBar.reset(); | |||||
| hideEditor(); | |||||
| resizerBar = nullptr; | |||||
| deleteProjectTabs(); | deleteProjectTabs(); | ||||
| project = newProject; | project = newProject; | ||||
| @@ -360,16 +314,12 @@ void ProjectContentComponent::updateMissingFileStatuses() | |||||
| tree->updateMissingFileStatuses(); | tree->updateMissingFileStatuses(); | ||||
| } | } | ||||
| bool ProjectContentComponent::showEditorForFile (const File& f, bool grabFocus) | |||||
| bool ProjectContentComponent::showEditorForFile (const File& fileToShow, bool grabFocus) | |||||
| { | { | ||||
| if (getCurrentFile() == f | |||||
| || showDocument (ProjucerApplication::getApp().openDocumentManager.openFile (project, f), grabFocus)) | |||||
| { | |||||
| fileNameLabel.setText (f.getFileName(), dontSendNotification); | |||||
| return true; | |||||
| } | |||||
| if (getCurrentFile() != fileToShow) | |||||
| return showDocument (ProjucerApplication::getApp().openDocumentManager.openFile (project, fileToShow), grabFocus); | |||||
| return false; | |||||
| return true; | |||||
| } | } | ||||
| bool ProjectContentComponent::hasFileInRecentList (const File& f) const | bool ProjectContentComponent::hasFileInRecentList (const File& f) const | ||||
| @@ -391,30 +341,22 @@ bool ProjectContentComponent::showDocument (OpenDocumentManager::Document* doc, | |||||
| if (doc->hasFileBeenModifiedExternally()) | if (doc->hasFileBeenModifiedExternally()) | ||||
| doc->reloadFromFile(); | doc->reloadFromFile(); | ||||
| if (doc == getCurrentDocument() && contentView != nullptr) | |||||
| if (doc != getCurrentDocument()) | |||||
| { | { | ||||
| if (grabFocus) | |||||
| contentView->grabKeyboardFocus(); | |||||
| return true; | |||||
| recentDocumentList.newDocumentOpened (doc); | |||||
| setEditorDocument (doc->createEditor(), doc); | |||||
| } | } | ||||
| recentDocumentList.newDocumentOpened (doc); | |||||
| auto opened = setEditorComponent (doc->createEditor(), doc); | |||||
| if (opened && grabFocus && isShowing()) | |||||
| contentView->grabKeyboardFocus(); | |||||
| if (grabFocus) | |||||
| contentViewComponent.grabKeyboardFocus(); | |||||
| return opened; | |||||
| return true; | |||||
| } | } | ||||
| void ProjectContentComponent::hideEditor() | void ProjectContentComponent::hideEditor() | ||||
| { | { | ||||
| currentDocument = nullptr; | currentDocument = nullptr; | ||||
| contentView.reset(); | |||||
| fileNameLabel.setVisible (false); | |||||
| contentViewComponent.setContent ({}, {}); | |||||
| ProjucerApplication::getCommandManager().commandStatusChanged(); | ProjucerApplication::getCommandManager().commandStatusChanged(); | ||||
| resized(); | resized(); | ||||
| @@ -422,68 +364,69 @@ void ProjectContentComponent::hideEditor() | |||||
| void ProjectContentComponent::hideDocument (OpenDocumentManager::Document* doc) | void ProjectContentComponent::hideDocument (OpenDocumentManager::Document* doc) | ||||
| { | { | ||||
| if (doc == currentDocument) | |||||
| { | |||||
| if (auto* replacement = recentDocumentList.getClosestPreviousDocOtherThan (doc)) | |||||
| showDocument (replacement, true); | |||||
| else | |||||
| hideEditor(); | |||||
| } | |||||
| if (doc != currentDocument) | |||||
| return; | |||||
| if (auto* replacement = recentDocumentList.getClosestPreviousDocOtherThan (currentDocument)) | |||||
| showDocument (replacement, true); | |||||
| else | |||||
| hideEditor(); | |||||
| } | } | ||||
| bool ProjectContentComponent::setEditorComponent (Component* editor, | |||||
| OpenDocumentManager::Document* doc) | |||||
| void ProjectContentComponent::setScrollableEditorComponent (std::unique_ptr<Component> component) | |||||
| { | { | ||||
| if (editor != nullptr) | |||||
| { | |||||
| contentView.reset(); | |||||
| jassert (component.get() != nullptr); | |||||
| if (doc == nullptr) | |||||
| class ContentViewport : public Component | |||||
| { | |||||
| public: | |||||
| ContentViewport (std::unique_ptr<Component> content) | |||||
| { | { | ||||
| auto* viewport = new ContentViewport (editor); | |||||
| contentView.reset (viewport); | |||||
| currentDocument = nullptr; | |||||
| fileNameLabel.setVisible (false); | |||||
| addAndMakeVisible (viewport); | |||||
| contentViewport.setViewedComponent (content.release(), true); | |||||
| addAndMakeVisible (contentViewport); | |||||
| } | } | ||||
| else | |||||
| { | |||||
| contentView.reset (editor); | |||||
| currentDocument = doc; | |||||
| fileNameLabel.setText (doc->getFile().getFileName(), dontSendNotification); | |||||
| fileNameLabel.setVisible (true); | |||||
| addAndMakeVisible (editor); | |||||
| void resized() override | |||||
| { | |||||
| contentViewport.setBounds (getLocalBounds()); | |||||
| } | } | ||||
| resized(); | |||||
| private: | |||||
| Viewport contentViewport; | |||||
| }; | |||||
| ProjucerApplication::getCommandManager().commandStatusChanged(); | |||||
| return true; | |||||
| } | |||||
| contentViewComponent.setContent (std::make_unique<ContentViewport> (std::move (component)), {}); | |||||
| currentDocument = nullptr; | |||||
| return false; | |||||
| ProjucerApplication::getCommandManager().commandStatusChanged(); | |||||
| } | } | ||||
| Component* ProjectContentComponent::getEditorComponentContent() const | |||||
| void ProjectContentComponent::setEditorDocument (std::unique_ptr<Component> component, OpenDocumentManager::Document* doc) | |||||
| { | { | ||||
| if (contentView != nullptr) | |||||
| if (auto* vp = dynamic_cast<ContentViewport*> (contentView.get())) | |||||
| return vp->viewport.getViewedComponent(); | |||||
| currentDocument = doc; | |||||
| contentViewComponent.setContent (std::move (component), | |||||
| currentDocument != nullptr ? currentDocument->getFile().getFileName() | |||||
| : String()); | |||||
| return nullptr; | |||||
| ProjucerApplication::getCommandManager().commandStatusChanged(); | |||||
| } | |||||
| Component* ProjectContentComponent::getEditorComponent() | |||||
| { | |||||
| return contentViewComponent.getCurrentComponent(); | |||||
| } | } | ||||
| void ProjectContentComponent::closeDocument() | void ProjectContentComponent::closeDocument() | ||||
| { | { | ||||
| if (currentDocument != nullptr) | if (currentDocument != nullptr) | ||||
| { | |||||
| ProjucerApplication::getApp().openDocumentManager | ProjucerApplication::getApp().openDocumentManager | ||||
| .closeDocument (currentDocument, OpenDocumentManager::SaveIfNeeded::yes); | .closeDocument (currentDocument, OpenDocumentManager::SaveIfNeeded::yes); | ||||
| else if (contentView != nullptr) | |||||
| if (! goToPreviousFile()) | |||||
| hideEditor(); | |||||
| return; | |||||
| } | |||||
| if (! goToPreviousFile()) | |||||
| hideEditor(); | |||||
| } | } | ||||
| static void showSaveWarning (OpenDocumentManager::Document* currentDocument) | static void showSaveWarning (OpenDocumentManager::Document* currentDocument) | ||||
| @@ -570,7 +513,7 @@ void ProjectContentComponent::closeProject() | |||||
| void ProjectContentComponent::showProjectSettings() | void ProjectContentComponent::showProjectSettings() | ||||
| { | { | ||||
| setEditorComponent (new ProjectSettingsComponent (*project), nullptr); | |||||
| setScrollableEditorComponent (std::make_unique<ProjectSettingsComponent> (*project)); | |||||
| } | } | ||||
| void ProjectContentComponent::showCurrentExporterSettings() | void ProjectContentComponent::showCurrentExporterSettings() | ||||
| @@ -634,7 +577,7 @@ void ProjectContentComponent::showModule (const String& moduleID) | |||||
| void ProjectContentComponent::showLiveBuildSettings() | void ProjectContentComponent::showLiveBuildSettings() | ||||
| { | { | ||||
| setEditorComponent (new LiveBuildSettingsComponent (*project), nullptr); | |||||
| setScrollableEditorComponent (std::make_unique<LiveBuildSettingsComponent> (*project)); | |||||
| } | } | ||||
| StringArray ProjectContentComponent::getExportersWhichCanLaunch() const | StringArray ProjectContentComponent::getExportersWhichCanLaunch() const | ||||
| @@ -851,7 +794,7 @@ void ProjectContentComponent::getCommandInfo (const CommandID commandID, Applica | |||||
| result.setInfo ("Close" + documentName, | result.setInfo ("Close" + documentName, | ||||
| "Closes the current document", | "Closes the current document", | ||||
| CommandCategories::general, 0); | CommandCategories::general, 0); | ||||
| result.setActive (contentView != nullptr); | |||||
| result.setActive (currentDocument != nullptr); | |||||
| result.defaultKeypresses.add ({ 'w', cmdCtrl, 0 }); | result.defaultKeypresses.add ({ 'w', cmdCtrl, 0 }); | ||||
| break; | break; | ||||
| @@ -28,6 +28,7 @@ | |||||
| #include "../../CodeEditor/jucer_OpenDocumentManager.h" | #include "../../CodeEditor/jucer_OpenDocumentManager.h" | ||||
| #include "jucer_HeaderComponent.h" | #include "jucer_HeaderComponent.h" | ||||
| #include "jucer_ProjectMessagesComponent.h" | #include "jucer_ProjectMessagesComponent.h" | ||||
| #include "jucer_ContentViewComponent.h" | |||||
| class CompileEngineChildProcess; | class CompileEngineChildProcess; | ||||
| class ProjectTab; | class ProjectTab; | ||||
| @@ -65,10 +66,11 @@ public: | |||||
| void saveAs(); | void saveAs(); | ||||
| void hideEditor(); | void hideEditor(); | ||||
| bool setEditorComponent (Component* editor, OpenDocumentManager::Document* doc); | |||||
| Component* getEditorComponentContent() const; | |||||
| Component* getEditorComponent() const { return contentView.get(); } | |||||
| Component& getSidebarComponent() { return sidebarTabs; } | |||||
| void setScrollableEditorComponent (std::unique_ptr<Component> component); | |||||
| void setEditorDocument (std::unique_ptr<Component> component, OpenDocumentManager::Document* doc); | |||||
| Component* getEditorComponent(); | |||||
| Component& getSidebarComponent() { return sidebarTabs; } | |||||
| bool goToPreviousFile(); | bool goToPreviousFile(); | ||||
| bool goToNextFile(); | bool goToNextFile(); | ||||
| @@ -144,26 +146,6 @@ public: | |||||
| static String getBuildTabName() { return "Build"; } | static String getBuildTabName() { return "Build"; } | ||||
| private: | private: | ||||
| //============================================================================== | |||||
| struct LogoComponent : public Component | |||||
| { | |||||
| LogoComponent(); | |||||
| void paint (Graphics& g) override; | |||||
| static String getVersionInfo(); | |||||
| std::unique_ptr<Drawable> logo; | |||||
| }; | |||||
| struct ContentViewport : public Component | |||||
| { | |||||
| ContentViewport (Component* content); | |||||
| void resized() override; | |||||
| Viewport viewport; | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ContentViewport) | |||||
| }; | |||||
| //============================================================================== | //============================================================================== | ||||
| bool documentAboutToClose (OpenDocumentManager::Document*) override; | bool documentAboutToClose (OpenDocumentManager::Document*) override; | ||||
| void changeListenerCallback (ChangeBroadcaster*) override; | void changeListenerCallback (ChangeBroadcaster*) override; | ||||
| @@ -196,14 +178,14 @@ private: | |||||
| OpenDocumentManager::Document* currentDocument = nullptr; | OpenDocumentManager::Document* currentDocument = nullptr; | ||||
| RecentDocumentList recentDocumentList; | RecentDocumentList recentDocumentList; | ||||
| LogoComponent logoComponent; | |||||
| HeaderComponent headerComponent { this }; | HeaderComponent headerComponent { this }; | ||||
| TabbedComponent sidebarTabs { TabbedButtonBar::TabsAtTop }; | |||||
| ProjectMessagesComponent projectMessagesComponent; | ProjectMessagesComponent projectMessagesComponent; | ||||
| Label fileNameLabel; | |||||
| TabbedComponent sidebarTabs { TabbedButtonBar::TabsAtTop }; | |||||
| ContentViewComponent contentViewComponent; | |||||
| std::unique_ptr<ResizableEdgeComponent> resizerBar; | std::unique_ptr<ResizableEdgeComponent> resizerBar; | ||||
| ComponentBoundsConstrainer sidebarSizeConstrainer; | ComponentBoundsConstrainer sidebarSizeConstrainer; | ||||
| std::unique_ptr<Component> translationTool, contentView; | |||||
| std::unique_ptr<Component> translationTool; | |||||
| BubbleMessageComponent bubbleMessage; | BubbleMessageComponent bubbleMessage; | ||||
| ReferenceCountedObjectPtr<CompileEngineChildProcess> childProcess; | ReferenceCountedObjectPtr<CompileEngineChildProcess> childProcess; | ||||
| @@ -392,6 +392,9 @@ class ProjectMessagesComponent : public Component | |||||
| public: | public: | ||||
| ProjectMessagesComponent() | ProjectMessagesComponent() | ||||
| { | { | ||||
| setFocusContainerType (FocusContainerType::focusContainer); | |||||
| setTitle ("Project Messages"); | |||||
| addAndMakeVisible (warningsComponent); | addAndMakeVisible (warningsComponent); | ||||
| addAndMakeVisible (notificationsComponent); | addAndMakeVisible (notificationsComponent); | ||||
| @@ -445,8 +448,15 @@ public: | |||||
| isMouseDown = false; | isMouseDown = false; | ||||
| repaint(); | repaint(); | ||||
| if (messagesWindow != nullptr) | |||||
| showOrHideAllMessages (! messagesWindow->isListShowing()); | |||||
| showOrHideMessagesWindow(); | |||||
| } | |||||
| std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override | |||||
| { | |||||
| return std::make_unique<AccessibilityHandler> (*this, | |||||
| AccessibilityRole::button, | |||||
| AccessibilityActions().addAction (AccessibilityActionType::press, | |||||
| [this] { showOrHideMessagesWindow(); })); | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -475,6 +485,20 @@ public: | |||||
| } | } | ||||
| } | } | ||||
| void numMessagesChanged() | |||||
| { | |||||
| const auto total = warningsComponent.getNumMessages() | |||||
| + notificationsComponent.getNumMessages(); | |||||
| setHelpText (String (total) + (total == 1 ? " message" : " messages")); | |||||
| } | |||||
| void showOrHideMessagesWindow() | |||||
| { | |||||
| if (messagesWindow != nullptr) | |||||
| showOrHideAllMessages (! messagesWindow->isListShowing()); | |||||
| } | |||||
| private: | private: | ||||
| //============================================================================== | //============================================================================== | ||||
| struct MessageCountComponent : public Component, | struct MessageCountComponent : public Component, | ||||
| @@ -511,9 +535,12 @@ private: | |||||
| void updateNumMessages() | void updateNumMessages() | ||||
| { | { | ||||
| numMessages = messagesTree.getNumChildren(); | numMessages = messagesTree.getNumChildren(); | ||||
| owner.numMessagesChanged(); | |||||
| repaint(); | repaint(); | ||||
| } | } | ||||
| int getNumMessages() const noexcept { return numMessages; } | |||||
| private: | private: | ||||
| void valueTreeChildAdded (ValueTree&, ValueTree&) override { updateNumMessages(); } | void valueTreeChildAdded (ValueTree&, ValueTree&) override { updateNumMessages(); } | ||||
| void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override { updateNumMessages(); } | void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override { updateNumMessages(); } | ||||
| @@ -64,6 +64,11 @@ public: | |||||
| } | } | ||||
| void mouseUp (const MouseEvent&) override | void mouseUp (const MouseEvent&) override | ||||
| { | |||||
| triggerClick(); | |||||
| } | |||||
| void triggerClick() | |||||
| { | { | ||||
| if (interactive) | if (interactive) | ||||
| { | { | ||||
| @@ -76,6 +81,15 @@ public: | |||||
| bool isDisplaingGPLLogo() const noexcept { return isGPL; } | bool isDisplaingGPLLogo() const noexcept { return isGPL; } | ||||
| std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override | |||||
| { | |||||
| return interactive ? std::make_unique<AccessibilityHandler> (*this, | |||||
| AccessibilityRole::button, | |||||
| AccessibilityActions().addAction (AccessibilityActionType::press, | |||||
| [this] { triggerClick(); })) | |||||
| : nullptr; | |||||
| } | |||||
| private: | private: | ||||
| //============================================================================== | //============================================================================== | ||||
| static Image createGPLAvatarImage() | static Image createGPLAvatarImage() | ||||
| @@ -845,6 +845,17 @@ public: | |||||
| { | { | ||||
| auto extraCompilerFlags = owner.compilerFlagSchemesMap[projectItem.getCompilerFlagSchemeString()].get().toString(); | auto extraCompilerFlags = owner.compilerFlagSchemesMap[projectItem.getCompilerFlagSchemeString()].get().toString(); | ||||
| if (shouldAddBigobjFlag (path)) | |||||
| { | |||||
| const String bigobjFlag ("/bigobj"); | |||||
| if (! extraCompilerFlags.contains (bigobjFlag)) | |||||
| { | |||||
| extraCompilerFlags << " " << bigobjFlag; | |||||
| extraCompilerFlags.trim(); | |||||
| } | |||||
| } | |||||
| if (extraCompilerFlags.isNotEmpty()) | if (extraCompilerFlags.isNotEmpty()) | ||||
| e->createNewChildElement ("AdditionalOptions")->addTextElement (extraCompilerFlags + " %(AdditionalOptions)"); | e->createNewChildElement ("AdditionalOptions")->addTextElement (extraCompilerFlags + " %(AdditionalOptions)"); | ||||
| @@ -1729,6 +1740,11 @@ protected: | |||||
| return path.getFileNameWithoutExtension().startsWithIgnoreCase ("include_juce_audio_plugin_client_RTAS_"); | return path.getFileNameWithoutExtension().startsWithIgnoreCase ("include_juce_audio_plugin_client_RTAS_"); | ||||
| } | } | ||||
| static bool shouldAddBigobjFlag (const build_tools::RelativePath& path) | |||||
| { | |||||
| return path.getFileNameWithoutExtension().equalsIgnoreCase ("include_juce_gui_basics"); | |||||
| } | |||||
| StringArray getModuleLibs() const | StringArray getModuleLibs() const | ||||
| { | { | ||||
| StringArray result; | StringArray result; | ||||
| @@ -119,9 +119,9 @@ void JucerTreeViewBase::paintContent (Graphics& g, Rectangle<int> area) | |||||
| g.drawFittedText (getDisplayName(), area, Justification::centredLeft, 1, 1.0f); | g.drawFittedText (getDisplayName(), area, Justification::centredLeft, 1, 1.0f); | ||||
| } | } | ||||
| Component* JucerTreeViewBase::createItemComponent() | |||||
| std::unique_ptr<Component> JucerTreeViewBase::createItemComponent() | |||||
| { | { | ||||
| return new TreeItemComponent (*this); | |||||
| return std::make_unique<TreeItemComponent> (*this); | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -177,9 +177,9 @@ void JucerTreeViewBase::itemClicked (const MouseEvent& e) | |||||
| if (e.mods.isPopupMenu()) | if (e.mods.isPopupMenu()) | ||||
| { | { | ||||
| if (getOwnerView()->getNumSelectedItems() > 1) | if (getOwnerView()->getNumSelectedItems() > 1) | ||||
| showMultiSelectionPopupMenu(); | |||||
| showMultiSelectionPopupMenu (e.getMouseDownScreenPosition()); | |||||
| else | else | ||||
| showPopupMenu(); | |||||
| showPopupMenu (e.getMouseDownScreenPosition()); | |||||
| } | } | ||||
| else if (isSelected()) | else if (isSelected()) | ||||
| { | { | ||||
| @@ -187,29 +187,18 @@ void JucerTreeViewBase::itemClicked (const MouseEvent& e) | |||||
| } | } | ||||
| } | } | ||||
| void JucerTreeViewBase::deleteItem() {} | |||||
| void JucerTreeViewBase::deleteAllSelectedItems() {} | |||||
| void JucerTreeViewBase::showDocument() {} | |||||
| void JucerTreeViewBase::showPopupMenu() {} | |||||
| void JucerTreeViewBase::showAddMenu() {} | |||||
| void JucerTreeViewBase::showMultiSelectionPopupMenu() {} | |||||
| static void treeViewMenuItemChosen (int resultCode, WeakReference<JucerTreeViewBase> item) | static void treeViewMenuItemChosen (int resultCode, WeakReference<JucerTreeViewBase> item) | ||||
| { | { | ||||
| if (item != nullptr) | if (item != nullptr) | ||||
| item->handlePopupMenuResult (resultCode); | item->handlePopupMenuResult (resultCode); | ||||
| } | } | ||||
| void JucerTreeViewBase::launchPopupMenu (PopupMenu& m) | |||||
| void JucerTreeViewBase::launchPopupMenu (PopupMenu& m, Point<int> p) | |||||
| { | { | ||||
| m.showMenuAsync (PopupMenu::Options(), | |||||
| m.showMenuAsync (PopupMenu::Options().withTargetScreenArea ({ p.x, p.y, 1, 1 }), | |||||
| ModalCallbackFunction::create (treeViewMenuItemChosen, WeakReference<JucerTreeViewBase> (this))); | ModalCallbackFunction::create (treeViewMenuItemChosen, WeakReference<JucerTreeViewBase> (this))); | ||||
| } | } | ||||
| void JucerTreeViewBase::handlePopupMenuResult (int) | |||||
| { | |||||
| } | |||||
| ProjectContentComponent* JucerTreeViewBase::getProjectContentComponent() const | ProjectContentComponent* JucerTreeViewBase::getProjectContentComponent() const | ||||
| { | { | ||||
| for (Component* c = getOwnerView(); c != nullptr; c = c->getParentComponent()) | for (Component* c = getOwnerView(); c != nullptr; c = c->getParentComponent()) | ||||
| @@ -44,8 +44,9 @@ public: | |||||
| void itemClicked (const MouseEvent& e) override; | void itemClicked (const MouseEvent& e) override; | ||||
| void itemSelectionChanged (bool isNowSelected) override; | void itemSelectionChanged (bool isNowSelected) override; | ||||
| void itemDoubleClicked (const MouseEvent&) override; | void itemDoubleClicked (const MouseEvent&) override; | ||||
| Component* createItemComponent() override; | |||||
| String getTooltip() override { return {}; } | |||||
| std::unique_ptr<Component> createItemComponent() override; | |||||
| String getTooltip() override { return {}; } | |||||
| String getAccessibilityName() override { return getDisplayName(); } | |||||
| void cancelDelayedSelectionTimer(); | void cancelDelayedSelectionTimer(); | ||||
| @@ -67,17 +68,18 @@ public: | |||||
| virtual File getDraggableFile() const { return {}; } | virtual File getDraggableFile() const { return {}; } | ||||
| void refreshSubItems(); | void refreshSubItems(); | ||||
| virtual void deleteItem(); | |||||
| virtual void deleteAllSelectedItems(); | |||||
| virtual void showDocument(); | |||||
| virtual void showMultiSelectionPopupMenu(); | |||||
| virtual void showRenameBox(); | |||||
| void launchPopupMenu (PopupMenu&); // runs asynchronously, and produces a callback to handlePopupMenuResult(). | |||||
| virtual void showPopupMenu(); | |||||
| virtual void showAddMenu(); | |||||
| virtual void handlePopupMenuResult (int resultCode); | |||||
| virtual void setSearchFilter (const String&) {} | |||||
| void showRenameBox(); | |||||
| virtual void deleteItem() {} | |||||
| virtual void deleteAllSelectedItems() {} | |||||
| virtual void showDocument() {} | |||||
| virtual void showMultiSelectionPopupMenu (Point<int>) {} | |||||
| virtual void showPopupMenu (Point<int>) {} | |||||
| virtual void showAddMenu (Point<int>) {} | |||||
| virtual void handlePopupMenuResult (int) {} | |||||
| virtual void setSearchFilter (const String&) {} | |||||
| void launchPopupMenu (PopupMenu&, Point<int>); // runs asynchronously, and produces a callback to handlePopupMenuResult(). | |||||
| //============================================================================== | //============================================================================== | ||||
| // To handle situations where an item gets deleted before openness is | // To handle situations where an item gets deleted before openness is | ||||
| @@ -187,7 +189,7 @@ public: | |||||
| tree.clearSelectedItems(); | tree.clearSelectedItems(); | ||||
| if (e.mods.isRightButtonDown()) | if (e.mods.isRightButtonDown()) | ||||
| rootItem->showPopupMenu(); | |||||
| rootItem->showPopupMenu (e.getMouseDownScreenPosition()); | |||||
| } | } | ||||
| } | } | ||||
| @@ -205,6 +207,7 @@ class TreeItemComponent : public Component | |||||
| public: | public: | ||||
| TreeItemComponent (JucerTreeViewBase& i) : item (&i) | TreeItemComponent (JucerTreeViewBase& i) : item (&i) | ||||
| { | { | ||||
| setAccessible (false); | |||||
| setInterceptsMouseClicks (false, true); | setInterceptsMouseClicks (false, true); | ||||
| item->textX = iconWidth; | item->textX = iconWidth; | ||||
| } | } | ||||