| @@ -254,6 +254,9 @@ public: | |||
| header (metadata[Ids::name].toString(), metadata[Ids::description].toString(), BinaryData::background_logo_svg), | |||
| codeViewer (doc, &cppTokeniser) | |||
| { | |||
| setTitle (exampleFile.getFileName()); | |||
| setFocusContainerType (FocusContainerType::focusContainer); | |||
| addAndMakeVisible (header); | |||
| openExampleButton.onClick = [this] { exampleSelectedCallback (exampleFile); }; | |||
| @@ -286,6 +289,7 @@ private: | |||
| codeViewer.setScrollbarThickness (6); | |||
| codeViewer.setReadOnly (true); | |||
| codeViewer.setTitle ("Code"); | |||
| getAppSettings().appearance.applyToCodeEditor (codeViewer); | |||
| codeViewer.scrollToLine (findBestLineToScrollToForClass (StringArray::fromLines (fileString), | |||
| @@ -34,6 +34,12 @@ | |||
| //============================================================================== | |||
| struct ContentComponent : public Component | |||
| { | |||
| ContentComponent() | |||
| { | |||
| setTitle ("Content"); | |||
| setFocusContainerType (FocusContainerType::focusContainer); | |||
| } | |||
| void resized() override | |||
| { | |||
| 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)); | |||
| }; | |||
| return std::make_unique<StartPageTreeHolder> (exampleCategories, | |||
| return std::make_unique<StartPageTreeHolder> ("Examples", | |||
| exampleCategories, | |||
| examples, | |||
| std::move (selectedCallback), | |||
| 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))); | |||
| }; | |||
| auto holder = std::make_unique<StartPageTreeHolder> (categories, | |||
| auto holder = std::make_unique<StartPageTreeHolder> ("Templates", | |||
| categories, | |||
| templateNames, | |||
| std::move (selectedCallback), | |||
| StartPageTreeHolder::Open::yes); | |||
| @@ -165,7 +173,14 @@ struct ProjectTemplatesAndExamples : public TabbedComponent | |||
| content (c), | |||
| 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(); | |||
| } | |||
| @@ -177,8 +192,9 @@ struct ProjectTemplatesAndExamples : public TabbedComponent | |||
| 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); | |||
| if (wasOpen) | |||
| @@ -31,14 +31,18 @@ class StartPageTreeHolder : public Component | |||
| public: | |||
| 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), | |||
| items (itemNames), | |||
| itemSelectedCallback (std::move (selectedCallback)) | |||
| { | |||
| jassert (headers.size() == (int) items.size()); | |||
| tree.setTitle (title); | |||
| tree.setRootItem (new TreeRootItem (*this)); | |||
| tree.setRootItemVisible (false); | |||
| tree.setIndentSize (15); | |||
| @@ -88,13 +92,14 @@ private: | |||
| 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 | |||
| { | |||
| @@ -122,10 +127,13 @@ private: | |||
| g.drawFittedText (name, bounds.reduced (5).withTrimmedLeft (10), Justification::centredLeft, 1); | |||
| } | |||
| void itemClicked (const MouseEvent&) override | |||
| void itemClicked (const MouseEvent& e) override | |||
| { | |||
| if (isSelected()) | |||
| itemSelectionChanged (true); | |||
| if (e.mods.isPopupMenu() && mightContainSubItems()) | |||
| setOpen (! isOpen()); | |||
| } | |||
| void itemSelectionChanged (bool isNowSelected) override | |||
| @@ -35,16 +35,21 @@ public: | |||
| LoginFormComponent (MainWindow& window) | |||
| : mainWindow (window) | |||
| { | |||
| setTitle ("Login"); | |||
| setFocusContainerType (FocusContainerType::focusContainer); | |||
| addAndMakeVisible (emailBox); | |||
| emailBox.setTextToShowWhenEmpty ("Email", Colours::black.withAlpha (0.2f)); | |||
| emailBox.setJustification (Justification::centredLeft); | |||
| emailBox.onReturnKey = [this] { submitDetails(); }; | |||
| emailBox.setTitle ("Email"); | |||
| addAndMakeVisible (passwordBox); | |||
| passwordBox.setTextToShowWhenEmpty ("Password", Colours::black.withAlpha (0.2f)); | |||
| passwordBox.setPasswordCharacter ((juce_wchar) 0x2022); | |||
| passwordBox.setJustification (Justification::centredLeft); | |||
| passwordBox.onReturnKey = [this] { submitDetails(); }; | |||
| passwordBox.setTitle ("Password"); | |||
| addAndMakeVisible (logInButton); | |||
| logInButton.onClick = [this] { submitDetails(); }; | |||
| @@ -72,6 +77,7 @@ public: | |||
| dismissButton.setShape (getLookAndFeel().getCrossShape (1.0f), false, true, false); | |||
| addAndMakeVisible (dismissButton); | |||
| dismissButton.onClick = [this] { mainWindow.hideLoginFormOverlay(); }; | |||
| dismissButton.setTitle ("Dismiss"); | |||
| setWantsKeyboardFocus (true); | |||
| setOpaque (true); | |||
| @@ -127,9 +127,9 @@ public: | |||
| PropertyComponent* jucePathPropertyComponent = nullptr; | |||
| for (auto* prop : propertyGroup.properties) | |||
| for (const auto& prop : propertyGroup.getProperties()) | |||
| if (prop->getName() == "Path to JUCE") | |||
| jucePathPropertyComponent = prop; | |||
| jucePathPropertyComponent = prop.get(); | |||
| if (jucePathPropertyComponent != nullptr) | |||
| { | |||
| @@ -487,6 +487,10 @@ void MainWindow::showLoginFormOverlay() | |||
| { | |||
| blurOverlayComponent = std::make_unique<BlurOverlayWithComponent> (*this, std::make_unique<LoginFormComponent> (*this)); | |||
| loginFormOpen = true; | |||
| if (auto* loginForm = blurOverlayComponent->getChildComponent (0)) | |||
| if (auto* handler = loginForm->getAccessibilityHandler()) | |||
| handler->grabFocus(); | |||
| } | |||
| 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)); | |||
| 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 | |||
| @@ -59,8 +59,8 @@ public: | |||
| void reloadFromFile() override { fileModificationTime = file.getLastModificationTime(); } | |||
| String getName() const override { return file.getFileName(); } | |||
| 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; } | |||
| String getState() const override { return {}; } | |||
| void restoreState (const String&) override {} | |||
| @@ -55,8 +55,8 @@ public: | |||
| virtual bool saveAs() = 0; | |||
| virtual bool hasFileBeenModifiedExternally() = 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 String getState() const = 0; | |||
| virtual void restoreState (const String& state) = 0; | |||
| @@ -45,11 +45,14 @@ CodeDocument& SourceCodeDocument::getCodeDocument() | |||
| 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)); | |||
| return e; | |||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wredundant-move") | |||
| return std::move (e); | |||
| JUCE_END_IGNORE_WARNINGS_GCC_LIKE | |||
| } | |||
| void SourceCodeDocument::reloadFromFile() | |||
| @@ -380,7 +383,7 @@ public: | |||
| addAndMakeVisible (findNext); | |||
| setWantsKeyboardFocus (false); | |||
| setFocusContainer (true); | |||
| setFocusContainerType (FocusContainerType::keyboardFocusContainer); | |||
| findPrev.setWantsKeyboardFocus (false); | |||
| findNext.setWantsKeyboardFocus (false); | |||
| @@ -83,8 +83,8 @@ public: | |||
| bool save() 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 applyLastState (CodeEditorComponent&) const; | |||
| @@ -41,7 +41,7 @@ public: | |||
| { | |||
| setInterceptsMouseClicks (false, false); | |||
| setWantsKeyboardFocus (false); | |||
| setFocusContainer (true); | |||
| setFocusContainerType (FocusContainerType::keyboardFocusContainer); | |||
| } | |||
| void paint (Graphics& g) override | |||
| @@ -254,7 +254,7 @@ void ComponentLayoutEditor::refreshAllComponents() | |||
| lastComp = c; | |||
| c->setWantsKeyboardFocus (false); | |||
| c->setFocusContainer (true); | |||
| c->setFocusContainerType (FocusContainerType::keyboardFocusContainer); | |||
| if (isNewOverlay) | |||
| overlay->updateBoundsToMatchTarget(); | |||
| @@ -713,14 +713,14 @@ public: | |||
| return false; | |||
| } | |||
| Component* createEditor() override | |||
| std::unique_ptr<Component> createEditor() override | |||
| { | |||
| if (ProjucerApplication::getApp().isGUIEditorEnabled()) | |||
| { | |||
| std::unique_ptr<JucerDocument> jucerDoc (JucerDocument::createForCppFile (getProject(), getFile())); | |||
| if (jucerDoc != nullptr) | |||
| return new JucerDocumentEditor (jucerDoc.release()); | |||
| return std::make_unique<JucerDocumentEditor> (jucerDoc.release()); | |||
| } | |||
| return SourceCodeDocument::createEditor(); | |||
| @@ -158,15 +158,19 @@ private: | |||
| bool mightContainSubItems() override { return false; } | |||
| String getUniqueName() const override { return comp.getName(); } | |||
| 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)); | |||
| if (canBeLaunched()) | |||
| 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 | |||
| @@ -274,11 +274,11 @@ private: | |||
| : defaultTextColourId); | |||
| } | |||
| void showPopupMenu() override | |||
| void showPopupMenu (Point<int> p) override | |||
| { | |||
| PopupMenu menu; | |||
| menu.addItem (1, "Copy"); | |||
| launchPopupMenu (menu); | |||
| launchPopupMenu (menu, p); | |||
| } | |||
| void handlePopupMenuResult (int resultCode) override | |||
| @@ -294,17 +294,12 @@ private: | |||
| 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 (SourceCodeEditor* ed = dynamic_cast<SourceCodeEditor*> (pcc->getEditorComponent())) | |||
| { | |||
| return ed; | |||
| } | |||
| } | |||
| return dynamic_cast<SourceCodeEditor*> (pcc->getEditorComponent()); | |||
| } | |||
| return nullptr; | |||
| @@ -337,7 +332,7 @@ private: | |||
| 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 end = CodeDocument::Position (ed->editor->getDocument(), message.range.range.getEnd()); | |||
| @@ -93,7 +93,7 @@ public: | |||
| addSubItem (new ConfigItem (config.config, *exporter)); | |||
| } | |||
| void showPopupMenu() override | |||
| void showPopupMenu (Point<int> p) override | |||
| { | |||
| PopupMenu menu; | |||
| menu.addItem (1, "Add a new configuration", exporter->supportsUserDefinedConfigurations()); | |||
| @@ -101,15 +101,15 @@ public: | |||
| menu.addSeparator(); | |||
| menu.addItem (3, "Delete this exporter"); | |||
| launchPopupMenu (menu); | |||
| launchPopupMenu (menu, p); | |||
| } | |||
| void showAddMenu() override | |||
| void showAddMenu (Point<int> p) override | |||
| { | |||
| PopupMenu menu; | |||
| menu.addItem (1, "Add a new configuration", exporter->supportsUserDefinedConfigurations()); | |||
| launchPopupMenu (menu); | |||
| launchPopupMenu (menu, p); | |||
| } | |||
| void handlePopupMenuResult (int resultCode) override | |||
| @@ -239,7 +239,7 @@ public: | |||
| } | |||
| } | |||
| void showPopupMenu() override | |||
| void showPopupMenu (Point<int> p) override | |||
| { | |||
| bool enabled = exporter.supportsUserDefinedConfigurations(); | |||
| @@ -248,7 +248,7 @@ public: | |||
| menu.addSeparator(); | |||
| menu.addItem (2, "Delete this configuration", enabled); | |||
| launchPopupMenu (menu); | |||
| launchPopupMenu (menu, p); | |||
| } | |||
| void handlePopupMenuResult (int resultCode) override | |||
| @@ -320,7 +320,7 @@ public: | |||
| void setName (const String&) override {} | |||
| Icon getIcon() const override { return project.getMainGroup().getIcon (isOpen()).withColour (getContentColour (true)); } | |||
| void showPopupMenu() override | |||
| void showPopupMenu (Point<int>) override | |||
| { | |||
| if (auto* pcc = getProjectContentComponent()) | |||
| pcc->showNewExporterMenu(); | |||
| @@ -131,7 +131,7 @@ public: | |||
| { | |||
| 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()) | |||
| pcc->hideEditor(); | |||
| } | |||
| @@ -197,12 +197,12 @@ public: | |||
| jassertfalse; | |||
| } | |||
| void showMultiSelectionPopupMenu() override | |||
| void showMultiSelectionPopupMenu (Point<int> p) override | |||
| { | |||
| PopupMenu m; | |||
| m.addItem (1, "Delete"); | |||
| m.showMenuAsync (PopupMenu::Options(), | |||
| m.showMenuAsync (PopupMenu::Options().withTargetScreenArea ({ p.x, p.y, 1, 1 }), | |||
| ModalCallbackFunction::create (treeViewMultiSelectItemChosen, this)); | |||
| } | |||
| @@ -548,7 +548,7 @@ public: | |||
| pcc->showEditorForFile (f, false); | |||
| } | |||
| void showPopupMenu() override | |||
| void showPopupMenu (Point<int> p) override | |||
| { | |||
| PopupMenu m; | |||
| @@ -571,13 +571,13 @@ public: | |||
| 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())) | |||
| group->showAddMenu(); | |||
| group->showAddMenu (p); | |||
| } | |||
| void handlePopupMenuResult (int resultCode) override | |||
| @@ -696,7 +696,7 @@ public: | |||
| void showDocument() override | |||
| { | |||
| if (auto* pcc = getProjectContentComponent()) | |||
| pcc->setEditorComponent (new FileGroupInformationComponent (item), nullptr); | |||
| pcc->setScrollableEditorComponent (std::make_unique<FileGroupInformationComponent> (item)); | |||
| } | |||
| static void openAllGroups (TreeViewItem* root) | |||
| @@ -731,7 +731,7 @@ public: | |||
| setFilesToCompile (projectItem.getChild (i), shouldCompile); | |||
| } | |||
| void showPopupMenu() override | |||
| void showPopupMenu (Point<int> p) override | |||
| { | |||
| PopupMenu m; | |||
| addCreateFileMenuItems (m); | |||
| @@ -764,15 +764,15 @@ public: | |||
| m.addItem (10, "Delete"); | |||
| } | |||
| launchPopupMenu (m); | |||
| launchPopupMenu (m, p); | |||
| } | |||
| void showAddMenu() override | |||
| void showAddMenu (Point<int> p) override | |||
| { | |||
| PopupMenu m; | |||
| addCreateFileMenuItems (m); | |||
| launchPopupMenu (m); | |||
| launchPopupMenu (m, p); | |||
| } | |||
| void handlePopupMenuResult (int resultCode) override | |||
| @@ -78,17 +78,17 @@ public: | |||
| return Icon (getIcons().singleModule, iconColour); | |||
| } | |||
| void showAddMenu() override | |||
| void showAddMenu (Point<int> p) override | |||
| { | |||
| if (auto* parent = dynamic_cast<EnabledModulesItem*> (getParentItem())) | |||
| parent->showPopupMenu(); | |||
| parent->showPopupMenu (p); | |||
| } | |||
| void showPopupMenu() override | |||
| void showPopupMenu (Point<int> p) override | |||
| { | |||
| PopupMenu menu; | |||
| menu.addItem (1, "Remove this module"); | |||
| launchPopupMenu (menu); | |||
| launchPopupMenu (menu, p); | |||
| } | |||
| void handlePopupMenuResult (int resultCode) override | |||
| @@ -149,7 +149,7 @@ private: | |||
| if (modules.doesModuleHaveHigherCppStandardThanProject (moduleID)) | |||
| props.add (new CppStandardWarningComponent()); | |||
| group.properties.clear(); | |||
| group.clearProperties(); | |||
| exporterModulePathDefaultValues.clear(); | |||
| exporterModulePathValues.clear(); | |||
| globalPathValues.clear(); | |||
| @@ -471,7 +471,7 @@ public: | |||
| void showDocument() override | |||
| { | |||
| if (auto* pcc = getProjectContentComponent()) | |||
| pcc->setEditorComponent (new ModulesInformationComponent (project), nullptr); | |||
| pcc->setScrollableEditorComponent (std::make_unique<ModulesInformationComponent> (project)); | |||
| } | |||
| static File getModuleFolder (const File& draggedFile) | |||
| @@ -515,7 +515,7 @@ public: | |||
| addSubItem (new ModuleItem (project, project.getEnabledModules().getModuleID (i))); | |||
| } | |||
| void showPopupMenu() override | |||
| void showPopupMenu (Point<int> p) override | |||
| { | |||
| auto& enabledModules = project.getEnabledModules(); | |||
| PopupMenu allModules; | |||
| @@ -563,7 +563,7 @@ public: | |||
| menu.addSeparator(); | |||
| menu.addItem (1001, "Add a module from a specified folder..."); | |||
| launchPopupMenu (menu); | |||
| launchPopupMenu (menu, p); | |||
| } | |||
| void handlePopupMenuResult (int resultCode) override | |||
| @@ -35,6 +35,9 @@ struct ProjectSettingsComponent : public Component, | |||
| group (project.getProjectFilenameRootString(), | |||
| Icon (getIcons().settings, Colours::transparentBlack)) | |||
| { | |||
| setTitle ("Project Settings"); | |||
| setFocusContainerType (FocusContainerType::focusContainer); | |||
| addAndMakeVisible (group); | |||
| updatePropertyList(); | |||
| @@ -238,16 +241,34 @@ private: | |||
| 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) | |||
| { | |||
| @@ -35,17 +35,16 @@ struct ProjectTreeItemBase : public JucerTreeViewBase, | |||
| void showSettingsPage (Component* content) | |||
| { | |||
| content->setComponentID (getUniqueName()); | |||
| 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() | |||
| { | |||
| if (auto* pcc = getProjectContentComponent()) | |||
| if (auto* content = pcc->getEditorComponentContent()) | |||
| if (auto* content = pcc->getEditorComponent()) | |||
| if (content->getComponentID() == getUniqueName()) | |||
| pcc->hideEditor(); | |||
| } | |||
| @@ -34,11 +34,14 @@ public: | |||
| ConcertinaHeader (String n, Path p) | |||
| : Component (n), name (n), iconPath (p) | |||
| { | |||
| setTitle (getName()); | |||
| panelIcon = Icon (iconPath, Colours::white); | |||
| nameLabel.setText (name, dontSendNotification); | |||
| nameLabel.setJustificationType (Justification::centredLeft); | |||
| nameLabel.setInterceptsMouseClicks (false, false); | |||
| nameLabel.setAccessible (false); | |||
| nameLabel.setColour (Label::textColourId, Colours::white); | |||
| addAndMakeVisible (nameLabel); | |||
| @@ -72,6 +75,14 @@ public: | |||
| 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 yPosition = 0; | |||
| @@ -166,25 +177,57 @@ private: | |||
| class ConcertinaTreeComponent : public Component | |||
| { | |||
| 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); | |||
| addAndMakeVisible (addButton.get()); | |||
| addButton->onClick = [this] { showAddMenu(); }; | |||
| } | |||
| if (hasSettingsButton) | |||
| if (additionalComponents.has (AdditionalComponents::settingsButton)) | |||
| { | |||
| settingsButton = std::make_unique<IconButton> ("Settings", getIcons().settings); | |||
| addAndMakeVisible (settingsButton.get()); | |||
| settingsButton->onClick = [this] { showSettings(); }; | |||
| } | |||
| if (hasFindPanel) | |||
| if (additionalComponents.has (AdditionalComponents::findPanel)) | |||
| { | |||
| findPanel = std::make_unique<FindPanel> ([this] (const String& filter) { treeToDisplay->rootItem->setSearchFilter (filter); }); | |||
| addAndMakeVisible (findPanel.get()); | |||
| @@ -232,12 +275,12 @@ private: | |||
| if (numSelected == 0) | |||
| { | |||
| if (auto* root = dynamic_cast<JucerTreeViewBase*> (treeToDisplay->tree.getRootItem())) | |||
| root->showPopupMenu(); | |||
| root->showPopupMenu (addButton->getScreenBounds().getCentre()); | |||
| } | |||
| else | |||
| { | |||
| 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 | |||
| #include "../../Utility/UI/PropertyComponents/jucer_LabelPropertyComponent.h" | |||
| //============================================================================== | |||
| struct ContentViewHeader : public Component | |||
| @@ -32,6 +33,7 @@ struct ContentViewHeader : public Component | |||
| ContentViewHeader (String headerName, Icon headerIcon) | |||
| : name (headerName), icon (headerIcon) | |||
| { | |||
| setTitle (name); | |||
| } | |||
| void paint (Graphics& g) override | |||
| @@ -152,14 +154,18 @@ private: | |||
| }; | |||
| //============================================================================== | |||
| class InfoButton : public Button | |||
| class InfoButton : public Button | |||
| { | |||
| public: | |||
| InfoButton (const String& infoToDisplay = {}) | |||
| : Button ({}) | |||
| { | |||
| setTitle ("Info"); | |||
| if (infoToDisplay.isNotEmpty()) | |||
| setInfoToDisplay (infoToDisplay); | |||
| setSize (20, 20); | |||
| } | |||
| void paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown) override | |||
| @@ -196,6 +202,8 @@ public: | |||
| width = jmin (300, stringWidth); | |||
| numLines += static_cast<int> (stringWidth / width); | |||
| setHelpText (info); | |||
| } | |||
| } | |||
| @@ -242,37 +250,48 @@ public: | |||
| description (desc) | |||
| { | |||
| addAndMakeVisible (header); | |||
| description.setFont ({ 16.0f }); | |||
| description.setColour (getLookAndFeel().findColour (defaultTextColourId)); | |||
| description.setLineSpacing (5.0f); | |||
| description.setJustification (Justification::centredLeft); | |||
| } | |||
| 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(); }; | |||
| if (auto* text = dynamic_cast<TextPropertyComponent*> (prop)) | |||
| if (auto* text = dynamic_cast<TextPropertyComponent*> (prop.get())) | |||
| if (text->isTextEditorMultiLine()) | |||
| text->addListener (this); | |||
| } | |||
| } | |||
| @@ -281,31 +300,21 @@ public: | |||
| header.setBounds (0, 0, width, headerSize); | |||
| 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; | |||
| } | |||
| @@ -319,19 +328,51 @@ public: | |||
| 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: | |||
| //============================================================================== | |||
| 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 | |||
| { | |||
| @@ -348,7 +389,6 @@ private: | |||
| updateSize(); | |||
| } | |||
| //============================================================================== | |||
| void updateSize() | |||
| { | |||
| updateSize (getX(), getY(), getWidth()); | |||
| @@ -357,7 +397,6 @@ private: | |||
| parent->parentSizeChanged(); | |||
| } | |||
| //============================================================================== | |||
| bool shouldResizePropertyComponent (PropertyComponent* p) | |||
| { | |||
| if (auto* textComp = dynamic_cast<TextPropertyComponent*> (p)) | |||
| @@ -392,11 +431,15 @@ private: | |||
| 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; | |||
| AttributedString description; | |||
| TextLayout descriptionLayout; | |||
| int headerSize = 40; | |||
| String description; | |||
| //============================================================================== | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PropertyGroupComponent) | |||
| @@ -40,6 +40,9 @@ | |||
| HeaderComponent::HeaderComponent (ProjectContentComponent* pcc) | |||
| : projectContentComponent (pcc) | |||
| { | |||
| setTitle ("Header"); | |||
| setFocusContainerType (FocusContainerType::focusContainer); | |||
| addAndMakeVisible (configLabel); | |||
| addAndMakeVisible (exporterBox); | |||
| @@ -34,63 +34,23 @@ | |||
| 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() | |||
| { | |||
| setOpaque (true); | |||
| setWantsKeyboardFocus (true); | |||
| addAndMakeVisible (logoComponent); | |||
| addAndMakeVisible (headerComponent); | |||
| addAndMakeVisible (projectMessagesComponent); | |||
| addAndMakeVisible (fileNameLabel); | |||
| fileNameLabel.setJustificationType (Justification::centred); | |||
| addAndMakeVisible (contentViewComponent); | |||
| sidebarSizeConstrainer.setMinimumWidth (200); | |||
| sidebarSizeConstrainer.setMaximumWidth (500); | |||
| sidebarTabs.setOutline (0); | |||
| sidebarTabs.getTabbedButtonBar().setMinimumTabScaleFactor (0.5); | |||
| sidebarTabs.setTitle ("Sidebar"); | |||
| sidebarTabs.setFocusContainerType (FocusContainerType::focusContainer); | |||
| ProjucerApplication::getApp().openDocumentManager.addListener (this); | |||
| @@ -140,15 +100,9 @@ void ProjectContentComponent::resized() | |||
| if (resizerBar != nullptr) | |||
| 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() | |||
| @@ -175,8 +129,8 @@ void ProjectContentComponent::setProject (Project* newProject) | |||
| if (project != nullptr) | |||
| project->removeChangeListener (this); | |||
| contentView.reset(); | |||
| resizerBar.reset(); | |||
| hideEditor(); | |||
| resizerBar = nullptr; | |||
| deleteProjectTabs(); | |||
| project = newProject; | |||
| @@ -360,16 +314,12 @@ void ProjectContentComponent::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 | |||
| @@ -391,30 +341,22 @@ bool ProjectContentComponent::showDocument (OpenDocumentManager::Document* doc, | |||
| if (doc->hasFileBeenModifiedExternally()) | |||
| 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() | |||
| { | |||
| currentDocument = nullptr; | |||
| contentView.reset(); | |||
| fileNameLabel.setVisible (false); | |||
| contentViewComponent.setContent ({}, {}); | |||
| ProjucerApplication::getCommandManager().commandStatusChanged(); | |||
| resized(); | |||
| @@ -422,68 +364,69 @@ void ProjectContentComponent::hideEditor() | |||
| 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() | |||
| { | |||
| if (currentDocument != nullptr) | |||
| { | |||
| ProjucerApplication::getApp().openDocumentManager | |||
| .closeDocument (currentDocument, OpenDocumentManager::SaveIfNeeded::yes); | |||
| else if (contentView != nullptr) | |||
| if (! goToPreviousFile()) | |||
| hideEditor(); | |||
| return; | |||
| } | |||
| if (! goToPreviousFile()) | |||
| hideEditor(); | |||
| } | |||
| static void showSaveWarning (OpenDocumentManager::Document* currentDocument) | |||
| @@ -570,7 +513,7 @@ void ProjectContentComponent::closeProject() | |||
| void ProjectContentComponent::showProjectSettings() | |||
| { | |||
| setEditorComponent (new ProjectSettingsComponent (*project), nullptr); | |||
| setScrollableEditorComponent (std::make_unique<ProjectSettingsComponent> (*project)); | |||
| } | |||
| void ProjectContentComponent::showCurrentExporterSettings() | |||
| @@ -634,7 +577,7 @@ void ProjectContentComponent::showModule (const String& moduleID) | |||
| void ProjectContentComponent::showLiveBuildSettings() | |||
| { | |||
| setEditorComponent (new LiveBuildSettingsComponent (*project), nullptr); | |||
| setScrollableEditorComponent (std::make_unique<LiveBuildSettingsComponent> (*project)); | |||
| } | |||
| StringArray ProjectContentComponent::getExportersWhichCanLaunch() const | |||
| @@ -851,7 +794,7 @@ void ProjectContentComponent::getCommandInfo (const CommandID commandID, Applica | |||
| result.setInfo ("Close" + documentName, | |||
| "Closes the current document", | |||
| CommandCategories::general, 0); | |||
| result.setActive (contentView != nullptr); | |||
| result.setActive (currentDocument != nullptr); | |||
| result.defaultKeypresses.add ({ 'w', cmdCtrl, 0 }); | |||
| break; | |||
| @@ -28,6 +28,7 @@ | |||
| #include "../../CodeEditor/jucer_OpenDocumentManager.h" | |||
| #include "jucer_HeaderComponent.h" | |||
| #include "jucer_ProjectMessagesComponent.h" | |||
| #include "jucer_ContentViewComponent.h" | |||
| class CompileEngineChildProcess; | |||
| class ProjectTab; | |||
| @@ -65,10 +66,11 @@ public: | |||
| void saveAs(); | |||
| 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 goToNextFile(); | |||
| @@ -144,26 +146,6 @@ public: | |||
| static String getBuildTabName() { return "Build"; } | |||
| 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; | |||
| void changeListenerCallback (ChangeBroadcaster*) override; | |||
| @@ -196,14 +178,14 @@ private: | |||
| OpenDocumentManager::Document* currentDocument = nullptr; | |||
| RecentDocumentList recentDocumentList; | |||
| LogoComponent logoComponent; | |||
| HeaderComponent headerComponent { this }; | |||
| TabbedComponent sidebarTabs { TabbedButtonBar::TabsAtTop }; | |||
| ProjectMessagesComponent projectMessagesComponent; | |||
| Label fileNameLabel; | |||
| TabbedComponent sidebarTabs { TabbedButtonBar::TabsAtTop }; | |||
| ContentViewComponent contentViewComponent; | |||
| std::unique_ptr<ResizableEdgeComponent> resizerBar; | |||
| ComponentBoundsConstrainer sidebarSizeConstrainer; | |||
| std::unique_ptr<Component> translationTool, contentView; | |||
| std::unique_ptr<Component> translationTool; | |||
| BubbleMessageComponent bubbleMessage; | |||
| ReferenceCountedObjectPtr<CompileEngineChildProcess> childProcess; | |||
| @@ -392,6 +392,9 @@ class ProjectMessagesComponent : public Component | |||
| public: | |||
| ProjectMessagesComponent() | |||
| { | |||
| setFocusContainerType (FocusContainerType::focusContainer); | |||
| setTitle ("Project Messages"); | |||
| addAndMakeVisible (warningsComponent); | |||
| addAndMakeVisible (notificationsComponent); | |||
| @@ -445,8 +448,15 @@ public: | |||
| isMouseDown = false; | |||
| 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: | |||
| //============================================================================== | |||
| struct MessageCountComponent : public Component, | |||
| @@ -511,9 +535,12 @@ private: | |||
| void updateNumMessages() | |||
| { | |||
| numMessages = messagesTree.getNumChildren(); | |||
| owner.numMessagesChanged(); | |||
| repaint(); | |||
| } | |||
| int getNumMessages() const noexcept { return numMessages; } | |||
| private: | |||
| void valueTreeChildAdded (ValueTree&, ValueTree&) override { updateNumMessages(); } | |||
| void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override { updateNumMessages(); } | |||
| @@ -64,6 +64,11 @@ public: | |||
| } | |||
| void mouseUp (const MouseEvent&) override | |||
| { | |||
| triggerClick(); | |||
| } | |||
| void triggerClick() | |||
| { | |||
| if (interactive) | |||
| { | |||
| @@ -76,6 +81,15 @@ public: | |||
| 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: | |||
| //============================================================================== | |||
| static Image createGPLAvatarImage() | |||
| @@ -845,6 +845,17 @@ public: | |||
| { | |||
| 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()) | |||
| e->createNewChildElement ("AdditionalOptions")->addTextElement (extraCompilerFlags + " %(AdditionalOptions)"); | |||
| @@ -1729,6 +1740,11 @@ protected: | |||
| 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 result; | |||
| @@ -119,9 +119,9 @@ void JucerTreeViewBase::paintContent (Graphics& g, Rectangle<int> area) | |||
| 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 (getOwnerView()->getNumSelectedItems() > 1) | |||
| showMultiSelectionPopupMenu(); | |||
| showMultiSelectionPopupMenu (e.getMouseDownScreenPosition()); | |||
| else | |||
| showPopupMenu(); | |||
| showPopupMenu (e.getMouseDownScreenPosition()); | |||
| } | |||
| 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) | |||
| { | |||
| if (item != nullptr) | |||
| 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))); | |||
| } | |||
| void JucerTreeViewBase::handlePopupMenuResult (int) | |||
| { | |||
| } | |||
| ProjectContentComponent* JucerTreeViewBase::getProjectContentComponent() const | |||
| { | |||
| for (Component* c = getOwnerView(); c != nullptr; c = c->getParentComponent()) | |||
| @@ -44,8 +44,9 @@ public: | |||
| void itemClicked (const MouseEvent& e) override; | |||
| void itemSelectionChanged (bool isNowSelected) 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(); | |||
| @@ -67,17 +68,18 @@ public: | |||
| virtual File getDraggableFile() const { return {}; } | |||
| 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 | |||
| @@ -187,7 +189,7 @@ public: | |||
| tree.clearSelectedItems(); | |||
| if (e.mods.isRightButtonDown()) | |||
| rootItem->showPopupMenu(); | |||
| rootItem->showPopupMenu (e.getMouseDownScreenPosition()); | |||
| } | |||
| } | |||
| @@ -205,6 +207,7 @@ class TreeItemComponent : public Component | |||
| public: | |||
| TreeItemComponent (JucerTreeViewBase& i) : item (&i) | |||
| { | |||
| setAccessible (false); | |||
| setInterceptsMouseClicks (false, true); | |||
| item->textX = iconWidth; | |||
| } | |||