From 1d56133e752d432798dddea2aa01e039c4001128 Mon Sep 17 00:00:00 2001 From: David Capello Date: Wed, 27 Mar 2013 21:19:35 -0300 Subject: [PATCH] Add support to split workspace views + Added WorkspacePart class + Added WorkspaceView::cloneWorkspaceView()/onClonedFrom() methods. --- src/CMakeLists.txt | 3 +- src/app.cpp | 3 +- src/commands/cmd_close_file.cpp | 4 +- src/ui_context.cpp | 102 ++++++++++++--------------- src/ui_context.h | 5 +- src/widgets/document_view.cpp | 18 +++++ src/widgets/document_view.h | 2 + src/widgets/main_window.cpp | 13 +++- src/widgets/main_window.h | 1 + src/widgets/tabs.cpp | 2 + src/widgets/workspace.cpp | 119 ++++++++++++++++++++++++++------ src/widgets/workspace.h | 21 ++++-- src/widgets/workspace_part.cpp | 84 ++++++++++++++++++++++ src/widgets/workspace_part.h | 49 +++++++++++++ src/widgets/workspace_view.h | 6 ++ src/widgets/workspace_views.h | 31 +++++++++ 16 files changed, 370 insertions(+), 93 deletions(-) create mode 100644 src/widgets/workspace_part.cpp create mode 100644 src/widgets/workspace_part.h create mode 100644 src/widgets/workspace_views.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8fcc06214..a3b6e885b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -407,7 +407,8 @@ add_library(aseprite-library widgets/status_bar.cpp widgets/tabs.cpp widgets/toolbar.cpp - widgets/workspace.cpp) + widgets/workspace.cpp + widgets/workspace_part.cpp) ###################################################################### # ASEPRITE application diff --git a/src/app.cpp b/src/app.cpp index 12abcde00..743e37dba 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -159,8 +159,9 @@ int App::run() // Create the main window and show it. m_mainWindow.reset(new MainWindow); - // Create the list of tabs + // Default status of the main window. app_rebuild_documents_tabs(); + app_default_statusbar_message(); m_mainWindow->openWindow(); diff --git a/src/commands/cmd_close_file.cpp b/src/commands/cmd_close_file.cpp index 8c8f200ad..ceae22f2a 100644 --- a/src/commands/cmd_close_file.cpp +++ b/src/commands/cmd_close_file.cpp @@ -82,7 +82,9 @@ protected: } // Close the active view. - workspace->closeView(workspace->getActiveView()); + WorkspaceView* view = workspace->getActiveView(); + workspace->removeView(view); + delete view; } private: diff --git a/src/ui_context.cpp b/src/ui_context.cpp index 99f9d4565..63bc8e24e 100644 --- a/src/ui_context.cpp +++ b/src/ui_context.cpp @@ -47,31 +47,34 @@ UIContext::UIContext() { ASSERT(m_instance == NULL); m_instance = this; - m_activeView = NULL; } UIContext::~UIContext() { - // No views at this point. - ASSERT(m_allViews.empty()); - ASSERT(m_activeView == NULL); - ASSERT(m_instance == this); m_instance = NULL; } -widgets::DocumentView* UIContext::getActiveView() +widgets::DocumentView* UIContext::getActiveView() const { - return m_activeView; + Workspace* workspace = App::instance()->getMainWindow()->getWorkspace(); + WorkspaceView* view = workspace->getActiveView(); + if (DocumentView* docView = dynamic_cast(view)) + return docView; + else + return NULL; } void UIContext::setActiveView(widgets::DocumentView* docView) { - m_activeView = docView; - current_editor = (docView ? docView->getEditor(): NULL); + if (docView != NULL) { + App::instance()->getMainWindow()->getTabsBar()->selectTab(docView); - App::instance()->getMainWindow()->getTabsBar()->selectTab(docView); - App::instance()->getMainWindow()->getWorkspace()->setActiveView(docView); + if (App::instance()->getMainWindow()->getWorkspace()->getActiveView() != docView) + App::instance()->getMainWindow()->getWorkspace()->setActiveView(docView); + } + + current_editor = (docView ? docView->getEditor(): NULL); if (current_editor) current_editor->requestFocus(); @@ -87,9 +90,9 @@ void UIContext::setActiveView(widgets::DocumentView* docView) // Change the main frame title. base::string defaultTitle = PACKAGE " v" VERSION; base::string title; - if (m_activeView) { + if (docView) { // Prepend the document's filename. - title += base::get_file_name(m_activeView->getDocument()->getFilename()); + title += base::get_file_name(docView->getDocument()->getFilename()); title += " - "; } title += defaultTitle; @@ -98,20 +101,26 @@ void UIContext::setActiveView(widgets::DocumentView* docView) size_t UIContext::countViewsOf(Document* document) const { + Workspace* workspace = App::instance()->getMainWindow()->getWorkspace(); size_t counter = 0; - for (DocumentViews::const_iterator - it=m_allViews.begin(), end=m_allViews.end(); it != end; ++it) { - DocumentView* view = *it; - if (view->getDocument() == document) - ++counter; + + for (Workspace::iterator it=workspace->begin(); it != workspace->end(); ++it) { + WorkspaceView* view = *it; + if (DocumentView* docView = dynamic_cast(view)) { + if (docView->getDocument() == document) { + ++counter; + } + } } + return counter; } Editor* UIContext::getActiveEditor() { - if (m_activeView) - return m_activeView->getEditor(); + DocumentView* activeView = getActiveView(); + if (activeView) + return activeView->getEditor(); else return NULL; } @@ -123,62 +132,41 @@ void UIContext::onAddDocument(Document* document) // Add a new view for this document DocumentView* view = new DocumentView(document, DocumentView::Normal); - m_allViews.push_back(view); // Add a tab with the new view for the document - App::instance()->getMainWindow()->getTabsBar()->addTab(view); App::instance()->getMainWindow()->getWorkspace()->addView(view); setActiveView(view); view->getEditor()->setDefaultScroll(); - - // Rebuild the list of tabs - app_rebuild_documents_tabs(); } void UIContext::onRemoveDocument(Document* document) { Context::onRemoveDocument(document); - DocumentView* newActiveView = NULL; - bool activeViewChanged = false; + Workspace* workspace = App::instance()->getMainWindow()->getWorkspace(); + DocumentViews docViews; - // Remove all views of this document - for (DocumentViews::iterator it=m_allViews.begin(); it != m_allViews.end(); ) { - DocumentView* view = *it; - - if (view->getDocument() == document) { - App::instance()->getMainWindow()->getTabsBar()->removeTab(view); - App::instance()->getMainWindow()->getWorkspace()->removeView(view); - - // We cannot point as "active view" this view that we're - // destroying. In this case we go to the next view (or the - // previous one if we are at the end). - if (view == m_activeView || - view == newActiveView) { - m_activeView = NULL; - newActiveView = ((it+1) != m_allViews.end() ? *(it+1): - (it != m_allViews.begin() ? *(it-1): NULL)); - activeViewChanged = true; + // Collect all views related to the document. + for (Workspace::iterator it=workspace->begin(); it != workspace->end(); ++it) { + WorkspaceView* view = *it; + if (DocumentView* docView = dynamic_cast(view)) { + if (docView->getDocument() == document) { + docViews.push_back(docView); } - - delete view; - it = m_allViews.erase(it); } - else - ++it; } - // Select the next view as current view. - if (activeViewChanged) - setActiveView(newActiveView); - - // Rebuild the tabs - app_rebuild_documents_tabs(); + for (DocumentViews::iterator it=docViews.begin(); it != docViews.end(); ++it) { + DocumentView* docView = *it; + workspace->removeView(docView); + delete docView; + } } void UIContext::onGetActiveLocation(DocumentLocation* location) const { - if (m_activeView) - m_activeView->getDocumentLocation(location); + widgets::DocumentView* activeView = getActiveView(); + if (activeView) + activeView->getDocumentLocation(location); } diff --git a/src/ui_context.h b/src/ui_context.h index 904c176f2..3220271b0 100644 --- a/src/ui_context.h +++ b/src/ui_context.h @@ -37,7 +37,7 @@ public: virtual bool isUiAvailable() const { return true; } - widgets::DocumentView* getActiveView(); + widgets::DocumentView* getActiveView() const; void setActiveView(widgets::DocumentView* documentView); // Returns the number of views that the given document has. @@ -56,9 +56,6 @@ protected: virtual void onGetActiveLocation(DocumentLocation* location) const OVERRIDE; private: - DocumentViews m_allViews; - widgets::DocumentView* m_activeView; - static UIContext* m_instance; }; diff --git a/src/widgets/document_view.cpp b/src/widgets/document_view.cpp index b74b56306..20460d8b0 100644 --- a/src/widgets/document_view.cpp +++ b/src/widgets/document_view.cpp @@ -158,6 +158,24 @@ std::string DocumentView::getTabText() return str; } +WorkspaceView* DocumentView::cloneWorkspaceView() +{ + return new DocumentView(m_document, Normal); +} + +void DocumentView::onClonedFrom(WorkspaceView* from) +{ + Editor* newEditor = getEditor(); + Editor* srcEditor = static_cast(from)->getEditor(); + + newEditor->setLayer(srcEditor->getLayer()); + newEditor->setFrame(srcEditor->getFrame()); + newEditor->setZoom(srcEditor->getZoom()); + + View::getView(newEditor) + ->setViewScroll(View::getView(srcEditor)->getViewScroll()); +} + bool DocumentView::onProcessMessage(Message* msg) { switch (msg->type) { diff --git a/src/widgets/document_view.h b/src/widgets/document_view.h index 100db0a67..80a4eeea6 100644 --- a/src/widgets/document_view.h +++ b/src/widgets/document_view.h @@ -56,6 +56,8 @@ namespace widgets { // WorkspaceView implementation ui::Widget* getContentWidget() OVERRIDE { return this; } + WorkspaceView* cloneWorkspaceView() OVERRIDE; + void onClonedFrom(WorkspaceView* from) OVERRIDE; // DocumentObserver implementation void onGeneralUpdate(DocumentEvent& ev) OVERRIDE; diff --git a/src/widgets/main_window.cpp b/src/widgets/main_window.cpp index 605f3cded..73faffccd 100644 --- a/src/widgets/main_window.cpp +++ b/src/widgets/main_window.cpp @@ -69,6 +69,7 @@ MainWindow::MainWindow() m_toolBar = new ToolBar(); m_tabsBar = new Tabs(this); m_workspace = new Workspace(); + m_workspace->ActiveViewChanged.connect(&MainWindow::onActiveViewChange, this); m_miniEditor = new MiniEditorWindow(); m_colorBarSplitter = findChildT("colorbarsplitter"); @@ -83,9 +84,6 @@ MainWindow::MainWindow() // Setup the menus m_menuBar->setMenu(AppMenus::instance()->getRootMenu()); - // Start text of status bar - app_default_statusbar_message(); - // Add the widgets in the boxes if (box_menubar) box_menubar->addChild(m_menuBar); if (box_colorbar) box_colorbar->addChild(m_colorBar); @@ -155,6 +153,15 @@ void MainWindow::onSaveLayout(SaveLayoutEvent& ev) m_colorBarSplitter->setPosition(m_lastSplitterPos); } +// When the active view is changed from methods like +// Workspace::splitView(), this function is called, and we have to +// inform to the UIContext that the current view has changed. +void MainWindow::onActiveViewChange() +{ + if (DocumentView* docView = dynamic_cast(m_workspace->getActiveView())) + UIContext::instance()->setActiveView(docView); +} + void MainWindow::clickTab(Tabs* tabs, TabView* tabView, int button) { if (!tabView) diff --git a/src/widgets/main_window.h b/src/widgets/main_window.h index d4e5803c8..475990454 100644 --- a/src/widgets/main_window.h +++ b/src/widgets/main_window.h @@ -56,6 +56,7 @@ public: protected: void onSaveLayout(ui::SaveLayoutEvent& ev) OVERRIDE; + void onActiveViewChange(); private: MainMenuBar* m_menuBar; // the menu bar widget diff --git a/src/widgets/tabs.cpp b/src/widgets/tabs.cpp index d3cfd6ada..6accd3e00 100644 --- a/src/widgets/tabs.cpp +++ b/src/widgets/tabs.cpp @@ -185,6 +185,8 @@ void Tabs::updateTabsText() void Tabs::selectTab(TabView* tabView) { + ASSERT(tabView != NULL); + Tab *tab = getTabByView(tabView); if (tab != NULL) selectTabInternal(tab); diff --git a/src/widgets/workspace.cpp b/src/widgets/workspace.cpp index 5e357caf2..8ecf3db21 100644 --- a/src/widgets/workspace.cpp +++ b/src/widgets/workspace.cpp @@ -20,7 +20,12 @@ #include "widgets/workspace.h" +#include "app.h" #include "skin/skin_theme.h" +#include "ui/splitter.h" +#include "widgets/main_window.h" +#include "widgets/tabs.h" +#include "widgets/workspace_part.h" #include "widgets/workspace_view.h" #include @@ -30,23 +35,30 @@ using namespace widgets; Workspace::Workspace() : Box(JI_VERTICAL) - , m_activeView(NULL) + , m_mainPart(new WorkspacePart) + , m_activePart(m_mainPart) { SkinTheme* theme = static_cast(getTheme()); setBgColor(theme->getColor(ThemeColor::Workspace)); + + addChild(m_mainPart); } Workspace::~Workspace() { + // No views at this point. + ASSERT(m_views.empty()); } void Workspace::addView(WorkspaceView* view) { m_views.push_back(view); - addChild(view->getContentWidget()); + m_activePart->addView(view); - setActiveView(view); + App::instance()->getMainWindow()->getTabsBar()->addTab(dynamic_cast(view)); + + ActiveViewChanged(); // Fire ActiveViewChanged event } void Workspace::removeView(WorkspaceView* view) @@ -55,39 +67,104 @@ void Workspace::removeView(WorkspaceView* view) ASSERT(it != m_views.end()); m_views.erase(it); - removeChild(view->getContentWidget()); + m_activePart->removeView(view); + if (m_activePart->getViewCount() == 0 && + m_activePart->getParent() != this) { + m_activePart = destroyPart(m_activePart); + } - setActiveView(NULL); + App::instance()->getMainWindow()->getTabsBar()->removeTab(dynamic_cast(view)); + + ActiveViewChanged(); // Fire ActiveViewChanged event +} + +WorkspaceView* Workspace::getActiveView() +{ + ASSERT(m_activePart != NULL); + return m_activePart->getActiveView(); } void Workspace::setActiveView(WorkspaceView* view) { - m_activeView = view; + ASSERT(view != NULL); - Widget* newContent = (view ? view->getContentWidget(): NULL); - WidgetsList children = getChildren(); - UI_FOREACH_WIDGET(children, it) { - if ((*it) != newContent) - (*it)->setVisible(false); - } + WorkspacePart* viewPart = + static_cast(view->getContentWidget()->getParent()); - if (newContent) { - newContent->setExpansive(true); - newContent->setVisible(true); - newContent->requestFocus(); - } + viewPart->setActiveView(view); - layout(); + m_activePart = viewPart; + ActiveViewChanged(); // Fire ActiveViewChanged event } void Workspace::splitView(WorkspaceView* view, int orientation) { - // TODO + WorkspacePart* viewPart = + static_cast(view->getContentWidget()->getParent()); + + // Create a new splitter to add new WorkspacePart on it: the given + // "viewPart" and a new part named "newPart". + Splitter* splitter = new Splitter(Splitter::ByPercentage, orientation); + splitter->setExpansive(true); + + // Create the new part to contain the cloned view (see below, "newView"). + WorkspacePart* newPart = new WorkspacePart(); + + // Replace the "viewPart" with the "splitter". + Widget* parent = viewPart->getParent(); + parent->replaceChild(viewPart, splitter); + splitter->addChild(viewPart); + splitter->addChild(newPart); + + // The new part is the active one. + m_activePart = newPart; + + // Clone the workspace view, and add it to the active part (newPart) + // using Workspace::addView(). + WorkspaceView* newView = view->cloneWorkspaceView(); + addView(newView); + setActiveView(newView); + + layout(); + + newView->onClonedFrom(view); + + ActiveViewChanged(); // Fire ActiveViewChanged event } -void Workspace::closeView(WorkspaceView* view) +WorkspacePart* Workspace::destroyPart(WorkspacePart* part) { - // TODO + ASSERT(part != NULL); + ASSERT(part->getViewCount() == 0); + + Widget* splitter = part->getParent(); + ASSERT(splitter != this); + ASSERT(splitter->getChildren().size() == 2); + splitter->removeChild(part); + delete part; + ASSERT(splitter->getChildren().size() == 1); + + Widget* otherWidget = splitter->getChildren().front(); + WorkspacePart* otherPart = dynamic_cast(otherWidget); + if (otherPart == NULL) { + Widget* widget = otherWidget; + for (;;) { + otherPart = widget->findFirstChildByType(); + if (otherPart != NULL) + break; + + widget = widget->getChildren().front(); + } + } + ASSERT(otherPart != NULL); + + splitter->removeChild(otherWidget); + splitter->getParent()->replaceChild(splitter, otherWidget); + delete splitter; + + layout(); + + return otherPart; } void Workspace::makeUnique(WorkspaceView* view) diff --git a/src/widgets/workspace.h b/src/widgets/workspace.h index c2ba2f39b..25805f062 100644 --- a/src/widgets/workspace.h +++ b/src/widgets/workspace.h @@ -19,33 +19,44 @@ #ifndef WIDGETS_WORKSPACE_H_INCLUDED #define WIDGETS_WORKSPACE_H_INCLUDED +#include "base/signal.h" #include "ui/box.h" +#include "widgets/workspace_views.h" #include namespace widgets { - class WorkspaceView; - typedef std::vector WorkspaceViews; + class WorkspacePart; class Workspace : public ui::Box { public: + typedef WorkspaceViews::iterator iterator; + Workspace(); ~Workspace(); + iterator begin() { return m_views.begin(); } + iterator end() { return m_views.end(); } + void addView(WorkspaceView* view); void removeView(WorkspaceView* view); - WorkspaceView* getActiveView() { return m_activeView; } + WorkspaceView* getActiveView(); void setActiveView(WorkspaceView* view); void splitView(WorkspaceView* view, int orientation); - void closeView(WorkspaceView* view); void makeUnique(WorkspaceView* view); + Signal0 ActiveViewChanged; + private: - WorkspaceView* m_activeView; + WorkspacePart* destroyPart(WorkspacePart* part); + + // All views of all parts. WorkspaceViews m_views; + WorkspacePart* m_mainPart; + WorkspacePart* m_activePart; }; } // namespace widgets diff --git a/src/widgets/workspace_part.cpp b/src/widgets/workspace_part.cpp new file mode 100644 index 000000000..7268e5a79 --- /dev/null +++ b/src/widgets/workspace_part.cpp @@ -0,0 +1,84 @@ +/* ASEPRITE + * Copyright (C) 2001-2013 David Capello + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#include "widgets/workspace_part.h" + +#include "skin/skin_theme.h" +#include "widgets/workspace_view.h" + +#include + +using namespace ui; +using namespace widgets; + +WorkspacePart::WorkspacePart() + : Box(JI_VERTICAL) + , m_activeView(NULL) +{ + SkinTheme* theme = static_cast(getTheme()); + setBgColor(theme->getColor(ThemeColor::Workspace)); + + setExpansive(true); +} + +WorkspacePart::~WorkspacePart() +{ +} + +void WorkspacePart::addView(WorkspaceView* view) +{ + m_views.push_back(view); + + addChild(view->getContentWidget()); + + setActiveView(view); +} + +void WorkspacePart::removeView(WorkspaceView* view) +{ + WorkspaceViews::iterator it = std::find(m_views.begin(), m_views.end(), view); + ASSERT(it != m_views.end()); + it = m_views.erase(it); + + removeChild(view->getContentWidget()); + + setActiveView((it != m_views.end() ? + *it : (!m_views.empty() ? m_views.front(): NULL))); +} + +void WorkspacePart::setActiveView(WorkspaceView* view) +{ + m_activeView = view; + + Widget* newContent = (view ? view->getContentWidget(): NULL); + WidgetsList children = getChildren(); + UI_FOREACH_WIDGET(children, it) { + if ((*it) != newContent) + (*it)->setVisible(false); + } + + if (newContent) { + newContent->setExpansive(true); + newContent->setVisible(true); + newContent->requestFocus(); + } + + layout(); +} diff --git a/src/widgets/workspace_part.h b/src/widgets/workspace_part.h new file mode 100644 index 000000000..06248fd90 --- /dev/null +++ b/src/widgets/workspace_part.h @@ -0,0 +1,49 @@ +/* ASEPRITE + * Copyright (C) 2001-2013 David Capello + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef WIDGETS_WORKSPACE_PART_H_INCLUDED +#define WIDGETS_WORKSPACE_PART_H_INCLUDED + +#include "ui/box.h" +#include "widgets/workspace_views.h" +#include + +namespace widgets { + + class WorkspacePart : public ui::Box + { + public: + WorkspacePart(); + ~WorkspacePart(); + + size_t getViewCount() { return m_views.size(); } + + void addView(WorkspaceView* view); + void removeView(WorkspaceView* view); + + WorkspaceView* getActiveView() { return m_activeView; } + void setActiveView(WorkspaceView* view); + + private: + WorkspaceView* m_activeView; + WorkspaceViews m_views; + }; + +} // namespace widgets + +#endif diff --git a/src/widgets/workspace_view.h b/src/widgets/workspace_view.h index a02979aac..740d3d672 100644 --- a/src/widgets/workspace_view.h +++ b/src/widgets/workspace_view.h @@ -29,6 +29,12 @@ namespace widgets { virtual ~WorkspaceView() { } virtual ui::Widget* getContentWidget() = 0; + virtual WorkspaceView* cloneWorkspaceView() = 0; + + // Called after the view is added in the correct position inside + // the workspace. It can be used to copy/clone scroll position + // from the original view. + virtual void onClonedFrom(WorkspaceView* from) = 0; }; } // namespace widgets diff --git a/src/widgets/workspace_views.h b/src/widgets/workspace_views.h new file mode 100644 index 000000000..118da5b61 --- /dev/null +++ b/src/widgets/workspace_views.h @@ -0,0 +1,31 @@ +/* ASEPRITE + * Copyright (C) 2001-2013 David Capello + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef WIDGETS_WORKSPACE_VIEWS_H_INCLUDED +#define WIDGETS_WORKSPACE_VIEWS_H_INCLUDED + +#include + +namespace widgets { + + class WorkspaceView; + typedef std::vector WorkspaceViews; + +} // namespace widgets + +#endif