From d1a7bd29aabc1ddd04372f4c569892682612764f Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Wed, 8 Feb 2023 18:18:06 -0800 Subject: [PATCH] mainwindow: improved panel organization --- app/core.cpp | 41 +-- app/core.h | 16 +- app/panel/CMakeLists.txt | 2 + app/panel/audiomonitor/audiomonitor.h | 2 +- app/panel/node/node.h | 2 +- app/{widget => }/panel/panel.cpp | 12 +- app/{widget => }/panel/panel.h | 5 + app/panel/panelmanager.cpp | 11 + app/panel/panelmanager.h | 4 +- app/panel/pixelsampler/pixelsamplerpanel.h | 2 +- app/panel/project/project.h | 2 +- app/panel/scope/scope.h | 2 +- app/panel/taskmanager/taskmanager.h | 2 +- app/panel/timebased/timebased.h | 2 +- app/panel/timeline/timeline.cpp | 12 +- app/panel/timeline/timeline.h | 5 +- app/panel/tool/tool.h | 2 +- app/widget/CMakeLists.txt | 1 - app/widget/panel/CMakeLists.txt | 22 -- .../projectexplorer/projectexplorer.cpp | 4 +- app/window/mainwindow/mainwindow.cpp | 264 +++++++++--------- app/window/mainwindow/mainwindow.h | 25 +- .../mainwindow/mainwindowlayoutinfo.cpp | 127 +++++++-- app/window/mainwindow/mainwindowlayoutinfo.h | 34 ++- 24 files changed, 336 insertions(+), 265 deletions(-) rename app/{widget => }/panel/panel.cpp (92%) rename app/{widget => }/panel/panel.h (97%) delete mode 100644 app/widget/panel/CMakeLists.txt diff --git a/app/core.cpp b/app/core.cpp index 416c25756..589458556 100644 --- a/app/core.cpp +++ b/app/core.cpp @@ -144,15 +144,15 @@ void Core::Start() // Initialize task manager TaskManager::CreateInstance(); + // Initialize ConformManager + ConformManager::CreateInstance(); + // Initialize RenderManager RenderManager::CreateInstance(); // Initialize FrameManager FrameManager::CreateInstance(); - // Initialize ConformManager - ConformManager::CreateInstance(); - // Initialize project serializers ProjectSerializer::Initialize(); @@ -468,8 +468,6 @@ void Core::AddOpenProject(Project* p, bool add_to_recents) { // Ensure project is not open at the moment if (open_project_ == p) { - // Signal UI to switch to this project - emit ProjectOpened(p); return; } @@ -478,15 +476,11 @@ void Core::AddOpenProject(Project* p, bool add_to_recents) CloseProject(false); } - connect(p, &Project::ModifiedChanged, this, &Core::ProjectWasModified); - open_project_ = p; - RenderManager::instance()->SetProject(p); + SetActiveProject(p); if (!p->filename().isEmpty() && add_to_recents) { PushRecentlyOpenedProject(p->filename()); } - - emit ProjectOpened(p); } bool Core::AddOpenProjectFromTask(Task *task, bool add_to_recents) @@ -510,6 +504,21 @@ bool Core::AddOpenProjectFromTask(Task *task, bool add_to_recents) return false; } +void Core::SetActiveProject(Project *p) +{ + if (open_project_) { + disconnect(open_project_, &Project::ModifiedChanged, this, &Core::ProjectWasModified); + } + + open_project_ = p; + RenderManager::instance()->SetProject(p); + main_window_->SetProject(p); + + if (open_project_) { + connect(open_project_, &Project::ModifiedChanged, this, &Core::ProjectWasModified); + } +} + void Core::ImportTaskComplete(Task* task) { ProjectImportTask* import_task = static_cast(task); @@ -758,10 +767,6 @@ void Core::StartGUI(bool full_screen) QWindowsWindowFunctions::setHasBorderInFullScreen(main_window_->windowHandle(), true); #endif - // When a new project is opened, update the mainwindow - connect(this, &Core::ProjectOpened, main_window_, &MainWindow::ProjectOpen, Qt::QueuedConnection); - connect(this, &Core::ProjectClosed, main_window_, &MainWindow::ProjectClose); - // Start autorecovery timer using the config value as its interval SetAutorecoveryInterval(OLIVE_CONFIG("AutorecoveryInterval").toInt()); connect(&autorecovery_timer_, &QTimer::timeout, this, &Core::SaveAutorecovery); @@ -1476,11 +1481,9 @@ bool Core::CloseProject(bool auto_open_new, bool ignore_modified) // For safety, the undo stack is cleared so no commands try to affect a freed project undo_stack_.clear(); - disconnect(open_project_, &Project::ModifiedChanged, this, &Core::ProjectWasModified); - RenderManager::instance()->SetProject(nullptr); - emit ProjectClosed(open_project_); - delete open_project_; - open_project_ = nullptr; + Project *tmp = open_project_; + SetActiveProject(nullptr); + delete tmp; } // Ensure a project is always active diff --git a/app/core.h b/app/core.h index d717457a8..b32993c09 100644 --- a/app/core.h +++ b/app/core.h @@ -407,20 +407,6 @@ public slots: } signals: - /** - * @brief Signal emitted when a project is opened - * - * Connects to main window so its UI can update based on the project - * - * @param p - */ - void ProjectOpened(Project* p); - - /** - * @brief Signal emitted when a project is closed - */ - void ProjectClosed(Project* p); - /** * @brief Signal emitted when the tool is changed from somewhere */ @@ -520,6 +506,8 @@ private: bool AddOpenProjectFromTask(Task* task, bool add_to_recents); + void SetActiveProject(Project *p); + /** * @brief Internal main window object */ diff --git a/app/panel/CMakeLists.txt b/app/panel/CMakeLists.txt index 6bfa5a43b..eb26a7e58 100644 --- a/app/panel/CMakeLists.txt +++ b/app/panel/CMakeLists.txt @@ -33,6 +33,8 @@ add_subdirectory(viewer) set(OLIVE_SOURCES ${OLIVE_SOURCES} + panel/panel.h + panel/panel.cpp panel/panelmanager.h panel/panelmanager.cpp PARENT_SCOPE diff --git a/app/panel/audiomonitor/audiomonitor.h b/app/panel/audiomonitor/audiomonitor.h index 2c76d60da..4cff8c94a 100644 --- a/app/panel/audiomonitor/audiomonitor.h +++ b/app/panel/audiomonitor/audiomonitor.h @@ -21,8 +21,8 @@ #ifndef AUDIOMONITORPANEL_H #define AUDIOMONITORPANEL_H +#include "panel/panel.h" #include "widget/audiomonitor/audiomonitor.h" -#include "widget/panel/panel.h" namespace olive { diff --git a/app/panel/node/node.h b/app/panel/node/node.h index 3fc07b49d..b14e1b91c 100644 --- a/app/panel/node/node.h +++ b/app/panel/node/node.h @@ -21,8 +21,8 @@ #ifndef NODEPANEL_H #define NODEPANEL_H +#include "panel/panel.h" #include "widget/nodeview/nodewidget.h" -#include "widget/panel/panel.h" namespace olive { diff --git a/app/widget/panel/panel.cpp b/app/panel/panel.cpp similarity index 92% rename from app/widget/panel/panel.cpp rename to app/panel/panel.cpp index bcc3365e2..5ae7fdd6e 100644 --- a/app/widget/panel/panel.cpp +++ b/app/panel/panel.cpp @@ -32,8 +32,10 @@ namespace olive { +#define super QDockWidget + PanelWidget::PanelWidget(const QString &object_name, QWidget *parent) : - QDockWidget(parent), + super(parent), border_visible_(false), signal_instead_of_close_(false) { @@ -55,15 +57,9 @@ void PanelWidget::SetMovementLocked(bool locked) if (locked) { // Disable moving on QDockWidget setFeatures(features() & ~QDockWidget::DockWidgetMovable); - - // Hide the title bar (only real way to do this is to replace it with an empty QWidget) - setTitleBarWidget(new QWidget(this)); } else { // Re-enable moving on QDockWidget setFeatures(features() | QDockWidget::DockWidgetMovable); - - // Set the "custom" titlebar to null so the default gets restored - setTitleBarWidget(nullptr); } } @@ -89,7 +85,7 @@ void PanelWidget::SetSubtitle(const QString &t) void PanelWidget::paintEvent(QPaintEvent *event) { // Perform default behavior - QDockWidget::paintEvent(event); + super::paintEvent(event); // Check if this panel (or a child of it) has focus using PanelFocusManager if (border_visible_) { diff --git a/app/widget/panel/panel.h b/app/panel/panel.h similarity index 97% rename from app/widget/panel/panel.h rename to app/panel/panel.h index 54df750c6..a3ab02627 100644 --- a/app/widget/panel/panel.h +++ b/app/panel/panel.h @@ -66,6 +66,11 @@ public: */ void SetSignalInsteadOfClose(bool e); + using Info = std::map; + + virtual void LoadData(const Info &info){} + virtual Info SaveData() const {return Info();} + /** * @brief Called whenever this panel is focused and user uses "Zoom In" (either in menus or as a keyboard shortcut) * diff --git a/app/panel/panelmanager.cpp b/app/panel/panelmanager.cpp index 8bf6dfe44..0a4a3aecb 100644 --- a/app/panel/panelmanager.cpp +++ b/app/panel/panelmanager.cpp @@ -78,6 +78,17 @@ PanelWidget *PanelManager::CurrentlyHovered() const return nullptr; } +PanelWidget *PanelManager::GetPanelWithName(const QString &name) const +{ + foreach (PanelWidget* panel, focus_history_) { + if (panel->objectName() == name) { + return panel; + } + } + + return nullptr; +} + bool PanelManager::ArePanelsLocked() { return locked_; diff --git a/app/panel/panelmanager.h b/app/panel/panelmanager.h index 45639b7e8..4955cbb2c 100644 --- a/app/panel/panelmanager.h +++ b/app/panel/panelmanager.h @@ -24,7 +24,7 @@ #include #include -#include "widget/panel/panel.h" +#include "panel/panel.h" namespace olive { @@ -74,6 +74,8 @@ public: */ PanelWidget* CurrentlyHovered() const; + PanelWidget *GetPanelWithName(const QString &name) const; + template /** * @brief Get most recently focused panel of a certain type diff --git a/app/panel/pixelsampler/pixelsamplerpanel.h b/app/panel/pixelsampler/pixelsamplerpanel.h index 38535d38b..34d032550 100644 --- a/app/panel/pixelsampler/pixelsamplerpanel.h +++ b/app/panel/pixelsampler/pixelsamplerpanel.h @@ -21,7 +21,7 @@ #ifndef PIXELSAMPLERPANEL_H #define PIXELSAMPLERPANEL_H -#include "widget/panel/panel.h" +#include "panel/panel.h" #include "widget/pixelsampler/pixelsampler.h" namespace olive { diff --git a/app/panel/project/project.h b/app/panel/project/project.h index d4918b7b3..f7cc57044 100644 --- a/app/panel/project/project.h +++ b/app/panel/project/project.h @@ -23,7 +23,7 @@ #include "footagemanagementpanel.h" #include "node/project/project.h" -#include "widget/panel/panel.h" +#include "panel/panel.h" #include "widget/projectexplorer/projectexplorer.h" namespace olive { diff --git a/app/panel/scope/scope.h b/app/panel/scope/scope.h index f46ef9fba..efca740c4 100644 --- a/app/panel/scope/scope.h +++ b/app/panel/scope/scope.h @@ -24,8 +24,8 @@ #include #include +#include "panel/panel.h" #include "panel/viewer/viewerbase.h" -#include "widget/panel/panel.h" #include "widget/scope/histogram/histogram.h" #include "widget/scope/waveform/waveform.h" diff --git a/app/panel/taskmanager/taskmanager.h b/app/panel/taskmanager/taskmanager.h index 1c84057d7..465cb51dd 100644 --- a/app/panel/taskmanager/taskmanager.h +++ b/app/panel/taskmanager/taskmanager.h @@ -21,8 +21,8 @@ #ifndef TASKMANAGER_PANEL_H #define TASKMANAGER_PANEL_H +#include "panel/panel.h" #include "widget/taskview/taskview.h" -#include "widget/panel/panel.h" namespace olive { diff --git a/app/panel/timebased/timebased.h b/app/panel/timebased/timebased.h index 566412b66..db04d6c0c 100644 --- a/app/panel/timebased/timebased.h +++ b/app/panel/timebased/timebased.h @@ -21,7 +21,7 @@ #ifndef TIMEBASEDPANEL_H #define TIMEBASEDPANEL_H -#include "widget/panel/panel.h" +#include "panel/panel.h" #include "widget/timebased/timebasedwidget.h" namespace olive { diff --git a/app/panel/timeline/timeline.cpp b/app/panel/timeline/timeline.cpp index 24e14320f..b676bbfcd 100644 --- a/app/panel/timeline/timeline.cpp +++ b/app/panel/timeline/timeline.cpp @@ -44,14 +44,18 @@ void TimelinePanel::SplitAtPlayhead() timeline_widget()->SplitAtPlayhead(); } -QByteArray TimelinePanel::SaveSplitterState() const +void TimelinePanel::LoadData(const Info &info) { - return timeline_widget()->SaveSplitterState(); + timeline_widget()->RestoreSplitterState(QByteArray::fromBase64(info.at("splitter").toUtf8())); } -void TimelinePanel::RestoreSplitterState(const QByteArray &state) +PanelWidget::Info TimelinePanel::SaveData() const { - timeline_widget()->RestoreSplitterState(state); + Info i; + + i["splitter"] = timeline_widget()->SaveSplitterState().toBase64(); + + return i; } void TimelinePanel::SelectAll() diff --git a/app/panel/timeline/timeline.h b/app/panel/timeline/timeline.h index 64868354e..fa61a75d5 100644 --- a/app/panel/timeline/timeline.h +++ b/app/panel/timeline/timeline.h @@ -42,9 +42,8 @@ public: void SplitAtPlayhead(); - QByteArray SaveSplitterState() const; - - void RestoreSplitterState(const QByteArray& state); + virtual void LoadData(const Info &info) override; + virtual Info SaveData() const override; virtual void SelectAll() override; diff --git a/app/panel/tool/tool.h b/app/panel/tool/tool.h index 3e2c4534d..358cf30c5 100644 --- a/app/panel/tool/tool.h +++ b/app/panel/tool/tool.h @@ -21,7 +21,7 @@ #ifndef TOOL_PANEL_H #define TOOL_PANEL_H -#include "widget/panel/panel.h" +#include "panel/panel.h" namespace olive { diff --git a/app/widget/CMakeLists.txt b/app/widget/CMakeLists.txt index ef967b7bd..1c075c9b6 100644 --- a/app/widget/CMakeLists.txt +++ b/app/widget/CMakeLists.txt @@ -37,7 +37,6 @@ add_subdirectory(nodetableview) add_subdirectory(nodetreeview) add_subdirectory(nodevaluetree) add_subdirectory(nodeview) -add_subdirectory(panel) add_subdirectory(path) add_subdirectory(pixelsampler) add_subdirectory(playbackcontrols) diff --git a/app/widget/panel/CMakeLists.txt b/app/widget/panel/CMakeLists.txt deleted file mode 100644 index 4b358fb09..000000000 --- a/app/widget/panel/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -# Olive - Non-Linear Video Editor -# Copyright (C) 2022 Olive Team -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -set(OLIVE_SOURCES - ${OLIVE_SOURCES} - widget/panel/panel.h - widget/panel/panel.cpp - PARENT_SCOPE -) diff --git a/app/widget/projectexplorer/projectexplorer.cpp b/app/widget/projectexplorer/projectexplorer.cpp index a9858f19a..b1fdd6c5f 100644 --- a/app/widget/projectexplorer/projectexplorer.cpp +++ b/app/widget/projectexplorer/projectexplorer.cpp @@ -495,12 +495,12 @@ void ProjectExplorer::ReplaceSelectedFootage() void ProjectExplorer::OpenContextMenuItemInNewTab() { - Core::instance()->main_window()->FolderOpen(project(), static_cast(context_menu_items_.first()), false); + Core::instance()->main_window()->OpenFolder(project(), static_cast(context_menu_items_.first()), false); } void ProjectExplorer::OpenContextMenuItemInNewWindow() { - Core::instance()->main_window()->FolderOpen(project(), static_cast(context_menu_items_.first()), true); + Core::instance()->main_window()->OpenFolder(project(), static_cast(context_menu_items_.first()), true); } void ProjectExplorer::ContextMenuStartProxy(QAction *a) diff --git a/app/window/mainwindow/mainwindow.cpp b/app/window/mainwindow/mainwindow.cpp index 8eb20abf9..ff4ad3e5c 100644 --- a/app/window/mainwindow/mainwindow.cpp +++ b/app/window/mainwindow/mainwindow.cpp @@ -37,7 +37,8 @@ namespace olive { MainWindow::MainWindow(QWidget *parent) : - QMainWindow(parent) + QMainWindow(parent), + project_(nullptr) { // Resizes main window to desktop geometry on startup. Fixes the following issues: // * Qt on Windows has a bug that "de-maximizes" the window when widgets are added, resizing the @@ -88,7 +89,7 @@ MainWindow::MainWindow(QWidget *parent) : sequence_viewer_panel_ = new SequenceViewerPanel(this); multicam_panel_ = new MulticamPanel(this); pixel_sampler_panel_ = new PixelSamplerPanel(this); - AppendProjectPanel(); + project_panel_ = new ProjectPanel(this); tool_panel_ = new ToolPanel(this); task_man_panel_ = new TaskManagerPanel(this); AppendTimelinePanel(); @@ -103,6 +104,7 @@ MainWindow::MainWindow(QWidget *parent) : connect(param_panel_, &ParamPanel::RequestViewerToStartEditingText, sequence_viewer_panel_, &ViewerPanel::RequestStartEditingText); connect(param_panel_, &ParamPanel::FocusedNodeChanged, curve_panel_, &CurvePanel::SetNode); connect(param_panel_, &ParamPanel::SelectedNodesChanged, node_panel_, &NodePanel::Select); + connect(project_panel_, &ProjectPanel::ProjectNameChanged, this, &MainWindow::UpdateTitle); connect(node_panel_, &NodePanel::NodeSelectionChanged, sequence_viewer_panel_, &ViewerPanel::SetNodeViewSelections); @@ -135,12 +137,22 @@ MainWindow::~MainWindow() void MainWindow::LoadLayout(const MainWindowLayoutInfo &info) { foreach (Folder* folder, info.open_folders()) { - FolderOpen(folder->project(), folder, true); + OpenFolder(folder->project(), folder, true); } - foreach (const MainWindowLayoutInfo::OpenSequence& sequence, info.open_sequences()) { - TimelinePanel* panel = OpenSequence(sequence.sequence, info.open_sequences().size() == 1); - panel->RestoreSplitterState(sequence.panel_state); + foreach (Sequence *sequence, info.open_sequences()) { + OpenSequence(sequence, info.open_sequences().size() == 1); + } + + foreach (ViewerOutput *viewer, info.open_viewers()) { + OpenNodeInViewer(viewer); + } + + for (auto it=info.panel_data().cbegin(); it!=info.panel_data().cend(); it++) { + // Find panel with this ID + if (PanelWidget *panel = PanelManager::instance()->GetPanelWithName(it->first)) { + panel->LoadData(it->second); + } } restoreState(info.state()); @@ -158,12 +170,19 @@ MainWindowLayoutInfo MainWindow::SaveLayout() const foreach (TimelinePanel* panel, timeline_panels_) { if (panel->GetConnectedViewer()) { - info.add_sequence({static_cast(panel->GetConnectedViewer()), - panel->SaveSplitterState()}); + info.add_sequence(panel->GetSequence()); } } - info.set_state(saveState()); + for (auto it=viewer_panels_.cbegin(); it!=viewer_panels_.cend(); it++) { + info.add_viewer((*it)->GetConnectedViewer()); + } + + foreach (PanelWidget *panel, PanelManager::instance()->panels()) { + info.set_panel_data(panel->objectName(), panel->SaveData()); + } + + info.set_state(this->saveState()); return info; } @@ -222,39 +241,38 @@ bool MainWindow::IsSequenceOpen(Sequence *sequence) const return false; } -void MainWindow::FolderOpen(Project* p, Folder *i, bool floating) +void MainWindow::OpenFolder(Project* p, Folder *i, bool floating) { - ProjectPanel* panel = new ProjectPanel(this); + ProjectPanel* panel = AppendPanelInternal(folder_panels_); panel->set_project(p); panel->set_root(i); - // Tabify with source project panel - foreach (ProjectPanel* proj_panel, project_panels_) { - if (proj_panel->project() == p) { - tabifyDockWidget(proj_panel, panel); - break; - } + if (floating) { + panel->setFloating(floating); + } else { + tabifyDockWidget(project_panel_, panel); } - panel->setFloating(floating); - - panel->show(); - panel->raise(); - // If the panel is closed, just destroy it - panel->SetSignalInsteadOfClose(true); - panel->setProperty("parent_list", reinterpret_cast(&folder_panels_)); - connect(panel, &ProjectPanel::CloseRequested, this, &MainWindow::FloatingPanelCloseRequested); - - folder_panels_.append(panel); + connect(panel, &ProjectPanel::CloseRequested, this, &MainWindow::FolderPanelCloseRequested); } void MainWindow::OpenNodeInViewer(ViewerOutput *node) { - if (viewer_panels_.contains(node)) { + ViewerPanel *existing = nullptr; + + for (auto it = viewer_panels_.cbegin(); it != viewer_panels_.cend(); it++) { + ViewerPanel *it2 = (*it); + if (it2->GetConnectedViewer() == node) { + existing = it2; + break; + } + } + + if (existing) { // This node already has a viewer, raise it - viewer_panels_.value(node)->raise(); + existing->raise(); } else { // Create a viewer for this node ViewerPanel* viewer = new ViewerPanel(this); @@ -267,7 +285,7 @@ void MainWindow::OpenNodeInViewer(ViewerOutput *node) connect(viewer, &ViewerPanel::CloseRequested, this, &MainWindow::ViewerCloseRequested); connect(node, &ViewerOutput::RemovedFromGraph, this, &MainWindow::ViewerWithPanelRemovedFromGraph); - viewer_panels_.insert(node, viewer); + viewer_panels_.append(viewer); } } @@ -328,62 +346,47 @@ void MainWindow::ToggleMaximizedPanel() } } -void MainWindow::ProjectOpen(Project *p) +void MainWindow::SetProject(Project *p) { - // See if this project is already open, and switch to it if so - foreach (ProjectPanel* pl, project_panels_) { - if (pl->project() == p) { - pl->raise(); - return; - } + if (project_ == p) { + return; } - ProjectPanel* panel; + if (project_) { + // Clear all data + param_panel_->SetContexts(QVector()); + node_panel_->SetContexts(QVector()); - if (!project_panels_.first()->project()) { - panel = project_panels_.first(); - } else { - panel = AppendProjectPanel(); - } + // Close any nodes open in TimeBasedWidgets + foreach (PanelWidget* panel, PanelManager::instance()->panels()) { + TimeBasedPanel* tbp = dynamic_cast(panel); - panel->set_project(p); - panel->setFocus(); -} - -void MainWindow::ProjectClose(Project *p) -{ - // Close project from NodeParamView - param_panel_->CloseContextsBelongingToProject(p); - - // Close project from NodeView - node_panel_->CloseContextsBelongingToProject(p); - - // Close any nodes open in TimeBasedWidgets - foreach (PanelWidget* panel, PanelManager::instance()->panels()) { - TimeBasedPanel* tbp = dynamic_cast(panel); - - if (tbp && tbp->GetConnectedViewer() && tbp->GetConnectedViewer()->project() == p) { - if (dynamic_cast(tbp)) { - // Prefer our CloseSequence function which will delete any unnecessary timeline panels - CloseSequence(static_cast(tbp->GetConnectedViewer())); - } else { - tbp->DisconnectViewerNode(); + if (tbp && tbp->GetConnectedViewer() && tbp->GetConnectedViewer()->project() == project_) { + if (dynamic_cast(tbp)) { + // Prefer our CloseSequence function which will delete any unnecessary timeline panels + CloseSequence(static_cast(tbp->GetConnectedViewer())); + } else { + tbp->DisconnectViewerNode(); + } } } - } - // Close any extra folder panels - foreach (ProjectPanel* panel, folder_panels_) { - if (panel->project() == p) { + // Close any extra folder panels + foreach (ProjectPanel* panel, folder_panels_) { panel->close(); } + + // Close any extra viewer panels + foreach (ViewerPanel *viewer, viewer_panels_) { + viewer->close(); + } } - // Close project from project panel - foreach (ProjectPanel* panel, project_panels_) { - if (panel->project() == p) { - RemoveProjectPanel(panel); - } + project_ = p; + project_panel_->set_project(p); + + if (project_) { + project_panel_->setFocus(); } } @@ -420,9 +423,7 @@ void MainWindow::SetApplicationProgressValue(int value) void MainWindow::SelectFootage(const QVector &e) { - for (ProjectPanel *p : project_panels_) { - SelectFootageForProjectPanel(e, p); - } + SelectFootageForProjectPanel(e, project_panel_); for (ProjectPanel *p : folder_panels_) { SelectFootageForProjectPanel(e, p); } @@ -502,8 +503,11 @@ void MainWindow::ShowWelcomeDialog() void MainWindow::RevealViewerInProject(ViewerOutput *r) { - foreach (ProjectPanel *p, project_panels_) { - if (p->project() == r->project() && p->SelectItem(r)) { + // Rather than just using the resident ProjectPanel, find the most recently focused one since + // that's probably the one people will want + auto panels = PanelManager::instance()->GetPanelsOfType(); + foreach (ProjectPanel *p, panels) { + if (p->SelectItem(r)) { break; } } @@ -553,11 +557,6 @@ void MainWindow::TimelineCloseRequested() RemoveTimelinePanel(t); } -void MainWindow::ProjectCloseRequested() -{ - Core::instance()->CloseProject(true); -} - void MainWindow::ViewerCloseRequested() { ViewerPanel* panel = static_cast(sender()); @@ -566,7 +565,7 @@ void MainWindow::ViewerCloseRequested() scope_panel_->SetViewerPanel(sequence_viewer_panel_); } - viewer_panels_.remove(viewer_panels_.key(panel)); + RemovePanelInternal(viewer_panels_, panel); panel->deleteLater(); } @@ -574,24 +573,37 @@ void MainWindow::ViewerCloseRequested() void MainWindow::ViewerWithPanelRemovedFromGraph() { ViewerOutput* vo = static_cast(sender()); - viewer_panels_.take(vo)->deleteLater(); - disconnect(vo, &ViewerOutput::RemovedFromGraph, this, &MainWindow::ViewerWithPanelRemovedFromGraph); + ViewerPanel *panel = nullptr; + + foreach (ViewerPanel *p, viewer_panels_) { + if (p->GetConnectedViewer() == vo) { + panel = p; + break; + } + } + + if (panel) { + RemovePanelInternal(viewer_panels_, panel); + panel->deleteLater(); + disconnect(vo, &ViewerOutput::RemovedFromGraph, this, &MainWindow::ViewerWithPanelRemovedFromGraph); + } } -void MainWindow::FloatingPanelCloseRequested() +void MainWindow::FolderPanelCloseRequested() { - PanelWidget* panel = static_cast(sender()); - - quintptr list_ptr = panel->property("parent_list").value(); - QList* list = reinterpret_cast< QList* >(list_ptr); - - list->removeOne(panel); + ProjectPanel* panel = static_cast(sender()); + RemovePanelInternal(folder_panels_, panel); + removeDockWidget(panel); panel->deleteLater(); } TimelinePanel* MainWindow::AppendTimelinePanel() { - TimelinePanel* panel = AppendPanelInternal(timeline_panels_); + TimelinePanel* panel = AppendPanelInternal(timeline_panels_); + + if (!timeline_panels_.isEmpty()) { + tabifyDockWidget(timeline_panels_.last(), panel); + } connect(panel, &PanelWidget::CloseRequested, this, &MainWindow::TimelineCloseRequested); connect(panel, &TimelinePanel::RequestCaptureStart, sequence_viewer_panel_, &SequenceViewerPanel::StartCapture); @@ -604,16 +616,6 @@ TimelinePanel* MainWindow::AppendTimelinePanel() return panel; } -ProjectPanel *MainWindow::AppendProjectPanel() -{ - ProjectPanel* panel = AppendPanelInternal(project_panels_); - - connect(panel, &PanelWidget::CloseRequested, this, &MainWindow::ProjectCloseRequested); - connect(panel, &ProjectPanel::ProjectNameChanged, this, &MainWindow::UpdateTitle); - - return panel; -} - void MainWindow::RemoveTimelinePanel(TimelinePanel *panel) { // Stop showing this timeline in the viewer @@ -621,17 +623,7 @@ void MainWindow::RemoveTimelinePanel(TimelinePanel *panel) panel->ConnectViewerNode(nullptr); if (timeline_panels_.size() != 1) { - timeline_panels_.removeOne(panel); - panel->deleteLater(); - } -} - -void MainWindow::RemoveProjectPanel(ProjectPanel *panel) -{ - if (project_panels_.size() == 1) { - panel->set_project(nullptr); - } else { - project_panels_.removeOne(panel); + RemovePanelInternal(timeline_panels_, panel); panel->deleteLater(); } } @@ -841,8 +833,8 @@ void MainWindow::SetDefaultLayout() pixel_sampler_panel_->setFloating(true); addDockWidget(Qt::TopDockWidgetArea, pixel_sampler_panel_); - project_panels_.first()->show(); - addDockWidget(Qt::BottomDockWidgetArea, project_panels_.first()); + project_panel_->show(); + addDockWidget(Qt::BottomDockWidgetArea, project_panel_); tool_panel_->show(); addDockWidget(Qt::BottomDockWidgetArea, tool_panel_); @@ -861,11 +853,11 @@ void MainWindow::SetDefaultLayout() {width()/2, width()/2}, Qt::Horizontal); - resizeDocks({project_panels_.first(), tool_panel_, timeline_panels_.first(), audio_monitor_panel_}, + resizeDocks({project_panel_, tool_panel_, timeline_panels_.first(), audio_monitor_panel_}, {width()/4, 1, width(), 1}, Qt::Horizontal); - resizeDocks({node_panel_, project_panels_.first()}, + resizeDocks({node_panel_, project_panel_}, {height()/2, height()/2}, Qt::Vertical); } @@ -897,19 +889,24 @@ void MainWindow::showEvent(QShowEvent *e) } } +void UpdatePanelName(PanelWidget *panel, int index) +{ + QString old_name = panel->objectName(); + QString new_name = QStringLiteral("%1:%2").arg(old_name.split(':').at(0), QString::number(index)); + qDebug() << "renaming" << old_name << "to" << new_name; + panel->setObjectName(new_name); +} + template T *MainWindow::AppendPanelInternal(QList& list) { T* panel = new T(this); - if (!list.isEmpty()) { - tabifyDockWidget(list.last(), panel); - } - // For some reason raise() on its own doesn't do anything, we need both panel->show(); panel->raise(); + UpdatePanelName(panel, list.size()); list.append(panel); // Let us handle the panel closing rather than the panel itself @@ -919,19 +916,16 @@ T *MainWindow::AppendPanelInternal(QList& list) } template -T *MainWindow::AppendFloatingPanelInternal(QList &list) +void MainWindow::RemovePanelInternal(QList &list, T *panel) { - T* panel = new T(this); + int index = list.indexOf(panel); - panel->setFloating(true); - panel->show(); - - panel->SetSignalInsteadOfClose(true); - connect(panel, &PanelWidget::CloseRequested, this, &MainWindow::FloatingPanelCloseRequested); - - panel->setProperty("parent_list", reinterpret_cast(&list)); - - return panel; + if (index != -1) { + list.removeAt(index); + for (int i = index; i < list.size(); i++) { + UpdatePanelName(list.at(i), i); + } + } } } diff --git a/app/window/mainwindow/mainwindow.h b/app/window/mainwindow/mainwindow.h index 892d2ca54..80cb7708a 100644 --- a/app/window/mainwindow/mainwindow.h +++ b/app/window/mainwindow/mainwindow.h @@ -68,7 +68,7 @@ public: bool IsSequenceOpen(Sequence* sequence) const; - void FolderOpen(Project* p, Folder *i, bool floating); + void OpenFolder(Project* p, Folder *i, bool floating); void OpenNodeInViewer(ViewerOutput* node); @@ -96,9 +96,7 @@ public: void SelectFootage(const QVector &e); public slots: - void ProjectOpen(Project *p); - - void ProjectClose(Project* p); + void SetProject(Project *p); void SetFullscreen(bool fullscreen); @@ -118,21 +116,14 @@ protected: private: TimelinePanel* AppendTimelinePanel(); - ProjectPanel* AppendProjectPanel(); - template T* AppendPanelInternal(QList& list); template - T* AppendFloatingPanelInternal(QList& list); - - template - void SetUniquePanelID(T* panel, const QList& list); + void RemovePanelInternal(QList& list, T *panel); void RemoveTimelinePanel(TimelinePanel *panel); - void RemoveProjectPanel(ProjectPanel* panel); - void TimelineFocused(ViewerOutput *viewer); static QString GetCustomShortcutsFile(); @@ -150,12 +141,12 @@ private: QByteArray premaximized_state_; // Standard panels + ProjectPanel *project_panel_; NodePanel* node_panel_; ParamPanel* param_panel_; CurvePanel* curve_panel_; SequenceViewerPanel* sequence_viewer_panel_; FootageViewerPanel* footage_viewer_panel_; - QList project_panels_; QList folder_panels_; ToolPanel* tool_panel_; QList timeline_panels_; @@ -163,7 +154,7 @@ private: TaskManagerPanel* task_man_panel_; PixelSamplerPanel* pixel_sampler_panel_; ScopePanel* scope_panel_; - QMap viewer_panels_; + QList viewer_panels_; MulticamPanel *multicam_panel_; #ifdef Q_OS_WINDOWS @@ -174,6 +165,8 @@ private: bool first_show_; + Project *project_; + private slots: void FocusedPanelChanged(PanelWidget* panel); @@ -181,13 +174,11 @@ private slots: void TimelineCloseRequested(); - void ProjectCloseRequested(); - void ViewerCloseRequested(); void ViewerWithPanelRemovedFromGraph(); - void FloatingPanelCloseRequested(); + void FolderPanelCloseRequested(); void StatusBarDoubleClicked(); diff --git a/app/window/mainwindow/mainwindowlayoutinfo.cpp b/app/window/mainwindow/mainwindowlayoutinfo.cpp index e11df3f05..b8e829562 100644 --- a/app/window/mainwindow/mainwindowlayoutinfo.cpp +++ b/app/window/mainwindow/mainwindowlayoutinfo.cpp @@ -6,27 +6,55 @@ void MainWindowLayoutInfo::toXml(QXmlStreamWriter *writer) const { writer->writeStartElement(QStringLiteral("layout")); + writer->writeAttribute(QStringLiteral("version"), QString::number(kVersion)); + writer->writeStartElement(QStringLiteral("folders")); foreach (Folder* folder, open_folders_) { - writer->writeTextElement(QStringLiteral("folder"), - QString::number(reinterpret_cast(folder))); + writer->writeTextElement(QStringLiteral("folder"), QString::number(reinterpret_cast(folder))); } writer->writeEndElement(); // folders writer->writeStartElement(QStringLiteral("timeline")); - foreach (const OpenSequence& sequence, open_sequences_) { - writer->writeTextElement(QStringLiteral("sequence"), - QString::number(reinterpret_cast(sequence.sequence))); - - writer->writeTextElement(QStringLiteral("state"), - QString(sequence.panel_state.toBase64())); + foreach (Sequence *sequence, open_sequences_) { + writer->writeTextElement(QStringLiteral("sequence"), QString::number(reinterpret_cast(sequence))); } writer->writeEndElement(); // timeline + writer->writeStartElement(QStringLiteral("viewers")); + + foreach (Sequence *sequence, open_sequences_) { + writer->writeTextElement(QStringLiteral("viewer"), QString::number(reinterpret_cast(sequence))); + } + + writer->writeEndElement(); // viewers + + writer->writeStartElement(QStringLiteral("data")); + + for (auto it = panel_data_.cbegin(); it != panel_data_.cend(); it++) { + writer->writeStartElement(QStringLiteral("panel")); + + writer->writeAttribute(QStringLiteral("id"), it->first); + + const PanelWidget::Info &info = it->second; + for (auto jt = info.cbegin(); jt != info.cend(); jt++) { + writer->writeStartElement(QStringLiteral("option")); + + writer->writeAttribute(QStringLiteral("name"), jt->first); + + writer->writeCharacters(jt->second); + + writer->writeEndElement(); // option + } + + writer->writeEndElement(); // panel + } + + writer->writeEndElement(); // data + writer->writeTextElement(QStringLiteral("state"), QString(state_.toBase64())); writer->writeEndElement(); // layout @@ -36,6 +64,19 @@ MainWindowLayoutInfo MainWindowLayoutInfo::fromXml(QXmlStreamReader *reader, con { MainWindowLayoutInfo info; + unsigned int file_version = 0; + + XMLAttributeLoop(reader, attr) { + if (attr.name() == QStringLiteral("version")) { + file_version = attr.value().toUInt(); + } + } + + // Really basic version checking, in the future we may use this to parse multiple versions + if (file_version != kVersion) { + return info; + } + while (XMLReadNextStartElement(reader)) { if (reader->name() == QStringLiteral("folders")) { @@ -44,7 +85,7 @@ MainWindowLayoutInfo MainWindowLayoutInfo::fromXml(QXmlStreamReader *reader, con quintptr item_id = reader->readElementText().toULongLong(); Folder* open_item = static_cast(node_ptrs.value(item_id)); - info.open_folders_.append(open_item); + info.open_folders_.push_back(open_item); } else { reader->skipCurrentElement(); } @@ -52,29 +93,61 @@ MainWindowLayoutInfo MainWindowLayoutInfo::fromXml(QXmlStreamReader *reader, con } else if (reader->name() == QStringLiteral("timeline")) { - Sequence* open_seq = nullptr; - QByteArray tl_state; - while (XMLReadNextStartElement(reader)) { if (reader->name() == QStringLiteral("sequence")) { quintptr item_id = reader->readElementText().toULongLong(); - open_seq = static_cast(node_ptrs.value(item_id)); - } else if (reader->name() == QStringLiteral("state")) { - tl_state = QByteArray::fromBase64(reader->readElementText().toUtf8()); + Sequence *open_seq = static_cast(node_ptrs.value(item_id)); + info.open_sequences_.push_back(open_seq); } else { reader->skipCurrentElement(); } } - if (open_seq) { - info.open_sequences_.append({open_seq, tl_state}); - } - } else if (reader->name() == QStringLiteral("state")) { info.state_ = QByteArray::fromBase64(reader->readElementText().toLatin1()); + } else if (reader->name() == QStringLiteral("data")) { + + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("panel")) { + QString id; + XMLAttributeLoop(reader, attr) { + if (attr.name() == QStringLiteral("id")) { + id = attr.value().toString(); + } + } + + if (!id.isEmpty()) { + PanelWidget::Info i; + + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("option")) { + QString name; + + XMLAttributeLoop(reader, attr) { + if (attr.name() == QStringLiteral("name")) { + name = attr.value().toString(); + } + } + + if (!name.isEmpty()) { + i[name] = reader->readElementText(); + } + } else { + reader->skipCurrentElement(); + } + } + + info.panel_data_[id] = i; + } + + } else { + reader->skipCurrentElement(); + } + } + } else { reader->skipCurrentElement(); } @@ -85,12 +158,22 @@ MainWindowLayoutInfo MainWindowLayoutInfo::fromXml(QXmlStreamReader *reader, con void MainWindowLayoutInfo::add_folder(olive::Folder *f) { - open_folders_.append(f); + open_folders_.push_back(f); } -void MainWindowLayoutInfo::add_sequence(const OpenSequence &seq) +void MainWindowLayoutInfo::add_sequence(Sequence *seq) { - open_sequences_.append(seq); + open_sequences_.push_back(seq); +} + +void MainWindowLayoutInfo::add_viewer(ViewerOutput *viewer) +{ + open_viewers_.push_back(viewer); +} + +void MainWindowLayoutInfo::set_panel_data(const QString &id, const PanelWidget::Info &data) +{ + panel_data_[id] = data; } void MainWindowLayoutInfo::set_state(const QByteArray &layout) diff --git a/app/window/mainwindow/mainwindowlayoutinfo.h b/app/window/mainwindow/mainwindowlayoutinfo.h index f70558284..6b79640b4 100644 --- a/app/window/mainwindow/mainwindowlayoutinfo.h +++ b/app/window/mainwindow/mainwindowlayoutinfo.h @@ -3,6 +3,7 @@ #include "node/project/folder/folder.h" #include "node/project/sequence/sequence.h" +#include "panel/panel.h" namespace olive { @@ -17,25 +18,34 @@ public: void add_folder(Folder* f); - struct OpenSequence { - Sequence* sequence; - QByteArray panel_state; - }; + void add_sequence(Sequence *seq); - void add_sequence(const OpenSequence& seq); + void add_viewer(ViewerOutput *viewer); + + void set_panel_data(const QString &id, const PanelWidget::Info &data); void set_state(const QByteArray& layout); - const QList& open_folders() const + const std::vector& open_folders() const { return open_folders_; } - const QList& open_sequences() const + const std::vector& open_sequences() const { return open_sequences_; } + const std::vector& open_viewers() const + { + return open_viewers_; + } + + const std::map &panel_data() const + { + return panel_data_; + } + const QByteArray& state() const { return state_; @@ -44,9 +54,15 @@ public: private: QByteArray state_; - QList open_folders_; + std::vector open_folders_; - QList open_sequences_; + std::vector open_sequences_; + + std::vector open_viewers_; + + std::map panel_data_; + + static const unsigned int kVersion = 1; };