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:
parent
cb7ecfd77b
commit
f4d8cf70d1
3 changed files with 244 additions and 34 deletions
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue