Fix memory leaks in MainWindow

Temporary/created subdocks must be deleted automatically, and children
that are not part of the window hierarchy must be deleted explicitly
now (using some std::unique_ptrs).
This commit is contained in:
David Capello 2022-03-28 16:02:27 -03:00 committed by David Capello
parent fa21d87ba8
commit 4858a5103e
4 changed files with 108 additions and 99 deletions

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2021 Igara Studio S.A.
// Copyright (C) 2021-2022 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -88,19 +88,24 @@ Dock::Dock()
initTheme();
}
void Dock::reset()
void Dock::resetDocks()
{
for (int i = 0; i < kSides; ++i) {
auto child = m_sides[i];
if (!child)
continue;
else if (auto subdock = dynamic_cast<Dock*>(child)) {
subdock->reset();
subdock->resetDocks();
if (subdock->m_autoDelete)
delete subdock;
}
else if (auto tabs = dynamic_cast<DockTabs*>(child)) {
for (auto child2 : tabs->children()) {
if (auto subdock2 = dynamic_cast<Dock*>(child2))
subdock2->reset();
if (auto subdock2 = dynamic_cast<Dock*>(child2)) {
subdock2->resetDocks();
if (subdock2->m_autoDelete)
delete subdock2;
}
}
}
m_sides[i] = nullptr;
@ -149,6 +154,7 @@ void Dock::dockRelativeTo(ui::Widget* relative,
ASSERT(parent);
Dock* subdock = new Dock;
subdock->m_autoDelete = true;
parent->replaceChild(relative, subdock);
subdock->dock(CENTER, relative);
subdock->dock(side, widget, prefSize);
@ -204,6 +210,7 @@ Dock* Dock::subdock(int side)
auto oldWidget = m_sides[i];
auto newSubdock = new Dock;
newSubdock->m_autoDelete = true;
setSide(i, newSubdock);
if (oldWidget) {

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2021 Igara Studio S.A.
// Copyright (C) 2021-2022 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -33,7 +33,7 @@ public:
Dock();
void reset();
void resetDocks();
// side = ui::LEFT, or ui::RIGHT, etc.
void dock(int side, ui::Widget* widget, const gfx::Size& prefSize = gfx::Size());
@ -73,6 +73,7 @@ private:
std::array<Widget*, kSides> m_sides;
std::array<int, kSides> m_aligns;
std::array<gfx::Size, kSides> m_sizes;
bool m_autoDelete = false;
// Used to drag-and-drop sides.
ui::Widget* m_capturedWidget = nullptr;

View File

@ -86,8 +86,10 @@ public:
MainWindow::MainWindow()
: ui::Window(ui::Window::DesktopWindow)
, m_tooltipManager(new TooltipManager)
, m_dock(new Dock)
, m_customizableDock(new Dock)
, m_layoutSelector(new LayoutSelector)
, m_mode(NormalMode)
, m_homeView(nullptr)
, m_scalePanic(nullptr)
@ -110,9 +112,8 @@ MainWindow::MainWindow()
// Refer to https://github.com/aseprite/aseprite/issues/3914
void MainWindow::initialize()
{
m_tooltipManager = new TooltipManager;
m_menuBar = new MainMenuBar;
m_layoutSelector = new LayoutSelector;
m_menuBar = std::make_unique<MainMenuBar>();
m_layoutSelector = std::make_unique<LayoutSelector>();
// Register commands to load menus+shortcuts for these commands
Editor::registerCommands();
@ -123,20 +124,20 @@ void MainWindow::initialize()
// Setup the main menubar
m_menuBar->setMenu(AppMenus::instance()->getRootMenu());
m_notifications = new Notifications();
m_statusBar = new StatusBar(m_tooltipManager);
m_toolBar = new ToolBar();
m_tabsBar = new WorkspaceTabs(this);
m_workspace = new Workspace();
m_previewEditor = new PreviewEditorWindow();
m_colorBar = new ColorBar(m_tooltipManager);
m_contextBar = new ContextBar(m_tooltipManager, m_colorBar);
m_notifications = std::make_unique<Notifications>();
m_statusBar = std::make_unique<StatusBar>(m_tooltipManager);
m_toolBar = std::make_unique<ToolBar>();
m_tabsBar = std::make_unique<WorkspaceTabs>(this);
m_workspace = std::make_unique<Workspace>();
m_previewEditor = std::make_unique<PreviewEditorWindow>();
m_colorBar = std::make_unique<ColorBar>(m_tooltipManager);
m_contextBar = std::make_unique<ContextBar>(m_tooltipManager, m_colorBar.get());
// The timeline (AniControls) tooltips will use the keyboard
// shortcuts loaded above.
m_timeline = new Timeline(m_tooltipManager);
m_timeline = std::make_unique<Timeline>(m_tooltipManager);
m_workspace->setTabsBar(m_tabsBar);
m_workspace->setTabsBar(m_tabsBar.get());
m_workspace->BeforeViewChanged.connect(&MainWindow::onBeforeViewChange, this);
m_workspace->ActiveViewChanged.connect(&MainWindow::onActiveViewChange, this);
@ -156,20 +157,20 @@ void MainWindow::initialize()
addChild(m_tooltipManager);
addChild(m_dock);
auto customizableDockPlaceholder = new Widget;
customizableDockPlaceholder->addChild(m_customizableDock);
customizableDockPlaceholder->InitTheme.connect([this] {
m_customizableDockPlaceholder = std::make_unique<Widget>();
m_customizableDockPlaceholder->addChild(m_customizableDock);
m_customizableDockPlaceholder->InitTheme.connect([this] {
auto theme = static_cast<skin::SkinTheme*>(this->theme());
m_customizableDock->setBgColor(theme->colors.workspace());
});
customizableDockPlaceholder->initTheme();
m_customizableDockPlaceholder->initTheme();
m_dock->top()->right()->dock(ui::RIGHT, m_notifications);
m_dock->top()->right()->dock(ui::CENTER, m_layoutSelector);
m_dock->top()->dock(ui::BOTTOM, m_tabsBar);
m_dock->top()->dock(ui::CENTER, m_menuBar);
m_dock->dock(ui::CENTER, customizableDockPlaceholder);
m_dock->dock(ui::BOTTOM, m_statusBar);
m_dock->top()->right()->dock(ui::RIGHT, m_notifications.get());
m_dock->top()->right()->dock(ui::CENTER, m_layoutSelector.get());
m_dock->top()->dock(ui::BOTTOM, m_tabsBar.get());
m_dock->top()->dock(ui::CENTER, m_menuBar.get());
m_dock->dock(ui::CENTER, m_customizableDockPlaceholder.get());
m_dock->dock(ui::BOTTOM, m_statusBar.get());
setDefaultLayout();
@ -190,45 +191,43 @@ void MainWindow::initialize()
MainWindow::~MainWindow()
{
m_dock->reset();
m_customizableDock->reset();
m_dock->resetDocks();
m_customizableDock->resetDocks();
delete m_scalePanic;
m_layoutSelector.reset();
m_scalePanic.reset();
#ifdef ENABLE_SCRIPTING
if (m_devConsoleView) {
if (m_devConsoleView->parent() && m_workspace)
m_workspace->removeView(m_devConsoleView);
delete m_devConsoleView;
m_workspace->removeView(m_devConsoleView.get());
m_devConsoleView.reset();
}
#endif
if (m_browserView) {
if (m_browserView->parent() && m_workspace)
m_workspace->removeView(m_browserView);
delete m_browserView;
m_workspace->removeView(m_browserView.get());
m_browserView.reset();
}
if (m_homeView) {
if (m_homeView->parent() && m_workspace)
m_workspace->removeView(m_homeView);
delete m_homeView;
m_workspace->removeView(m_homeView.get());
m_homeView.reset();
}
if (m_contextBar)
delete m_contextBar;
if (m_previewEditor)
delete m_previewEditor;
m_contextBar.reset();
m_previewEditor.reset();
// Destroy the workspace first so ~Editor can dettach slots from
// ColorBar. TODO this is a terrible hack for slot/signal stuff,
// connections should be handle in a better/safer way.
if (m_workspace)
delete m_workspace;
m_workspace.reset();
// Remove the root-menu from the menu-bar (because the rootmenu
// module should destroy it).
if (m_menuBar)
m_menuBar->setMenu(NULL);
m_menuBar->setMenu(nullptr);
}
void MainWindow::onLanguageChange()
@ -246,8 +245,8 @@ DocView* MainWindow::getDocView()
HomeView* MainWindow::getHomeView()
{
if (!m_homeView)
m_homeView = new HomeView;
return m_homeView;
m_homeView = std::make_unique<HomeView>();
return m_homeView.get();
}
#ifdef ENABLE_UPDATER
@ -284,20 +283,20 @@ void MainWindow::showHomeOnOpen()
// Show "Home" tab in the first position, and select it only if
// there is no other view selected.
m_workspace->addView(m_homeView, 0);
m_workspace->addView(m_homeView.get(), 0);
if (selectedTab)
m_tabsBar->selectTab(selectedTab);
else
m_tabsBar->selectTab(m_homeView);
m_tabsBar->selectTab(m_homeView.get());
}
}
void MainWindow::showHome()
{
if (!getHomeView()->parent()) {
m_workspace->addView(m_homeView, 0);
m_workspace->addView(m_homeView.get(), 0);
}
m_tabsBar->selectTab(m_homeView);
m_tabsBar->selectTab(m_homeView.get());
}
void MainWindow::showDefaultStatusBar()
@ -312,19 +311,19 @@ void MainWindow::showDefaultStatusBar()
bool MainWindow::isHomeSelected() const
{
return (m_homeView && m_workspace->activeView() == m_homeView);
return (m_homeView && m_workspace->activeView() == m_homeView.get());
}
void MainWindow::showBrowser(const std::string& filename, const std::string& section)
{
if (!m_browserView)
m_browserView = new BrowserView;
m_browserView = std::make_unique<BrowserView>();
m_browserView->loadFile(filename, section);
if (!m_browserView->parent()) {
m_workspace->addView(m_browserView);
m_tabsBar->selectTab(m_browserView);
m_workspace->addView(m_browserView.get());
m_tabsBar->selectTab(m_browserView.get());
}
}
@ -332,11 +331,11 @@ void MainWindow::showDevConsole()
{
#ifdef ENABLE_SCRIPTING
if (!m_devConsoleView)
m_devConsoleView = new DevConsoleView;
m_devConsoleView = std::make_unique<DevConsoleView>();
if (!m_devConsoleView->parent()) {
m_workspace->addView(m_devConsoleView);
m_tabsBar->selectTab(m_devConsoleView);
m_workspace->addView(m_devConsoleView.get());
m_tabsBar->selectTab(m_devConsoleView.get());
}
#endif
}
@ -376,28 +375,28 @@ void MainWindow::popTimeline()
void MainWindow::setDefaultLayout()
{
m_customizableDock->reset();
m_customizableDock->dock(ui::LEFT, m_colorBar);
m_customizableDock->center()->dock(ui::TOP, m_contextBar);
m_customizableDock->center()->dock(ui::RIGHT, m_toolBar);
m_customizableDock->resetDocks();
m_customizableDock->dock(ui::LEFT, m_colorBar.get());
m_customizableDock->center()->dock(ui::TOP, m_contextBar.get());
m_customizableDock->center()->dock(ui::RIGHT, m_toolBar.get());
m_customizableDock->center()->center()->dock(ui::BOTTOM,
m_timeline,
m_timeline.get(),
gfx::Size(64 * guiscale(), 64 * guiscale()));
m_customizableDock->center()->center()->dock(ui::CENTER, m_workspace);
m_customizableDock->center()->center()->dock(ui::CENTER, m_workspace.get());
layout();
}
void MainWindow::setDefaultMirrorLayout()
{
m_customizableDock->reset();
m_customizableDock->dock(ui::RIGHT, m_colorBar);
m_customizableDock->center()->dock(ui::TOP, m_contextBar);
m_customizableDock->center()->dock(ui::LEFT, m_toolBar);
m_customizableDock->resetDocks();
m_customizableDock->dock(ui::RIGHT, m_colorBar.get());
m_customizableDock->center()->dock(ui::TOP, m_contextBar.get());
m_customizableDock->center()->dock(ui::LEFT, m_toolBar.get());
m_customizableDock->center()->center()->dock(ui::BOTTOM,
m_timeline,
m_timeline.get(),
gfx::Size(64 * guiscale(), 64 * guiscale()));
m_customizableDock->center()->center()->dock(ui::CENTER, m_workspace);
m_customizableDock->center()->center()->dock(ui::CENTER, m_workspace.get());
layout();
}
@ -438,7 +437,8 @@ void MainWindow::onResize(ui::ResizeEvent& ev)
if ((scale > 2) && (!m_scalePanic)) {
const gfx::Size wa = nativeWindow->screen()->workarea().size();
if ((wa.w / scale < 256 || wa.h / scale < 256)) {
showNotification(m_scalePanic = new ScreenScalePanic);
m_scalePanic = std::make_unique<ScreenScalePanic>();
showNotification(m_scalePanic.get());
}
}
}
@ -541,7 +541,7 @@ void MainWindow::onContextMenuTab(Tabs* tabs, TabView* tabView)
WorkspaceView* view = dynamic_cast<WorkspaceView*>(tabView);
ASSERT(view);
if (view)
view->onTabPopup(m_workspace);
view->onTabPopup(m_workspace.get());
}
void MainWindow::onTabsContainerDoubleClicked(Tabs* tabs)
@ -627,11 +627,11 @@ void MainWindow::configureWorkspaceLayout()
if (os::System::instance()->menus() == nullptr || pref.general.showMenuBar()) {
if (!m_menuBar->parent())
m_dock->top()->dock(CENTER, m_menuBar);
m_dock->top()->dock(CENTER, m_menuBar.get());
}
else {
if (m_menuBar->parent())
m_dock->undock(m_menuBar);
m_dock->undock(m_menuBar.get());
}
m_menuBar->setVisible(normal);
@ -650,7 +650,7 @@ void MainWindow::configureWorkspaceLayout()
auto timelinePosition = pref.general.timelinePosition();
int side = ui::BOTTOM;
m_customizableDock->undock(m_timeline);
m_customizableDock->undock(m_timeline.get());
switch (timelinePosition) {
case gen::TimelinePosition::LEFT: side = ui::LEFT; break;
@ -659,7 +659,7 @@ void MainWindow::configureWorkspaceLayout()
}
m_customizableDock->center()->center()->dock(side,
m_timeline,
m_timeline.get(),
gfx::Size(64 * guiscale(), 64 * guiscale()));
m_timeline->setVisible(isDoc && (m_mode == NormalMode || m_mode == ContextBarAndTimelineMode) &&

View File

@ -55,13 +55,13 @@ public:
MainWindow();
~MainWindow();
MainMenuBar* getMenuBar() { return m_menuBar; }
ContextBar* getContextBar() { return m_contextBar; }
StatusBar* statusBar() { return m_statusBar; }
WorkspaceTabs* getTabsBar() { return m_tabsBar; }
Timeline* getTimeline() { return m_timeline; }
Workspace* getWorkspace() { return m_workspace; }
PreviewEditorWindow* getPreviewEditor() { return m_previewEditor; }
MainMenuBar* getMenuBar() { return m_menuBar.get(); }
ContextBar* getContextBar() { return m_contextBar.get(); }
StatusBar* statusBar() { return m_statusBar.get(); }
WorkspaceTabs* getTabsBar() { return m_tabsBar.get(); }
Timeline* getTimeline() { return m_timeline.get(); }
Workspace* getWorkspace() { return m_workspace.get(); }
PreviewEditorWindow* getPreviewEditor() { return m_previewEditor.get(); }
#ifdef ENABLE_UPDATER
CheckUpdateDelegate* getCheckUpdateDelegate();
#endif
@ -130,23 +130,24 @@ private:
ui::TooltipManager* m_tooltipManager;
Dock* m_dock;
Dock* m_customizableDock;
MainMenuBar* m_menuBar;
LayoutSelector* m_layoutSelector;
StatusBar* m_statusBar;
ColorBar* m_colorBar;
ContextBar* m_contextBar;
ToolBar* m_toolBar;
WorkspaceTabs* m_tabsBar;
std::unique_ptr<Widget> m_customizableDockPlaceholder;
std::unique_ptr<MainMenuBar> m_menuBar;
std::unique_ptr<LayoutSelector> m_layoutSelector;
std::unique_ptr<StatusBar> m_statusBar;
std::unique_ptr<ColorBar> m_colorBar;
std::unique_ptr<ContextBar> m_contextBar;
std::unique_ptr<ToolBar> m_toolBar;
std::unique_ptr<WorkspaceTabs> m_tabsBar;
Mode m_mode;
Timeline* m_timeline;
Workspace* m_workspace;
PreviewEditorWindow* m_previewEditor;
HomeView* m_homeView;
Notifications* m_notifications;
INotificationDelegate* m_scalePanic;
BrowserView* m_browserView;
std::unique_ptr<Timeline> m_timeline;
std::unique_ptr<Workspace> m_workspace;
std::unique_ptr<PreviewEditorWindow> m_previewEditor;
std::unique_ptr<HomeView> m_homeView;
std::unique_ptr<Notifications> m_notifications;
std::unique_ptr<INotificationDelegate> m_scalePanic;
std::unique_ptr<BrowserView> m_browserView;
#ifdef ENABLE_SCRIPTING
DevConsoleView* m_devConsoleView;
std::unique_ptr<DevConsoleView> m_devConsoleView;
#endif
};