1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

MultiDocumentPanel: Fix reporting wrong active document, avoid reordering

This commit is contained in:
attila 2022-06-15 17:59:14 +02:00
parent 0adbfee99d
commit 3acc71f7df
3 changed files with 154 additions and 100 deletions

View file

@ -151,6 +151,12 @@ public:
}
}
void activeDocumentChanged() override
{
if (auto* activeDoc = getActiveDocument())
Logger::outputDebugString ("activeDocumentChanged() to " + activeDoc->getName());
}
private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DemoMultiDocumentPanel)
};
@ -171,9 +177,26 @@ public:
showInTabsButton.onClick = [this] { updateLayoutMode(); };
addAndMakeVisible (showInTabsButton);
addNoteButton.onClick = [this] { addNote ("Note " + String (multiDocumentPanel.getNumDocuments() + 1), "Hello World!"); };
oneDocShouldBeFullscreenButton.onClick = [this]
{
multiDocumentPanel.useFullscreenWhenOneDocument (oneDocShouldBeFullscreenButton.getToggleState());
};
addAndMakeVisible (oneDocShouldBeFullscreenButton);
oneDocShouldBeFullscreenButton.setToggleState (false, juce::sendNotification);
addNoteButton.onClick = [this]
{
addNote ("Note " + String (noteCounter), "Hello World! " + String (noteCounter));
++noteCounter;
};
addAndMakeVisible (addNoteButton);
closeActiveDocumentButton.onClick = [this]
{
multiDocumentPanel.closeDocumentAsync (multiDocumentPanel.getActiveDocument(), false, [] (auto) {});
};
addAndMakeVisible (closeActiveDocumentButton);
closeApplicationButton.onClick = [this]
{
multiDocumentPanel.closeAllDocumentsAsync (true, [] (bool allSaved)
@ -191,7 +214,7 @@ public:
addNote ("Notes Demo", "You can drag-and-drop text files onto this page to open them as notes..");
addExistingNotes();
setSize (500, 500);
setSize (650, 500);
}
void paint (Graphics& g) override
@ -203,10 +226,15 @@ public:
{
auto area = getLocalBounds();
auto buttonArea = area.removeFromTop (28).reduced (2);
closeApplicationButton.setBounds (buttonArea.removeFromRight (150));
addNoteButton .setBounds (buttonArea.removeFromRight (150));
showInTabsButton .setBounds (buttonArea);
auto topButtonRow = area.removeFromTop (28).reduced (2);
showInTabsButton .setBounds (topButtonRow.removeFromLeft (150));
closeApplicationButton .setBounds (topButtonRow.removeFromRight (150));
addNoteButton .setBounds (topButtonRow.removeFromRight (150));
closeActiveDocumentButton .setBounds (topButtonRow.removeFromRight (150));
oneDocShouldBeFullscreenButton.setBounds (area.removeFromTop (28).reduced (2).removeFromLeft (240));
multiDocumentPanel.setBounds (area);
}
@ -261,11 +289,14 @@ private:
createNotesForFiles (files);
}
ToggleButton showInTabsButton { "Show with tabs" };
TextButton addNoteButton { "Create a new note" },
closeApplicationButton { "Close app" };
ToggleButton showInTabsButton { "Show with tabs" };
ToggleButton oneDocShouldBeFullscreenButton { "Fill screen when only one note is open" };
TextButton addNoteButton { "Create a new note" },
closeApplicationButton { "Close app" },
closeActiveDocumentButton { "Close active document" };
DemoMultiDocumentPanel multiDocumentPanel;
int noteCounter = 1;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MDIDemo)
};

View file

@ -56,19 +56,19 @@ void MultiDocumentPanelWindow::closeButtonPressed()
void MultiDocumentPanelWindow::activeWindowStatusChanged()
{
DocumentWindow::activeWindowStatusChanged();
updateOrder();
updateActiveDocument();
}
void MultiDocumentPanelWindow::broughtToFront()
{
DocumentWindow::broughtToFront();
updateOrder();
updateActiveDocument();
}
void MultiDocumentPanelWindow::updateOrder()
void MultiDocumentPanelWindow::updateActiveDocument()
{
if (auto* owner = getOwner())
owner->updateOrder();
owner->updateActiveDocumentFromUIState();
}
MultiDocumentPanel* MultiDocumentPanelWindow::getOwner() const noexcept
@ -84,7 +84,7 @@ struct MultiDocumentPanel::TabbedComponentInternal : public TabbedComponent
void currentTabChanged (int, const String&) override
{
if (auto* owner = findParentComponentOfClass<MultiDocumentPanel>())
owner->updateOrder();
owner->updateActiveDocumentFromUIState();
}
};
@ -262,22 +262,85 @@ bool MultiDocumentPanel::addDocument (Component* const component,
}
resized();
activeDocumentChanged();
updateActiveDocument (component);
return true;
}
void MultiDocumentPanel::closeDocumentInternal (Component* component)
void MultiDocumentPanel::recreateLayout()
{
tabComponent.reset();
for (int i = getNumChildComponents(); --i >= 0;)
{
std::unique_ptr<MultiDocumentPanelWindow> dw (dynamic_cast<MultiDocumentPanelWindow*> (getChildComponent (i)));
if (dw != nullptr)
{
dw->getContentComponent()->getProperties().set ("mdiDocumentPos_", dw->getWindowStateAsString());
dw->clearContentComponent();
}
}
resized();
auto tempComps = components;
components.clear();
{
// We want to preserve the activeComponent, so we are blocking the changes originating
// from addDocument()
const ScopedValueSetter<bool> scope { isLayoutBeingChanged, true };
for (auto* c : tempComps)
addDocument (c,
Colour ((uint32) static_cast<int> (c->getProperties().getWithDefault ("mdiDocumentBkg_",
(int) Colours::white.getARGB()))),
MultiDocHelpers::shouldDeleteComp (c));
}
if (activeComponent != nullptr)
setActiveDocument (activeComponent);
updateActiveDocumentFromUIState();
}
void MultiDocumentPanel::closeDocumentInternal (Component* componentToClose)
{
// Intellisense warns about component being uninitialised.
// I'm not sure how a function argument could be uninitialised.
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6001)
const OptionalScopedPointer<Component> component { componentToClose,
MultiDocHelpers::shouldDeleteComp (componentToClose) };
component->removeComponentListener (this);
const bool shouldDelete = MultiDocHelpers::shouldDeleteComp (component);
component->getProperties().remove ("mdiDocumentDelete_");
component->getProperties().remove ("mdiDocumentBkg_");
const auto removedIndex = components.indexOf (component);
if (removedIndex < 0)
{
jassertfalse;
return;
}
components.remove (removedIndex);
// See if the active document needs to change because of closing a document. It should only
// change if we closed the active document. If so, the next active document should be the
// subsequent one.
if (component == activeComponent)
{
auto* newActiveComponent = components[jmin (removedIndex, components.size() - 1)];
updateActiveDocument (newActiveComponent);
}
// We update the UI to reflect the new state, but we want to prevent the UI state callback
// to change the active document.
const ScopedValueSetter<bool> scope { isLayoutBeingChanged, true };
if (mode == FloatingWindows)
{
for (auto* child : getChildren())
@ -292,11 +355,6 @@ void MultiDocumentPanel::closeDocumentInternal (Component* component)
}
}
if (shouldDelete)
delete component;
components.removeFirstMatchingValue (component);
if (isFullscreenWhenOneDocument() && components.size() == 1)
{
for (int i = getNumChildComponents(); --i >= 0;)
@ -307,13 +365,11 @@ void MultiDocumentPanel::closeDocumentInternal (Component* component)
dw->clearContentComponent();
}
addAndMakeVisible (components.getFirst());
addAndMakeVisible (getActiveDocument());
}
}
else
{
jassert (components.indexOf (component) >= 0);
if (tabComponent != nullptr)
{
for (int i = tabComponent->getNumTabs(); --i >= 0;)
@ -325,25 +381,18 @@ void MultiDocumentPanel::closeDocumentInternal (Component* component)
removeChildComponent (component);
}
if (shouldDelete)
delete component;
if (tabComponent != nullptr && tabComponent->getNumTabs() <= numDocsBeforeTabsUsed)
if (components.size() <= numDocsBeforeTabsUsed && getActiveDocument() != nullptr)
{
tabComponent.reset();
components.removeFirstMatchingValue (component);
if (components.size() > 0 && tabComponent == nullptr)
addAndMakeVisible (components.getFirst());
addAndMakeVisible (getActiveDocument());
}
}
resized();
// This ensures that the active tab is painted properly when a tab is closed!
if (auto* activeComponent = getActiveDocument())
setActiveDocument (activeComponent);
activeDocumentChanged();
if (auto* activeDocument = getActiveDocument())
setActiveDocument (activeDocument);
JUCE_END_IGNORE_WARNINGS_MSVC
}
@ -438,15 +487,7 @@ Component* MultiDocumentPanel::getDocument (const int index) const noexcept
Component* MultiDocumentPanel::getActiveDocument() const noexcept
{
if (mode == FloatingWindows)
{
for (auto* child : getChildren())
if (auto* dw = dynamic_cast<MultiDocumentPanelWindow*> (child))
if (dw->isActiveWindow())
return dw->getContentComponent();
}
return components.getLast();
return activeComponent;
}
void MultiDocumentPanel::setActiveDocument (Component* component)
@ -490,7 +531,10 @@ void MultiDocumentPanel::setMaximumNumDocuments (const int newNumber)
void MultiDocumentPanel::useFullscreenWhenOneDocument (const bool shouldUseTabs)
{
numDocsBeforeTabsUsed = shouldUseTabs ? 1 : 0;
const auto newNumDocsBeforeTabsUsed = shouldUseTabs ? 1 : 0;
if (std::exchange (numDocsBeforeTabsUsed, newNumDocsBeforeTabsUsed) != newNumDocsBeforeTabsUsed)
recreateLayout();
}
bool MultiDocumentPanel::isFullscreenWhenOneDocument() const noexcept
@ -501,38 +545,8 @@ bool MultiDocumentPanel::isFullscreenWhenOneDocument() const noexcept
//==============================================================================
void MultiDocumentPanel::setLayoutMode (const LayoutMode newLayoutMode)
{
if (mode != newLayoutMode)
{
mode = newLayoutMode;
if (mode == FloatingWindows)
{
tabComponent.reset();
}
else
{
for (int i = getNumChildComponents(); --i >= 0;)
{
std::unique_ptr<MultiDocumentPanelWindow> dw (dynamic_cast<MultiDocumentPanelWindow*> (getChildComponent (i)));
if (dw != nullptr)
{
dw->getContentComponent()->getProperties().set ("mdiDocumentPos_", dw->getWindowStateAsString());
dw->clearContentComponent();
}
}
}
resized();
auto tempComps = components;
components.clear();
for (auto* c : tempComps)
addDocument (c,
Colour ((uint32) static_cast<int> (c->getProperties().getWithDefault ("mdiDocumentBkg_", (int) Colours::white.getARGB()))),
MultiDocHelpers::shouldDeleteComp (c));
}
if (std::exchange (mode, newLayoutMode) != newLayoutMode)
recreateLayout();
}
void MultiDocumentPanel::setBackgroundColour (Colour newBackgroundColour)
@ -590,31 +604,36 @@ void MultiDocumentPanel::componentNameChanged (Component&)
}
}
void MultiDocumentPanel::updateOrder()
void MultiDocumentPanel::updateActiveDocumentFromUIState()
{
auto oldList = components;
if (mode == FloatingWindows)
auto* newActiveComponent = [&]() -> Component*
{
components.clear();
for (auto* child : getChildren())
if (auto* dw = dynamic_cast<MultiDocumentPanelWindow*> (child))
components.add (dw->getContentComponent());
}
else
{
if (tabComponent != nullptr)
if (mode == FloatingWindows)
{
if (auto* current = tabComponent->getCurrentContentComponent())
for (auto* c : components)
{
components.removeFirstMatchingValue (current);
components.add (current);
if (auto* window = static_cast<MultiDocumentPanelWindow*> (c->getParentComponent()))
if (window->isActiveWindow())
return c;
}
}
}
if (components != oldList)
if (tabComponent != nullptr)
if (auto* current = tabComponent->getCurrentContentComponent())
return current;
return activeComponent;
}();
updateActiveDocument (newActiveComponent);
}
void MultiDocumentPanel::updateActiveDocument (Component* component)
{
if (isLayoutBeingChanged)
return;
if (std::exchange (activeComponent, component) != component)
activeDocumentChanged();
}

View file

@ -64,7 +64,7 @@ public:
private:
//==============================================================================
void updateOrder();
void updateActiveDocument();
MultiDocumentPanel* getOwner() const noexcept;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MultiDocumentPanelWindow)
@ -362,11 +362,15 @@ private:
friend class MultiDocumentPanelWindow;
Component* getContainerComp (Component*) const;
void updateOrder();
void updateActiveDocumentFromUIState();
void updateActiveDocument (Component*);
void addWindow (Component*);
void recreateLayout();
LayoutMode mode = MaximisedWindowsWithTabs;
Array<Component*> components;
Component* activeComponent = nullptr;
bool isLayoutBeingChanged = false;
std::unique_ptr<TabbedComponent> tabComponent;
Colour backgroundColour { Colours::lightblue };
int maximumNumDocuments = 0, numDocsBeforeTabsUsed = 0;