1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-22 01:34:21 +00:00

Projucer: Added a search field to the file panel to filter project files and added options to expand/collapse all groups in the file tree popup menu

This commit is contained in:
ed 2017-06-20 14:57:04 +01:00
parent cb7ecfd77b
commit f4d8cf70d1
3 changed files with 244 additions and 34 deletions

View file

@ -982,7 +982,7 @@ void ProjectContentComponent::getCommandInfo (const CommandID commandID, Applica
"Shows the main project options page",
CommandCategories::general, 0);
result.setActive (project != nullptr);
result.defaultKeypresses.add (KeyPress ('p', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0));
result.defaultKeypresses.add (KeyPress ('s', ModifierKeys::commandModifier | ModifierKeys::ctrlModifier, 0));
break;
case CommandIDs::showProjectTab:
@ -990,7 +990,7 @@ void ProjectContentComponent::getCommandInfo (const CommandID commandID, Applica
"Shows the tab containing the project information",
CommandCategories::general, 0);
result.setActive (project != nullptr);
result.defaultKeypresses.add (KeyPress ('p', ModifierKeys::commandModifier, 0));
result.defaultKeypresses.add (KeyPress ('p', ModifierKeys::commandModifier | ModifierKeys::ctrlModifier, 0));
break;
case CommandIDs::showBuildTab:
@ -998,6 +998,7 @@ void ProjectContentComponent::getCommandInfo (const CommandID commandID, Applica
"Shows the tab containing the build panel",
CommandCategories::general, 0);
result.setActive (project != nullptr);
result.defaultKeypresses.add (KeyPress ('b', ModifierKeys::commandModifier | ModifierKeys::ctrlModifier, 0));
break;
case CommandIDs::showFileExplorerPanel:
@ -1005,7 +1006,7 @@ void ProjectContentComponent::getCommandInfo (const CommandID commandID, Applica
"Shows the panel containing the tree of files for this project",
CommandCategories::general, 0);
result.setActive (project != nullptr);
result.defaultKeypresses.add (KeyPress ('f', ModifierKeys::commandModifier, 0));
result.defaultKeypresses.add (KeyPress ('f', ModifierKeys::commandModifier | ModifierKeys::ctrlModifier, 0));
break;
case CommandIDs::showModulesPanel:
@ -1013,7 +1014,7 @@ void ProjectContentComponent::getCommandInfo (const CommandID commandID, Applica
"Shows the panel containing the project's list of modules",
CommandCategories::general, 0);
result.setActive (project != nullptr);
result.defaultKeypresses.add (KeyPress ('m', ModifierKeys::commandModifier, 0));
result.defaultKeypresses.add (KeyPress ('m', ModifierKeys::commandModifier | ModifierKeys::ctrlModifier, 0));
break;
case CommandIDs::showExportersPanel:
@ -1021,7 +1022,7 @@ void ProjectContentComponent::getCommandInfo (const CommandID commandID, Applica
"Shows the panel containing the project's list of exporters",
CommandCategories::general, 0);
result.setActive (project != nullptr);
result.defaultKeypresses.add (KeyPress ('e', ModifierKeys::commandModifier, 0));
result.defaultKeypresses.add (KeyPress ('e', ModifierKeys::commandModifier | ModifierKeys::ctrlModifier, 0));
break;
case CommandIDs::showExporterSettings:

View file

@ -182,6 +182,12 @@ struct FileTreePanel : public TreePanelBase
if (auto* p = dynamic_cast<FileTreeItemTypes::ProjectTreeItemBase*> (rootItem.get()))
p->checkFileStatus();
}
void setSearchFilter (const String& filter)
{
if (auto* p = dynamic_cast<FileTreeItemTypes::GroupItem*> (rootItem.get()))
p->setSearchFilter (filter);
}
};
struct ModuleTreePanel : public TreePanelBase
@ -216,12 +222,12 @@ struct ExportersTreePanel : public TreePanelBase
//==============================================================================
class ConcertinaTreeComponent : public Component,
private Button::Listener
private Button::Listener,
private ChangeListener
{
public:
ConcertinaTreeComponent (TreePanelBase* tree, bool showSettings = false)
: treeToDisplay (tree),
showSettingsButton (showSettings)
ConcertinaTreeComponent (TreePanelBase* tree, bool showSettingsButton = false, bool showFindPanel = false)
: treeToDisplay (tree)
{
addAndMakeVisible (popupMenuButton = new IconButton ("Add", &getIcons().plus));
popupMenuButton->addListener (this);
@ -232,6 +238,12 @@ public:
settingsButton->addListener (this);
}
if (showFindPanel)
{
addAndMakeVisible (findPanel = new FindPanel());
findPanel->addChangeListener (this);
}
addAndMakeVisible (treeToDisplay);
}
@ -249,18 +261,26 @@ public:
bottomSlice.removeFromRight (5);
popupMenuButton->setBounds (bottomSlice.removeFromRight (25).reduced (2));
if (showSettingsButton)
settingsButton->setBounds (bottomSlice.removeFromRight(25).reduced (2));
if (settingsButton != nullptr)
settingsButton->setBounds (bottomSlice.removeFromRight (25).reduced (2));
if (findPanel != nullptr)
findPanel->setBounds (bottomSlice.reduced (2));
treeToDisplay->setBounds (bounds);
}
TreePanelBase* getTree() const noexcept { return treeToDisplay.get(); }
void grabFindFocus()
{
if (findPanel != nullptr)
findPanel->grabKeyboardFocus();
}
private:
ScopedPointer<TreePanelBase> treeToDisplay;
ScopedPointer<IconButton> popupMenuButton, settingsButton;
bool showSettingsButton;
void buttonClicked (Button* b) override
{
@ -296,11 +316,93 @@ private:
}
}
void changeListenerCallback (ChangeBroadcaster* source) override
{
if (source == findPanel)
if (auto* fileTree = dynamic_cast<FileTreePanel*> (treeToDisplay.get()))
fileTree->setSearchFilter (findPanel->editor.getText());
}
class FindPanel : public Component,
public ChangeBroadcaster,
private TextEditor::Listener,
private Timer,
private FocusChangeListener
{
public:
FindPanel()
{
addAndMakeVisible (editor);
editor.addListener (this);
Desktop::getInstance().addFocusChangeListener (this);
lookAndFeelChanged();
}
void paintOverChildren (Graphics& g) override
{
if (! isFocused)
return;
g.setColour (findColour (defaultHighlightColourId));
Path p;
p.addRoundedRectangle (getLocalBounds().reduced (2), 3.0f);
g.strokePath (p, PathStrokeType (2.0f));
}
void resized() override
{
editor.setBounds (getLocalBounds().reduced (2));
}
TextEditor editor;
private:
void lookAndFeelChanged() override
{
editor.setTextToShowWhenEmpty ("Filter...", findColour (widgetTextColourId).withAlpha (0.3f));
}
void textEditorTextChanged (TextEditor&) override
{
startTimer (250);
}
void textEditorFocusLost (TextEditor&) override
{
isFocused = false;
repaint();
}
void globalFocusChanged (Component* focusedComponent) override
{
if (focusedComponent == &editor)
{
isFocused = true;
repaint();
}
}
void timerCallback() override
{
stopTimer();
sendChangeMessage();
}
bool isFocused = false;
};
ScopedPointer<FindPanel> findPanel;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConcertinaTreeComponent)
};
//==============================================================================
class ProjectTab : public Component,
public ApplicationCommandTarget,
private ChangeListener
{
public:
@ -389,6 +491,47 @@ public:
return ((float) (concertinaPanel.getPanel (panelIndex)->getHeight()) / (concertinaPanel.getHeight() - 90));
}
//==============================================================================
ApplicationCommandTarget* getNextCommandTarget() override { return nullptr; }
void getAllCommands (Array <CommandID>& commands) override
{
const CommandID ids[] = { CommandIDs::showFindPanel };
commands.addArray (ids, numElementsInArray (ids));
}
void getCommandInfo (CommandID commandID, ApplicationCommandInfo& result) override
{
switch (commandID)
{
case CommandIDs::showFindPanel:
result.setInfo (TRANS ("Find"), TRANS ("Searches for ."), "Editing", 0);
result.defaultKeypresses.add (KeyPress ('f', ModifierKeys::commandModifier, 0));
break;
default:
break;
}
}
bool perform (const InvocationInfo& info) override
{
switch (info.commandID)
{
case CommandIDs::showFindPanel:
{
find();
return true;
}
default:
return false;
}
return true;
}
private:
ConcertinaPanel concertinaPanel;
@ -414,7 +557,7 @@ private:
if (project != nullptr)
{
concertinaPanel.addPanel (0, new ConcertinaTreeComponent (new FileTreePanel (*project)), true);
concertinaPanel.addPanel (0, new ConcertinaTreeComponent (new FileTreePanel (*project), false, true), true);
concertinaPanel.addPanel (1, new ConcertinaTreeComponent (new ModuleTreePanel (*project), true), true);
concertinaPanel.addPanel (2, new ConcertinaTreeComponent (new ExportersTreePanel (*project)), true);
}
@ -456,5 +599,13 @@ private:
}
}
void find()
{
showPanel (0);
if (auto* treeComponent = dynamic_cast<ConcertinaTreeComponent*> (concertinaPanel.getPanel (0)))
treeComponent->grabFindFocus();
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProjectTab)
};

View file

@ -27,8 +27,9 @@
class GroupItem : public ProjectTreeItemBase
{
public:
GroupItem (const Project::Item& projectItem)
: ProjectTreeItemBase (projectItem)
GroupItem (const Project::Item& projectItem, const String& filter = String())
: ProjectTreeItemBase (projectItem),
searchFilter (filter)
{
}
@ -79,10 +80,40 @@ public:
p->checkFileStatus();
}
bool isGroupEmpty (const Project::Item& group) // recursive
{
bool isEmpty = true;
for (auto i = 0; i < group.getNumChildren(); ++i)
{
auto child = group.getChild (i);
if (child.isGroup())
isEmpty = isGroupEmpty (child);
else if (child.isFile() && child.getName().containsIgnoreCase (searchFilter))
isEmpty = false;
}
return isEmpty;
}
ProjectTreeItemBase* createSubItem (const Project::Item& child) override
{
if (child.isGroup()) return new GroupItem (child);
if (child.isFile()) return new SourceFileItem (child);
if (child.isGroup())
{
if (isGroupEmpty (child))
return nullptr;
return new GroupItem (child, searchFilter);
}
if (child.isFile())
{
if (child.getName().containsIgnoreCase (searchFilter))
return new SourceFileItem (child);
return nullptr;
}
jassertfalse;
return nullptr;
@ -94,12 +125,26 @@ public:
pcc->setEditorComponent (new GroupInformationComponent (item), nullptr);
}
static void openAllGroups (TreeViewItem* root)
{
for (auto i = 0; i < root->getNumSubItems(); ++i)
if (auto* sub = root->getSubItem (i))
openOrCloseAllSubGroups (*sub, true);
}
static void closeAllGroups (TreeViewItem* root)
{
for (auto i = 0; i < root->getNumSubItems(); ++i)
if (auto* sub = root->getSubItem (i))
openOrCloseAllSubGroups (*sub, false);
}
static void openOrCloseAllSubGroups (TreeViewItem& item, bool shouldOpen)
{
item.setOpen (shouldOpen);
for (int i = item.getNumSubItems(); --i >= 0;)
if (TreeViewItem* sub = item.getSubItem(i))
if (auto* sub = item.getSubItem (i))
openOrCloseAllSubGroups (*sub, shouldOpen);
}
@ -119,27 +164,30 @@ public:
m.addSeparator();
m.addItem (1, "Collapse all Groups");
m.addItem (2, "Expand all Groups");
if (! isRoot())
{
if (isOpen())
m.addItem (1, "Collapse all Sub-groups");
m.addItem (3, "Collapse all Sub-groups");
else
m.addItem (2, "Expand all Sub-groups");
m.addItem (4, "Expand all Sub-groups");
}
m.addSeparator();
m.addItem (3, "Enable compiling of all enclosed files");
m.addItem (4, "Disable compiling of all enclosed files");
m.addItem (5, "Enable compiling of all enclosed files");
m.addItem (6, "Disable compiling of all enclosed files");
m.addSeparator();
m.addItem (5, "Sort Items Alphabetically");
m.addItem (6, "Sort Items Alphabetically (Groups first)");
m.addItem (7, "Sort Items Alphabetically");
m.addItem (8, "Sort Items Alphabetically (Groups first)");
m.addSeparator();
if (! isRoot())
{
m.addItem (7, "Rename...");
m.addItem (8, "Delete");
m.addItem (9, "Rename...");
m.addItem (10, "Delete");
}
launchPopupMenu (m);
@ -157,14 +205,16 @@ public:
{
switch (resultCode)
{
case 1: openOrCloseAllSubGroups (*this, false); break;
case 2: openOrCloseAllSubGroups (*this, true); break;
case 3: setFilesToCompile (item, true); break;
case 4: setFilesToCompile (item, false); break;
case 5: item.sortAlphabetically (false, false); break;
case 6: item.sortAlphabetically (true, false); break;
case 7: triggerAsyncRename (item); break;
case 8: deleteAllSelectedItems(); break;
case 1: closeAllGroups (getOwnerView()->getRootItem()); break;
case 2: openAllGroups (getOwnerView()->getRootItem()); break;
case 3: openOrCloseAllSubGroups (*this, false); break;
case 4: openOrCloseAllSubGroups (*this, true); break;
case 5: setFilesToCompile (item, true); break;
case 6: setFilesToCompile (item, false); break;
case 7: item.sortAlphabetically (false, false); break;
case 8: item.sortAlphabetically (true, false); break;
case 9: triggerAsyncRename (item); break;
case 10: deleteAllSelectedItems(); break;
default: processCreateFileMenuItem (resultCode); break;
}
}
@ -200,4 +250,12 @@ public:
return nullptr;
}
void setSearchFilter (const String& filter)
{
searchFilter = filter;
refreshSubItems();
}
String searchFilter;
};