1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2020 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 By using JUCE, you agree to the terms of both the JUCE 6 End-User License
11 Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
12
13 End User License Agreement: www.juce.com/juce-6-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
15
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
18
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21 DISCLAIMED.
22
23 ==============================================================================
24 */
25
26 #include "../jucer_Headers.h"
27 #include "../jucer_Application.h"
28
29 #include "jucer_StartPageComponent.h"
30 #include "jucer_StartPageTreeHolder.h"
31 #include "jucer_NewProjectTemplates.h"
32 #include "jucer_ContentComponents.h"
33
34 //==============================================================================
35 struct ContentComponent : public Component
36 {
resizedContentComponent37 void resized() override
38 {
39 if (content != nullptr)
40 content->setBounds (getLocalBounds());
41 }
42
setContentContentComponent43 void setContent (std::unique_ptr<Component>&& newContent)
44 {
45 if (content.get() != newContent.get())
46 {
47 content = std::move (newContent);
48 addAndMakeVisible (content.get());
49 resized();
50 }
51 }
52
53 private:
54 std::unique_ptr<Component> content;
55
56 //==============================================================================
57 JUCE_LEAK_DETECTOR (ContentComponent)
58 };
59
60 //==============================================================================
findExampleFile(int dirIndex,int index)61 static File findExampleFile (int dirIndex, int index)
62 {
63 auto dir = ProjucerApplication::getSortedExampleDirectories()[dirIndex];
64 return ProjucerApplication::getSortedExampleFilesInDirectory (dir)[index];
65 }
66
createExampleProjectsTab(ContentComponent & content,std::function<void (const File &)> cb)67 static std::unique_ptr<Component> createExampleProjectsTab (ContentComponent& content, std::function<void (const File&)> cb)
68 {
69 StringArray exampleCategories;
70 std::vector<StringArray> examples;
71
72 for (auto& dir : ProjucerApplication::getSortedExampleDirectories())
73 {
74 exampleCategories.add (dir.getFileName());
75
76 StringArray ex;
77 for (auto& f : ProjucerApplication::getSortedExampleFilesInDirectory (dir))
78 ex.add (f.getFileNameWithoutExtension());
79
80 examples.push_back (ex);
81 }
82
83 if (exampleCategories.isEmpty())
84 return nullptr;
85
86 auto selectedCallback = [&, cb] (int category, int index) mutable
87 {
88 content.setContent (std::make_unique<ExampleComponent> (findExampleFile (category, index), cb));
89 };
90
91 return std::make_unique<StartPageTreeHolder> (exampleCategories,
92 examples,
93 std::move (selectedCallback),
94 StartPageTreeHolder::Open::no);
95 }
96
97 //==============================================================================
getAllTemplateCategoryStrings()98 static StringArray getAllTemplateCategoryStrings()
99 {
100 StringArray categories;
101
102 for (auto& t : NewProjectTemplates::getAllTemplates())
103 categories.addIfNotAlreadyThere (NewProjectTemplates::getProjectCategoryString (t.category));
104
105 return categories;
106 }
107
getTemplatesInCategory(const String & category)108 static std::vector<NewProjectTemplates::ProjectTemplate> getTemplatesInCategory (const String& category)
109 {
110 std::vector<NewProjectTemplates::ProjectTemplate> templates;
111
112 for (auto& t : NewProjectTemplates::getAllTemplates())
113 if (NewProjectTemplates::getProjectCategoryString (t.category) == category)
114 templates.push_back (t);
115
116 return templates;
117 }
118
getAllTemplateNamesForCategory(const String & category)119 static StringArray getAllTemplateNamesForCategory (const String& category)
120 {
121 StringArray types;
122
123 for (auto& t : getTemplatesInCategory (category))
124 types.add (t.displayName);
125
126 return types;
127 }
128
createProjectTemplatesTab(ContentComponent & content,std::function<void (std::unique_ptr<Project> &&)> && cb)129 static std::unique_ptr<Component> createProjectTemplatesTab (ContentComponent& content,
130 std::function<void (std::unique_ptr<Project>&&)>&& cb)
131 {
132 auto categories = getAllTemplateCategoryStrings();
133
134 std::vector<StringArray> templateNames;
135
136 for (auto& c : categories)
137 templateNames.push_back (getAllTemplateNamesForCategory (c));
138
139 auto selectedCallback = [&, cb] (int category, int index)
140 {
141 auto categoryString = getAllTemplateCategoryStrings()[category];
142 auto templates = getTemplatesInCategory (categoryString);
143
144 content.setContent (std::make_unique<TemplateComponent> (templates[(size_t) index], std::move (cb)));
145 };
146
147 auto holder = std::make_unique<StartPageTreeHolder> (categories,
148 templateNames,
149 std::move (selectedCallback),
150 StartPageTreeHolder::Open::yes);
151 holder->setSelectedItem (categories[0], 1);
152
153 JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wredundant-move")
154 return std::move (holder);
155 JUCE_END_IGNORE_WARNINGS_GCC_LIKE
156 }
157
158 //==============================================================================
159 struct ProjectTemplatesAndExamples : public TabbedComponent
160 {
ProjectTemplatesAndExamplesProjectTemplatesAndExamples161 ProjectTemplatesAndExamples (ContentComponent& c,
162 std::function<void (std::unique_ptr<Project>&&)>&& newProjectCb,
163 std::function<void (const File&)>&& exampleCb)
164 : TabbedComponent (TabbedButtonBar::Orientation::TabsAtTop),
165 content (c),
166 exampleSelectedCallback (std::move (exampleCb))
167 {
168 addTab ("New Project", Colours::transparentBlack, createProjectTemplatesTab (content, std::move (newProjectCb)).release(), true);
169 refreshExamplesTab();
170 }
171
refreshExamplesTabProjectTemplatesAndExamples172 void refreshExamplesTab()
173 {
174 auto wasOpen = (getCurrentTabIndex() == 1);
175
176 removeTab (1);
177
178 auto exampleTabs = createExampleProjectsTab (content, exampleSelectedCallback);
179
180 addTab ("Open Example", Colours::transparentBlack, exampleTabs == nullptr ? new SetJUCEPathComponent (*this)
181 : exampleTabs.release(),
182 true);
183
184 if (wasOpen)
185 setCurrentTabIndex (1);
186 }
187
188 private:
189 //==============================================================================
190 struct SetJUCEPathComponent : public Component,
191 private ChangeListener
192 {
SetJUCEPathComponentProjectTemplatesAndExamples::SetJUCEPathComponent193 explicit SetJUCEPathComponent (ProjectTemplatesAndExamples& o)
194 : owner (o)
195 {
196 getGlobalProperties().addChangeListener (this);
197
198 setPathButton.setButtonText ("Set path to JUCE...");
199 setPathButton.onClick = [] { ProjucerApplication::getApp().showPathsWindow (true); };
200
201 addAndMakeVisible (setPathButton);
202 }
203
~SetJUCEPathComponentProjectTemplatesAndExamples::SetJUCEPathComponent204 ~SetJUCEPathComponent() override
205 {
206 getGlobalProperties().removeChangeListener (this);
207 }
208
paintProjectTemplatesAndExamples::SetJUCEPathComponent209 void paint (Graphics& g) override
210 {
211 g.fillAll (findColour (secondaryBackgroundColourId));
212 }
213
resizedProjectTemplatesAndExamples::SetJUCEPathComponent214 void resized() override
215 {
216 auto bounds = getLocalBounds().reduced (5);
217 bounds.removeFromTop (25);
218
219 setPathButton.setBounds (bounds.removeFromTop (25));
220 }
221
222 private:
changeListenerCallbackProjectTemplatesAndExamples::SetJUCEPathComponent223 void changeListenerCallback (ChangeBroadcaster*) override
224 {
225 if (isValidJUCEExamplesDirectory (ProjucerApplication::getJUCEExamplesDirectoryPathFromGlobal()))
226 owner.refreshExamplesTab();
227 }
228
229 ProjectTemplatesAndExamples& owner;
230 TextButton setPathButton;
231 };
232
233 ContentComponent& content;
234 std::function<void (const File&)> exampleSelectedCallback;
235 };
236
237 //==============================================================================
StartPageComponent(std::function<void (std::unique_ptr<Project> &&)> && newProjectCb,std::function<void (const File &)> && exampleCb)238 StartPageComponent::StartPageComponent (std::function<void (std::unique_ptr<Project>&&)>&& newProjectCb,
239 std::function<void (const File&)>&& exampleCb)
240 : content (std::make_unique<ContentComponent>()),
241 tabs (std::make_unique<ProjectTemplatesAndExamples> (*content, std::move (newProjectCb), std::move (exampleCb)))
242 {
243 tabs->setOutline (0);
244 addAndMakeVisible (*tabs);
245
246 addAndMakeVisible (openExistingButton);
247 openExistingButton.setCommandToTrigger (&ProjucerApplication::getCommandManager(), CommandIDs::open, true);
248
249 addAndMakeVisible (*content);
250
251 setSize (900, 600);
252 }
253
paint(Graphics & g)254 void StartPageComponent::paint (Graphics& g)
255 {
256 g.fillAll (findColour (backgroundColourId));
257 }
258
resized()259 void StartPageComponent::resized()
260 {
261 auto bounds = getLocalBounds().reduced (10);
262
263 auto tabBounds = bounds.removeFromLeft (bounds.getWidth() / 3);
264
265 openExistingButton.setBounds (tabBounds.removeFromBottom (30).reduced (10, 0));
266 tabBounds.removeFromBottom (5);
267
268 tabs->setBounds (tabBounds);
269 bounds.removeFromLeft (10);
270
271 content->setBounds (bounds);
272 }
273