From 25e7361f2c967fa5ff72b6b85a6798d6a1cb89df Mon Sep 17 00:00:00 2001 From: jules Date: Sun, 8 Jul 2012 15:05:55 +0100 Subject: [PATCH] Refactored some TreeView key handling. Introjucer: now restores the last set of open documents for a project. --- .../Source/Application/jucer_MainWindow.cpp | 15 +- .../Application/jucer_OpenDocumentManager.cpp | 63 +++++++- .../Application/jucer_OpenDocumentManager.h | 7 +- .../Code Editor/jucer_SourceCodeEditor.cpp | 39 +++-- .../Code Editor/jucer_SourceCodeEditor.h | 57 ++++---- .../Project/jucer_ProjectContentComponent.cpp | 38 ++++- .../Project/jucer_ProjectContentComponent.h | 3 + .../Source/Utility/jucer_JucerTreeViewBase.h | 13 +- .../keyboard/juce_ModifierKeys.h | 9 +- .../juce_gui_basics/widgets/juce_TreeView.cpp | 134 ++++++++++-------- .../juce_gui_basics/widgets/juce_TreeView.h | 4 + .../code_editor/juce_CodeEditorComponent.cpp | 15 ++ .../code_editor/juce_CodeEditorComponent.h | 5 + 13 files changed, 294 insertions(+), 108 deletions(-) diff --git a/extras/Introjucer/Source/Application/jucer_MainWindow.cpp b/extras/Introjucer/Source/Application/jucer_MainWindow.cpp index 5a1a4cdb16..565e4e10aa 100644 --- a/extras/Introjucer/Source/Application/jucer_MainWindow.cpp +++ b/extras/Introjucer/Source/Application/jucer_MainWindow.cpp @@ -94,6 +94,7 @@ void MainWindow::createProjectContentCompIfNeeded() { clearContentComponent(); setContentOwned (JucerApplication::getApp()->createProjectContentComponent(), false); + jassert (getProjectContentComponent() != nullptr); } } @@ -126,13 +127,17 @@ bool MainWindow::closeProject (Project* project) getAppProperties().setValue (getProjectWindowPosName(), getWindowStateAsString()); - if (! JucerApplication::getApp()->openDocumentManager.closeAllDocumentsUsingProject (*project, true)) - return false; - ProjectContentComponent* const pcc = getProjectContentComponent(); if (pcc != nullptr) + { pcc->saveTreeViewState(); + pcc->saveOpenDocumentList(); + pcc->hideEditor(); + } + + if (! JucerApplication::getApp()->openDocumentManager.closeAllDocumentsUsingProject (*project, true)) + return false; FileBasedDocument::SaveResult r = project->saveIfNeededAndUserAgrees(); @@ -373,6 +378,10 @@ bool MainWindowList::openFile (const File& file) w->setProject (newDoc.release()); w->makeVisible(); avoidSuperimposedWindows (w); + + jassert (w->getProjectContentComponent() != nullptr); + w->getProjectContentComponent()->reloadLastOpenDocuments(); + return true; } } diff --git a/extras/Introjucer/Source/Application/jucer_OpenDocumentManager.cpp b/extras/Introjucer/Source/Application/jucer_OpenDocumentManager.cpp index 6a347fa2ab..e43acf8d67 100644 --- a/extras/Introjucer/Source/Application/jucer_OpenDocumentManager.cpp +++ b/extras/Introjucer/Source/Application/jucer_OpenDocumentManager.cpp @@ -61,6 +61,8 @@ public: Component* createEditor() { return new ItemPreviewComponent (file); } Component* createViewer() { return createEditor(); } void fileHasBeenRenamed (const File& newFile) { file = newFile; } + String getState() const { return String::empty; } + void restoreState (const String& state) {} String getType() const { @@ -318,7 +320,7 @@ void RecentDocumentList::clear() void RecentDocumentList::newDocumentOpened (OpenDocumentManager::Document* document) { - if (document != getCurrentDocument()) + if (document != nullptr && document != getCurrentDocument()) { nextDocs.clear(); previousDocs.add (document); @@ -371,3 +373,62 @@ void RecentDocumentList::documentAboutToClose (OpenDocumentManager::Document* do jassert (! previousDocs.contains (document)); jassert (! nextDocs.contains (document)); } + +static void restoreDocList (Project& project, Array & list, const XmlElement* xml) +{ + if (xml != nullptr) + { + OpenDocumentManager& odm = JucerApplication::getApp()->openDocumentManager; + + forEachXmlChildElementWithTagName (*xml, e, "DOC") + { + const File file (e->getStringAttribute ("file")); + + if (file.exists()) + { + OpenDocumentManager::Document* doc = odm.openFile (&project, file); + + if (doc != nullptr) + { + doc->restoreState (e->getStringAttribute ("state")); + + list.add (doc); + } + } + } + } +} + +void RecentDocumentList::restoreFromXML (Project& project, const XmlElement& xml) +{ + clear(); + + if (xml.hasTagName ("RECENT_DOCUMENTS")) + { + restoreDocList (project, previousDocs, xml.getChildByName ("PREVIOUS")); + restoreDocList (project, nextDocs, xml.getChildByName ("NEXT")); + } +} + +static void saveDocList (const Array & list, XmlElement& xml) +{ + for (int i = 0; i < list.size(); ++i) + { + const OpenDocumentManager::Document& doc = *list.getUnchecked(i); + + XmlElement* e = xml.createNewChildElement ("DOC"); + + e->setAttribute ("file", doc.getFile().getFullPathName()); + e->setAttribute ("state", doc.getState()); + } +} + +XmlElement* RecentDocumentList::createXML() const +{ + XmlElement* xml = new XmlElement ("RECENT_DOCUMENTS"); + + saveDocList (previousDocs, *xml->createNewChildElement ("PREVIOUS")); + saveDocList (nextDocs, *xml->createNewChildElement ("NEXT")); + + return xml; +} diff --git a/extras/Introjucer/Source/Application/jucer_OpenDocumentManager.h b/extras/Introjucer/Source/Application/jucer_OpenDocumentManager.h index c97ac4ac77..e712b7f24f 100644 --- a/extras/Introjucer/Source/Application/jucer_OpenDocumentManager.h +++ b/extras/Introjucer/Source/Application/jucer_OpenDocumentManager.h @@ -60,6 +60,8 @@ public: virtual Component* createEditor() = 0; virtual Component* createViewer() = 0; virtual void fileHasBeenRenamed (const File& newFile) = 0; + virtual String getState() const = 0; + virtual void restoreState (const String& state) = 0; }; //============================================================================== @@ -122,6 +124,8 @@ public: RecentDocumentList(); ~RecentDocumentList(); + void clear(); + void newDocumentOpened (OpenDocumentManager::Document* document); OpenDocumentManager::Document* getCurrentDocument() const { return previousDocs.getLast(); } @@ -134,7 +138,8 @@ public: OpenDocumentManager::Document* getClosestPreviousDocOtherThan (OpenDocumentManager::Document* oneToAvoid) const; - void clear(); + void restoreFromXML (Project& project, const XmlElement& xml); + XmlElement* createXML() const; private: void documentAboutToClose (OpenDocumentManager::Document*); diff --git a/extras/Introjucer/Source/Code Editor/jucer_SourceCodeEditor.cpp b/extras/Introjucer/Source/Code Editor/jucer_SourceCodeEditor.cpp index 3da14a210e..db9458dc1b 100644 --- a/extras/Introjucer/Source/Code Editor/jucer_SourceCodeEditor.cpp +++ b/extras/Introjucer/Source/Code Editor/jucer_SourceCodeEditor.cpp @@ -40,7 +40,7 @@ SourceCodeEditor::~SourceCodeEditor() SourceCodeDocument* doc = dynamic_cast (getDocument()); if (doc != nullptr) - doc->updateLastPosition (*editor); + doc->updateLastState (*editor); } void SourceCodeEditor::createEditor (CodeDocument& codeDocument) @@ -97,22 +97,45 @@ void SourceCodeEditor::valueTreeParentChanged (ValueTree&) void SourceCodeEditor::valueTreeRedirected (ValueTree&) { updateColourScheme(); } //============================================================================== +SourceCodeDocument::SourceCodeDocument (Project* project_, const File& file_) + : modDetector (file_), project (project_) +{ +} + +CodeDocument& SourceCodeDocument::getCodeDocument() +{ + if (codeDoc == nullptr) + { + codeDoc = new CodeDocument(); + reloadInternal(); + } + + return *codeDoc; +} + Component* SourceCodeDocument::createEditor() { SourceCodeEditor* e = new SourceCodeEditor (this); - e->createEditor (codeDoc); - applyLastPosition (*(e->editor)); + e->createEditor (getCodeDocument()); + applyLastState (*(e->editor)); return e; } void SourceCodeDocument::reloadFromFile() { + getCodeDocument(); + reloadInternal(); +} + +void SourceCodeDocument::reloadInternal() +{ + jassert (codeDoc != nullptr); modDetector.updateHash(); ScopedPointer in (modDetector.getFile().createInputStream()); if (in != nullptr) - codeDoc.loadFromStream (*in); + codeDoc->loadFromStream (*in); } bool SourceCodeDocument::save() @@ -122,24 +145,24 @@ bool SourceCodeDocument::save() { FileOutputStream fo (temp.getFile()); - if (! (fo.openedOk() && codeDoc.writeToStream (fo))) + if (! (fo.openedOk() && getCodeDocument().writeToStream (fo))) return false; } if (! temp.overwriteTargetFileWithTemporary()) return false; - codeDoc.setSavePoint(); + getCodeDocument().setSavePoint(); modDetector.updateHash(); return true; } -void SourceCodeDocument::updateLastPosition (CodeEditorComponent& editor) +void SourceCodeDocument::updateLastState (CodeEditorComponent& editor) { lastState = new CodeEditorComponent::State (editor); } -void SourceCodeDocument::applyLastPosition (CodeEditorComponent& editor) const +void SourceCodeDocument::applyLastState (CodeEditorComponent& editor) const { if (lastState != nullptr) lastState->restoreState (editor); diff --git a/extras/Introjucer/Source/Code Editor/jucer_SourceCodeEditor.h b/extras/Introjucer/Source/Code Editor/jucer_SourceCodeEditor.h index 4086582eca..d5476efe34 100644 --- a/extras/Introjucer/Source/Code Editor/jucer_SourceCodeEditor.h +++ b/extras/Introjucer/Source/Code Editor/jucer_SourceCodeEditor.h @@ -35,31 +35,21 @@ class SourceCodeDocument : public OpenDocumentManager::Document { public: //============================================================================== - SourceCodeDocument (Project* project_, const File& file_) - : modDetector (file_), project (project_) - { - reloadFromFile(); - } - - //============================================================================== - struct Type : public OpenDocumentManager::DocumentType - { - bool canOpenFile (const File& file) { return file.hasFileExtension ("cpp;h;hpp;mm;m;c;cc;cxx;txt;inc;tcc;xml;plist;rtf;html;htm;php;py;rb;cs"); } - Document* openFile (Project* project, const File& file) { return new SourceCodeDocument (project, file); } - }; - - //============================================================================== - bool loadedOk() const { return true; } - bool isForFile (const File& file) const { return getFile() == file; } - bool isForNode (const ValueTree& node) const { return false; } - bool refersToProject (Project& p) const { return project == &p; } - Project* getProject() const { return project; } - String getName() const { return getFile().getFileName(); } - String getType() const { return getFile().getFileExtension() + " file"; } - File getFile() const { return modDetector.getFile(); } - bool needsSaving() const { return codeDoc.hasChangedSinceSavePoint(); } - bool hasFileBeenModifiedExternally() { return modDetector.hasBeenModified(); } - void fileHasBeenRenamed (const File& newFile) { modDetector.fileHasBeenRenamed (newFile); } + SourceCodeDocument (Project*, const File&); + + bool loadedOk() const { return true; } + bool isForFile (const File& file) const { return getFile() == file; } + bool isForNode (const ValueTree& node) const { return false; } + bool refersToProject (Project& p) const { return project == &p; } + Project* getProject() const { return project; } + String getName() const { return getFile().getFileName(); } + String getType() const { return getFile().getFileExtension() + " file"; } + File getFile() const { return modDetector.getFile(); } + bool needsSaving() const { return codeDoc != nullptr && codeDoc->hasChangedSinceSavePoint(); } + bool hasFileBeenModifiedExternally() { return modDetector.hasBeenModified(); } + void fileHasBeenRenamed (const File& newFile) { modDetector.fileHasBeenRenamed (newFile); } + String getState() const { return lastState != nullptr ? lastState->toString() : String::empty; } + void restoreState (const String& state) { lastState = new CodeEditorComponent::State (state); } void reloadFromFile(); bool save(); @@ -67,15 +57,26 @@ public: Component* createEditor(); Component* createViewer() { return createEditor(); } - void updateLastPosition (CodeEditorComponent& editor); - void applyLastPosition (CodeEditorComponent& editor) const; + void updateLastState (CodeEditorComponent& editor); + void applyLastState (CodeEditorComponent& editor) const; + + CodeDocument& getCodeDocument(); + + //============================================================================== + struct Type : public OpenDocumentManager::DocumentType + { + bool canOpenFile (const File& file) { return file.hasFileExtension ("cpp;h;hpp;mm;m;c;cc;cxx;txt;inc;tcc;xml;plist;rtf;html;htm;php;py;rb;cs"); } + Document* openFile (Project* project, const File& file) { return new SourceCodeDocument (project, file); } + }; protected: FileModificationDetector modDetector; - CodeDocument codeDoc; + ScopedPointer codeDoc; Project* project; ScopedPointer lastState; + + void reloadInternal(); }; //============================================================================== diff --git a/extras/Introjucer/Source/Project/jucer_ProjectContentComponent.cpp b/extras/Introjucer/Source/Project/jucer_ProjectContentComponent.cpp index f470f8609c..6e1e0877c9 100644 --- a/extras/Introjucer/Source/Project/jucer_ProjectContentComponent.cpp +++ b/extras/Introjucer/Source/Project/jucer_ProjectContentComponent.cpp @@ -218,6 +218,31 @@ void ProjectContentComponent::saveTreeViewState() } } +void ProjectContentComponent::saveOpenDocumentList() +{ + if (project != nullptr) + { + ScopedPointer xml (recentDocumentList.createXML()); + + if (xml != nullptr) + getAppProperties().setValue ("lastDocs_" + project->getProjectUID(), xml); + } +} + +void ProjectContentComponent::reloadLastOpenDocuments() +{ + if (project != nullptr) + { + ScopedPointer xml (getAppProperties().getXmlValue ("lastDocs_" + project->getProjectUID())); + + if (xml != nullptr) + { + recentDocumentList.restoreFromXML (*project, *xml); + showDocument (recentDocumentList.getCurrentDocument()); + } + } +} + void ProjectContentComponent::changeListenerCallback (ChangeBroadcaster*) { updateMissingFileStatuses(); @@ -251,8 +276,11 @@ bool ProjectContentComponent::showDocument (OpenDocumentManager::Document* doc) if (doc->hasFileBeenModifiedExternally()) doc->reloadFromFile(); - if (doc == getCurrentDocument()) + if (doc == getCurrentDocument() && contentView != nullptr) + { + contentView->grabKeyboardFocus(); return true; + } recentDocumentList.newDocumentOpened (doc); @@ -265,7 +293,6 @@ void ProjectContentComponent::hideEditor() contentView = nullptr; updateMainWindowTitle(); commandManager->commandStatusChanged(); - recentDocumentList.clear(); } void ProjectContentComponent::hideDocument (OpenDocumentManager::Document* doc) @@ -303,7 +330,12 @@ bool ProjectContentComponent::setEditorComponent (Component* editor, OpenDocumen bool ProjectContentComponent::goToPreviousFile() { - return showDocument (recentDocumentList.getPrevious()); + OpenDocumentManager::Document* currentSourceDoc = recentDocumentList.getCurrentDocument(); + + if (currentSourceDoc != nullptr && currentSourceDoc != getCurrentDocument()) + return showDocument (currentSourceDoc); + else + return showDocument (recentDocumentList.getPrevious()); } bool ProjectContentComponent::goToNextFile() diff --git a/extras/Introjucer/Source/Project/jucer_ProjectContentComponent.h b/extras/Introjucer/Source/Project/jucer_ProjectContentComponent.h index 63c289f2ea..d346f29f95 100644 --- a/extras/Introjucer/Source/Project/jucer_ProjectContentComponent.h +++ b/extras/Introjucer/Source/Project/jucer_ProjectContentComponent.h @@ -44,7 +44,10 @@ public: Project* getProject() const noexcept { return project; } virtual void setProject (Project* project); + void saveTreeViewState(); + void saveOpenDocumentList(); + void reloadLastOpenDocuments(); bool showEditorForFile (const File& f); File getCurrentFile() const; diff --git a/extras/Introjucer/Source/Utility/jucer_JucerTreeViewBase.h b/extras/Introjucer/Source/Utility/jucer_JucerTreeViewBase.h index 2765fa89dc..aa67f71fbe 100644 --- a/extras/Introjucer/Source/Utility/jucer_JucerTreeViewBase.h +++ b/extras/Introjucer/Source/Utility/jucer_JucerTreeViewBase.h @@ -46,6 +46,8 @@ public: void itemSelectionChanged (bool isNowSelected); void itemDoubleClicked (const MouseEvent&); + void cancelDelayedSelectionTimer(); + //============================================================================== virtual Font getFont() const; virtual String getRenamingName() const = 0; @@ -89,7 +91,6 @@ public: protected: ProjectContentComponent* getProjectContentComponent() const; - void cancelDelayedSelectionTimer(); virtual void addSubItems() {} private: @@ -128,7 +129,17 @@ public: const ScopedPointer treeOpenness (getAppProperties().getXmlValue (opennessStateKey)); if (treeOpenness != nullptr) + { tree.restoreOpennessState (*treeOpenness, true); + + for (int i = tree.getNumSelectedItems(); --i >= 0;) + { + JucerTreeViewBase* item = dynamic_cast (tree.getSelectedItem (i)); + + if (item != nullptr) + item->cancelDelayedSelectionTimer(); + } + } } void saveOpenness() diff --git a/modules/juce_gui_basics/keyboard/juce_ModifierKeys.h b/modules/juce_gui_basics/keyboard/juce_ModifierKeys.h index 85d384edeb..5afaaec0e3 100644 --- a/modules/juce_gui_basics/keyboard/juce_ModifierKeys.h +++ b/modules/juce_gui_basics/keyboard/juce_ModifierKeys.h @@ -155,6 +155,9 @@ public: /** Represents a combination of all the mouse buttons at once. */ allMouseButtonModifiers = leftButtonModifier | rightButtonModifier | middleButtonModifier, + + /** Represents a combination of all the alt, ctrl and command key modifiers. */ + ctrlAltCommandModifiers = ctrlModifier | altModifier | commandModifier }; //============================================================================== @@ -171,11 +174,11 @@ public: /** Returns the raw flags for direct testing. */ inline int getRawFlags() const noexcept { return flags; } - inline const ModifierKeys withoutFlags (int rawFlagsToClear) const noexcept { return ModifierKeys (flags & ~rawFlagsToClear); } - inline const ModifierKeys withFlags (int rawFlagsToSet) const noexcept { return ModifierKeys (flags | rawFlagsToSet); } + ModifierKeys withoutFlags (int rawFlagsToClear) const noexcept { return ModifierKeys (flags & ~rawFlagsToClear); } + ModifierKeys withFlags (int rawFlagsToSet) const noexcept { return ModifierKeys (flags | rawFlagsToSet); } /** Tests a combination of flags and returns true if any of them are set. */ - inline bool testFlags (const int flagsToTest) const noexcept { return (flags & flagsToTest) != 0; } + bool testFlags (const int flagsToTest) const noexcept { return (flags & flagsToTest) != 0; } /** Returns the total number of mouse buttons that are down. */ int getNumMouseButtonsDown() const noexcept; diff --git a/modules/juce_gui_basics/widgets/juce_TreeView.cpp b/modules/juce_gui_basics/widgets/juce_TreeView.cpp index 9f618f16f0..6c4163c3e8 100644 --- a/modules/juce_gui_basics/widgets/juce_TreeView.cpp +++ b/modules/juce_gui_basics/widgets/juce_TreeView.cpp @@ -761,86 +761,100 @@ void TreeView::scrollToKeepItemVisible (TreeViewItem* item) } } -bool TreeView::keyPressed (const KeyPress& key) +void TreeView::toggleOpenSelectedItem() { - if (key.isKeyCode (KeyPress::upKey)) - { - moveSelectedRow (-1); - } - else if (key.isKeyCode (KeyPress::downKey)) - { - moveSelectedRow (1); - } - else if (key.isKeyCode (KeyPress::pageDownKey) || key.isKeyCode (KeyPress::pageUpKey)) + TreeViewItem* const firstSelected = getSelectedItem (0); + if (firstSelected != nullptr) + firstSelected->setOpen (! firstSelected->isOpen()); +} + +void TreeView::moveOutOfSelectedItem() +{ + TreeViewItem* const firstSelected = getSelectedItem (0); + + if (firstSelected != nullptr) { - if (rootItem != nullptr) + if (firstSelected->isOpen()) { - int rowsOnScreen = getHeight() / jmax (1, rootItem->itemHeight); + firstSelected->setOpen (false); + } + else + { + TreeViewItem* parent = firstSelected->parentItem; - if (key.isKeyCode (KeyPress::pageUpKey)) - rowsOnScreen = -rowsOnScreen; + if ((! rootItemVisible) && parent == rootItem) + parent = nullptr; - if (rowsOnScreen != 0) - moveSelectedRow (rowsOnScreen); + if (parent != nullptr) + { + parent->setSelected (true, true); + scrollToKeepItemVisible (parent); + } } } - else if (key.isKeyCode (KeyPress::homeKey)) - { - moveSelectedRow (-0x3fffffff); - } - else if (key.isKeyCode (KeyPress::endKey)) - { - moveSelectedRow (0x3fffffff); - } - else if (key.isKeyCode (KeyPress::returnKey)) +} + +void TreeView::moveIntoSelectedItem() +{ + TreeViewItem* const firstSelected = getSelectedItem (0); + + if (firstSelected != nullptr) { - TreeViewItem* const firstSelected = getSelectedItem (0); - if (firstSelected != nullptr) - firstSelected->setOpen (! firstSelected->isOpen()); + if (firstSelected->isOpen() || ! firstSelected->mightContainSubItems()) + moveSelectedRow (1); + else + firstSelected->setOpen (true); } - else if (key.isKeyCode (KeyPress::leftKey)) +} + +void TreeView::moveByPages (int numPages) +{ + TreeViewItem* currentItem = getSelectedItem (0); + + if (currentItem != nullptr) { - TreeViewItem* const firstSelected = getSelectedItem (0); + const Rectangle pos (currentItem->getItemPosition (false)); + const int targetY = pos.getY() + numPages * (getHeight() - pos.getHeight()); + int currentRow = currentItem->getRowNumberInTree(); - if (firstSelected != nullptr) + for (;;) { - if (firstSelected->isOpen()) - { - firstSelected->setOpen (false); - } - else - { - TreeViewItem* parent = firstSelected->parentItem; + moveSelectedRow (numPages); + currentItem = getSelectedItem (0); - if ((! rootItemVisible) && parent == rootItem) - parent = nullptr; + if (currentItem == nullptr) + break; - if (parent != nullptr) - { - parent->setSelected (true, true); - scrollToKeepItemVisible (parent); - } - } - } - } - else if (key.isKeyCode (KeyPress::rightKey)) - { - TreeViewItem* const firstSelected = getSelectedItem (0); + const int y = currentItem->getItemPosition (false).getY(); + if ((numPages < 0 && y <= targetY) || (numPages > 0 && y >= targetY)) + break; - if (firstSelected != nullptr) - { - if (firstSelected->isOpen() || ! firstSelected->mightContainSubItems()) - moveSelectedRow (1); - else - firstSelected->setOpen (true); + const int newRow = currentItem->getRowNumberInTree(); + if (newRow == currentRow) + break; + + currentRow = newRow; } } - else +} + +bool TreeView::keyPressed (const KeyPress& key) +{ + if (rootItem != nullptr + && ! key.getModifiers().testFlags (ModifierKeys::ctrlAltCommandModifiers)) { - return false; + if (key.getKeyCode() == KeyPress::upKey) { moveSelectedRow (-1); return true; } + if (key.getKeyCode() == KeyPress::downKey) { moveSelectedRow (1); return true; } + if (key.getKeyCode() == KeyPress::homeKey) { moveSelectedRow (-0x3fffffff); return true; } + if (key.getKeyCode() == KeyPress::endKey) { moveSelectedRow (0x3fffffff); return true; } + if (key.getKeyCode() == KeyPress::pageUpKey) { moveByPages (-1); return true; } + if (key.getKeyCode() == KeyPress::pageDownKey) { moveByPages (1); return true; } + if (key.getKeyCode() == KeyPress::returnKey) { toggleOpenSelectedItem(); return true; } + if (key.getKeyCode() == KeyPress::leftKey) { moveOutOfSelectedItem(); return true; } + if (key.getKeyCode() == KeyPress::rightKey) { moveIntoSelectedItem(); return true; } } - return true; + return false; } void TreeView::itemsChanged() noexcept diff --git a/modules/juce_gui_basics/widgets/juce_TreeView.h b/modules/juce_gui_basics/widgets/juce_TreeView.h index 172ea2f8f3..f25c020771 100644 --- a/modules/juce_gui_basics/widgets/juce_TreeView.h +++ b/modules/juce_gui_basics/widgets/juce_TreeView.h @@ -820,6 +820,10 @@ private: void hideDragHighlight() noexcept; void handleDrag (const StringArray& files, const SourceDetails&); void handleDrop (const StringArray& files, const SourceDetails&); + void toggleOpenSelectedItem(); + void moveOutOfSelectedItem(); + void moveIntoSelectedItem(); + void moveByPages (int numPages); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TreeView); }; diff --git a/modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.cpp b/modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.cpp index 8e2caa656e..59f72682a9 100644 --- a/modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.cpp +++ b/modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.cpp @@ -1425,3 +1425,18 @@ void CodeEditorComponent::State::restoreState (CodeEditorComponent& editor) cons if (lastTopLine > 0 && lastTopLine < editor.getDocument().getNumLines()) editor.scrollToLine (lastTopLine); } + +CodeEditorComponent::State::State (const String& s) +{ + StringArray tokens; + tokens.addTokens (s, ":", String::empty); + + lastTopLine = tokens[0].getIntValue(); + lastCaretPos = tokens[1].getIntValue(); + lastSelectionEnd = tokens[2].getIntValue(); +} + +String CodeEditorComponent::State::toString() const +{ + return String (lastTopLine) + ":" + String (lastCaretPos) + ":" + String (lastSelectionEnd); +} diff --git a/modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.h b/modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.h index bd74e634cc..6d109b006c 100644 --- a/modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.h +++ b/modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.h @@ -163,11 +163,16 @@ public: { /** Creates an object containing the state of the given editor. */ State (const CodeEditorComponent& editor); + /** Creates a state object from a string that was previously created with toString(). */ + State (const String& stringifiedVersion); State (const State& other) noexcept; /** Updates the given editor with this saved state. */ void restoreState (CodeEditorComponent& editor) const; + /** Returns a stringified version of this state that can be used to recreate it later. */ + String toString() const; + private: int lastTopLine, lastCaretPos, lastSelectionEnd; };