Add support to close Home tab with middle mouse button or right-click popup menu

This commit is contained in:
David Capello 2015-02-22 21:18:53 -03:00
parent 926e714f74
commit ff66ea025c
19 changed files with 195 additions and 176 deletions

View File

@ -615,6 +615,10 @@
</menu>
</menu>
<menu id="tab_popup">
<item command="CloseFile" text="&amp;Close" />
</menu>
<menu id="document_tab_popup">
<item command="CloseFile" text="&amp;Close" />
<separator />

View File

@ -78,6 +78,7 @@ void AppMenus::reload()
PRINTF("Main menu loaded.\n");
m_tabPopupMenu.reset(loadMenuById(handle, "tab_popup"));
m_documentTabPopupMenu.reset(loadMenuById(handle, "document_tab_popup"));
m_layerPopupMenu.reset(loadMenuById(handle, "layer_popup"));
m_framePopupMenu.reset(loadMenuById(handle, "frame_popup"));

View File

@ -39,6 +39,7 @@ namespace app {
Menu* getRootMenu() { return m_rootMenu; }
MenuItem* getRecentListMenuitem() { return m_recentListMenuitem; }
Menu* getTabPopupMenu() { return m_tabPopupMenu; }
Menu* getDocumentTabPopupMenu() { return m_documentTabPopupMenu; }
Menu* getLayerPopupMenu() { return m_layerPopupMenu; }
Menu* getFramePopupMenu() { return m_framePopupMenu; }
@ -56,6 +57,7 @@ namespace app {
base::UniquePtr<Menu> m_rootMenu;
MenuItem* m_recentListMenuitem;
base::UniquePtr<Menu> m_tabPopupMenu;
base::UniquePtr<Menu> m_documentTabPopupMenu;
base::UniquePtr<Menu> m_layerPopupMenu;
base::UniquePtr<Menu> m_framePopupMenu;

View File

@ -29,53 +29,30 @@ namespace app {
using namespace ui;
static bool close_active_document(Context* context);
class CloseFileCommand : public Command {
public:
CloseFileCommand()
: Command("CloseFile",
"Close File",
CmdUIOnlyFlag)
{
CmdUIOnlyFlag) {
}
Command* clone() const override { return new CloseFileCommand(*this); }
protected:
bool onEnabled(Context* context)
{
const ContextReader reader(context);
const Sprite* sprite(reader.sprite());
return sprite != NULL;
}
void onExecute(Context* context)
{
bool onEnabled(Context* context) override {
Workspace* workspace = App::instance()->getMainWindow()->getWorkspace();
if (workspace->activeView() == NULL)
return;
if (DocumentView* docView =
dynamic_cast<DocumentView*>(workspace->activeView())) {
Document* document = docView->getDocument();
if (static_cast<UIContext*>(context)->countViewsOf(document) == 1) {
// If we have only one view for this document, close the file.
close_active_document(context);
return;
}
}
// Close the active view.
WorkspaceView* view = workspace->activeView();
workspace->removeView(view);
delete view;
return (view != nullptr);
}
private:
static char* read_authors_txt(const char *filename);
void onExecute(Context* context) override {
Workspace* workspace = App::instance()->getMainWindow()->getWorkspace();
WorkspaceView* view = workspace->activeView();
if (view)
workspace->closeView(view);
}
};
class CloseAllFilesCommand : public Command {
@ -83,96 +60,31 @@ public:
CloseAllFilesCommand()
: Command("CloseAllFiles",
"Close All Files",
CmdRecordableFlag)
{
CmdRecordableFlag) {
}
Command* clone() const override { return new CloseAllFilesCommand(*this); }
protected:
bool onEnabled(Context* context)
{
return !context->documents().empty();
}
void onExecute(Context* context) override {
Workspace* workspace = App::instance()->getMainWindow()->getWorkspace();
void onExecute(Context* context)
{
while (true) {
if (context->activeDocument() != NULL) {
if (!close_active_document(context))
break;
}
else
std::vector<DocumentView*> docViews;
for (auto view : *workspace) {
DocumentView* docView = dynamic_cast<DocumentView*>(view);
if (docView)
docViews.push_back(docView);
}
for (auto docView : docViews) {
if (!workspace->closeView(docView))
break;
}
}
};
// Closes the active document, asking to the user to save it if it is
// modified.
static bool close_active_document(Context* context)
{
Document* closedDocument = NULL;
bool save_it;
bool try_again = true;
while (try_again) {
// This flag indicates if we have to sabe the sprite before to destroy it
save_it = false;
{
// The sprite is locked as reader temporaly
const ContextReader reader(context);
const Document* document = reader.document();
closedDocument = const_cast<Document*>(document);
// see if the sprite has changes
while (document->isModified()) {
// ask what want to do the user with the changes in the sprite
int ret = Alert::show("Warning<<Saving changes in:<<%s||&Save||Do&n't Save||&Cancel",
document->name().c_str());
if (ret == 1) {
// "save": save the changes
save_it = true;
break;
}
else if (ret != 2) {
// "cancel" or "ESC" */
return false; // we back doing nothing
}
else {
// "discard"
break;
}
}
}
// Does we need to save the sprite?
if (save_it) {
Command* save_command =
CommandsModule::instance()->getCommandByName(CommandId::SaveFile);
context->executeCommand(save_command);
try_again = true;
}
else
try_again = false;
}
// Destroy the sprite (locking it as writer)
{
DocumentDestroyer document(context, closedDocument);
StatusBar::instance()
->setStatusText(0, "Sprite '%s' closed.",
document->name().c_str());
document.destroyDocument();
}
return true;
}
Command* CommandFactory::createCloseFileCommand()
{
return new CloseFileCommand;

View File

@ -11,9 +11,11 @@
#include "app/ui/devconsole_view.h"
#include "app/app_menus.h"
#include "app/ui/workspace.h"
#include "ui/entry.h"
#include "ui/message.h"
#include "ui/system.h"
#include "ui/textbox.h"
#include "ui/view.h"
@ -103,9 +105,19 @@ void DevConsoleView::onClonedFrom(WorkspaceView* from)
{
}
void DevConsoleView::onCloseView(Workspace* workspace)
bool DevConsoleView::onCloseView(Workspace* workspace)
{
workspace->removeView(this);
return true;
}
void DevConsoleView::onTabPopup(Workspace* workspace)
{
Menu* menu = AppMenus::instance()->getTabPopupMenu();
if (!menu)
return;
menu->showPopup(ui::get_mouse_position());
}
bool DevConsoleView::onProcessMessage(Message* msg)

View File

@ -33,7 +33,8 @@ namespace app {
WorkspaceView* cloneWorkspaceView() override;
void onWorkspaceViewSelected() override;
void onClonedFrom(WorkspaceView* from) override;
void onCloseView(Workspace* workspace) override;
bool onCloseView(Workspace* workspace) override;
void onTabPopup(Workspace* workspace) override;
protected:
bool onProcessMessage(ui::Message* msg) override;

View File

@ -12,7 +12,9 @@
#include "app/ui/document_view.h"
#include "app/app.h"
#include "app/app_menus.h"
#include "app/commands/commands.h"
#include "app/document_access.h"
#include "app/modules/editors.h"
#include "app/modules/palettes.h"
#include "app/ui/editor/editor.h"
@ -21,6 +23,7 @@
#include "app/ui/keyboard_shortcuts.h"
#include "app/ui/main_window.h"
#include "app/ui/preview_editor.h"
#include "app/ui/status_bar.h"
#include "app/ui/workspace.h"
#include "app/ui_context.h"
#include "base/path.h"
@ -28,6 +31,8 @@
#include "doc/layer.h"
#include "doc/sprite.h"
#include "ui/accelerator.h"
#include "ui/alert.h"
#include "ui/menu.h"
#include "ui/message.h"
#include "ui/system.h"
#include "ui/view.h"
@ -224,16 +229,76 @@ void DocumentView::onClonedFrom(WorkspaceView* from)
->setViewScroll(View::getView(srcEditor)->getViewScroll());
}
void DocumentView::onCloseView(Workspace* workspace)
bool DocumentView::onCloseView(Workspace* workspace)
{
Context* ctx = UIContext::instance();
bool save_it;
bool try_again = true;
while (try_again) {
// This flag indicates if we have to sabe the sprite before to destroy it
save_it = false;
{
// see if the sprite has changes
while (m_document->isModified()) {
// ask what want to do the user with the changes in the sprite
int ret = Alert::show("Warning<<Saving changes in:<<%s||&Save||Do&n't Save||&Cancel",
m_document->name().c_str());
if (ret == 1) {
// "save": save the changes
save_it = true;
break;
}
else if (ret != 2) {
// "cancel" or "ESC" */
return false; // we back doing nothing
}
else {
// "discard"
break;
}
}
}
// Does we need to save the sprite?
if (save_it) {
Command* save_command =
CommandsModule::instance()->getCommandByName(CommandId::SaveFile);
ctx->executeCommand(save_command);
try_again = true;
}
else
try_again = false;
}
// Destroy the sprite (locking it as writer)
DocumentDestroyer destroyer(
static_cast<app::Context*>(m_document->context()), m_document);
StatusBar::instance()
->setStatusText(0, "Sprite '%s' closed.",
m_document->name().c_str());
destroyer.destroyDocument();
// At this point the view is already destroyed
return true;
}
void DocumentView::onTabPopup(Workspace* workspace)
{
Menu* menu = AppMenus::instance()->getDocumentTabPopupMenu();
if (!menu)
return;
UIContext* context = UIContext::instance();
context->setActiveView(this);
context->updateFlags();
Command* close_file_cmd =
CommandsModule::instance()->getCommandByName(CommandId::CloseFile);
context->executeCommand(close_file_cmd, NULL);
menu->showPopup(ui::get_mouse_position());
}
bool DocumentView::onProcessMessage(Message* msg)

View File

@ -49,7 +49,8 @@ namespace app {
WorkspaceView* cloneWorkspaceView() override;
void onWorkspaceViewSelected() override;
void onClonedFrom(WorkspaceView* from) override;
void onCloseView(Workspace* workspace) override;
bool onCloseView(Workspace* workspace) override;
void onTabPopup(Workspace* workspace) override;
// DocumentObserver implementation
void onGeneralUpdate(doc::DocumentEvent& ev) override;

View File

@ -11,9 +11,11 @@
#include "app/ui/home_view.h"
#include "app/app_menus.h"
#include "app/ui/skin/skin_theme.h"
#include "app/ui/workspace.h"
#include "ui/label.h"
#include "ui/system.h"
#include "ui/textbox.h"
#include "ui/view.h"
@ -59,9 +61,19 @@ void HomeView::onClonedFrom(WorkspaceView* from)
ASSERT(false); // Never called
}
void HomeView::onCloseView(Workspace* workspace)
bool HomeView::onCloseView(Workspace* workspace)
{
workspace->removeView(this);
return true;
}
void HomeView::onTabPopup(Workspace* workspace)
{
Menu* menu = AppMenus::instance()->getTabPopupMenu();
if (!menu)
return;
menu->showPopup(ui::get_mouse_position());
}
void HomeView::onWorkspaceViewSelected()

View File

@ -33,7 +33,8 @@ namespace app {
ui::Widget* getContentWidget() override { return this; }
WorkspaceView* cloneWorkspaceView() override;
void onClonedFrom(WorkspaceView* from) override;
void onCloseView(Workspace* workspace) override;
bool onCloseView(Workspace* workspace) override;
void onTabPopup(Workspace* workspace) override;
void onWorkspaceViewSelected() override;
};

View File

@ -288,45 +288,33 @@ void MainWindow::onActiveViewChange()
layout();
}
void MainWindow::clickTab(Tabs* tabs, TabView* tabView, ui::MouseButtons buttons)
void MainWindow::onSelectTab(Tabs* tabs, TabView* tabView)
{
if (!tabView)
return;
WorkspaceView* workspaceView = dynamic_cast<WorkspaceView*>(tabView);
if (m_workspace->activeView() != workspaceView)
m_workspace->setActiveView(workspaceView);
DocumentView* docView = dynamic_cast<DocumentView*>(workspaceView);
if (!docView)
return;
UIContext* context = UIContext::instance();
context->setActiveView(docView);
context->updateFlags();
// Right-button: popup-menu
if (buttons & kButtonRight) {
Menu* popup_menu = AppMenus::instance()->getDocumentTabPopupMenu();
if (popup_menu != NULL) {
popup_menu->showPopup(ui::get_mouse_position());
}
}
// Middle-button: close the sprite
else if (buttons & kButtonMiddle) {
docView->onCloseView(m_workspace);
}
WorkspaceView* view = dynamic_cast<WorkspaceView*>(tabView);
if (m_workspace->activeView() != view)
m_workspace->setActiveView(view);
}
void MainWindow::clickClose(Tabs* tabs, TabView* tabView)
void MainWindow::onCloseTab(Tabs* tabs, TabView* tabView)
{
WorkspaceView* view = dynamic_cast<WorkspaceView*>(tabView);
ASSERT(view);
if (view)
view->onCloseView(m_workspace);
m_workspace->closeView(view);
}
void MainWindow::mouseOverTab(Tabs* tabs, TabView* tabView)
void MainWindow::onContextMenuTab(Tabs* tabs, TabView* tabView)
{
WorkspaceView* view = dynamic_cast<WorkspaceView*>(tabView);
ASSERT(view);
if (view)
view->onTabPopup(m_workspace);
}
void MainWindow::onMouseOverTab(Tabs* tabs, TabView* tabView)
{
// Note: tabView can be NULL
if (DocumentView* docView = dynamic_cast<DocumentView*>(tabView)) {
@ -339,7 +327,7 @@ void MainWindow::mouseOverTab(Tabs* tabs, TabView* tabView)
}
}
bool MainWindow::isModified(Tabs* tabs, TabView* tabView)
bool MainWindow::onIsModified(Tabs* tabs, TabView* tabView)
{
if (DocumentView* docView = dynamic_cast<DocumentView*>(tabView)) {
Document* document = docView->getDocument();

View File

@ -64,10 +64,11 @@ namespace app {
void popTimeline();
// TabsDelegate implementation.
void clickTab(Tabs* tabs, TabView* tabView, ui::MouseButtons buttons) override;
void clickClose(Tabs* tabs, TabView* tabView) override;
void mouseOverTab(Tabs* tabs, TabView* tabView) override;
bool isModified(Tabs* tabs, TabView* tabView) override;
void onSelectTab(Tabs* tabs, TabView* tabView) override;
void onCloseTab(Tabs* tabs, TabView* tabView) override;
void onContextMenuTab(Tabs* tabs, TabView* tabView) override;
void onMouseOverTab(Tabs* tabs, TabView* tabView) override;
bool onIsModified(Tabs* tabs, TabView* tabView) override;
protected:
bool onProcessMessage(ui::Message* msg) override;

View File

@ -105,7 +105,11 @@ void Tabs::removeTab(TabView* tabView)
m_hot = nullptr;
if (m_selected == tab) {
selectNextTab();
if (tab == m_list.back())
selectPreviousTab();
else
selectNextTab();
if (m_selected == tab)
m_selected = nullptr;
}
@ -119,7 +123,7 @@ void Tabs::removeTab(TabView* tabView)
m_removedTab = tab;
if (m_delegate)
tab->modified = m_delegate->isModified(this, tabView);
tab->modified = m_delegate->onIsModified(this, tabView);
tab->view = nullptr; // The view will be destroyed after Tabs::removeTab() anyway
// Next tab in the list
@ -185,7 +189,7 @@ void Tabs::selectNextTab()
if (it != currentTabIt) {
selectTabInternal(*it);
if (m_delegate)
m_delegate->clickTab(this, m_selected->view, kButtonLeft);
m_delegate->onSelectTab(this, m_selected->view);
}
}
}
@ -205,7 +209,7 @@ void Tabs::selectPreviousTab()
if (it != currentTabIt) {
selectTabInternal(*it);
if (m_delegate)
m_delegate->clickTab(this, m_selected->view, kButtonLeft);
m_delegate->onSelectTab(this, m_selected->view);
}
}
}
@ -255,7 +259,7 @@ bool Tabs::onProcessMessage(Message* msg)
if (m_selected && m_delegate &&
!m_clickedCloseButton &&
mouseMsg->left()) {
m_delegate->clickTab(this, m_selected->view, mouseMsg->buttons());
m_delegate->onSelectTab(this, m_selected->view);
}
captureMouse();
@ -267,11 +271,11 @@ bool Tabs::onProcessMessage(Message* msg)
MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
if (m_delegate && m_selected && m_selected == m_hot) {
if (m_hotCloseButton && m_clickedCloseButton) {
m_delegate->clickClose(this, m_selected->view);
if (mouseMsg->middle() || (m_hotCloseButton && m_clickedCloseButton)) {
m_delegate->onCloseTab(this, m_selected->view);
}
else if (!mouseMsg->left()) {
m_delegate->clickTab(this, m_selected->view, mouseMsg->buttons());
else if (mouseMsg->right()) {
m_delegate->onContextMenuTab(this, m_selected->view);
}
}
@ -511,7 +515,7 @@ void Tabs::drawTab(Graphics* g, const gfx::Rect& _box, Tab* tab, int dy,
if (m_delegate) {
if (tab->view)
tab->modified = m_delegate->isModified(this, tab->view);
tab->modified = m_delegate->onIsModified(this, tab->view);
if (tab->modified &&
(!hover || !m_hotCloseButton)) {
@ -588,7 +592,7 @@ void Tabs::calculateHot()
m_hotCloseButton = hotCloseButton;
if (m_delegate)
m_delegate->mouseOverTab(this, m_hot ? m_hot->view: NULL);
m_delegate->onMouseOverTab(this, m_hot ? m_hot->view: NULL);
invalidate();
}

View File

@ -45,17 +45,20 @@ namespace app {
public:
virtual ~TabsDelegate() { }
// Called when the user presses a mouse button over a tab.
virtual void clickTab(Tabs* tabs, TabView* tabView, ui::MouseButtons buttons) = 0;
// Called when the user selected the tab with the left mouse button.
virtual void onSelectTab(Tabs* tabs, TabView* tabView) = 0;
// When the tab close button is pressed.
virtual void clickClose(Tabs* tabs, TabView* tabView) = 0;
// When the tab close button is pressed (or middle mouse button is used to close it).
virtual void onCloseTab(Tabs* tabs, TabView* tabView) = 0;
// When the right-click is pressed in the tab.
virtual void onContextMenuTab(Tabs* tabs, TabView* tabView) = 0;
// Called when the mouse is over a tab (the data can be null if the
// mouse just leave all tabs)
virtual void mouseOverTab(Tabs* tabs, TabView* tabView) = 0;
virtual void onMouseOverTab(Tabs* tabs, TabView* tabView) = 0;
virtual bool isModified(Tabs* tabs, TabView* tabView) = 0;
virtual bool onIsModified(Tabs* tabs, TabView* tabView) = 0;
};
// Tabs control. Used to show opened documents.

View File

@ -78,6 +78,11 @@ void Workspace::removeView(WorkspaceView* view)
ActiveViewChanged(); // Fire ActiveViewChanged event
}
bool Workspace::closeView(WorkspaceView* view)
{
return view->onCloseView(this);
}
WorkspaceView* Workspace::activeView()
{
ASSERT(m_activePart != NULL);

View File

@ -30,6 +30,10 @@ namespace app {
void addView(WorkspaceView* view);
void removeView(WorkspaceView* view);
// Closes the given view. Returns false if the user cancels the
// operation.
bool closeView(WorkspaceView* view);
WorkspaceView* activeView();
void setActiveView(WorkspaceView* view);

View File

@ -52,8 +52,9 @@ void WorkspacePart::removeView(WorkspaceView* view)
removeChild(view->getContentWidget());
setActiveView((it != m_views.end() ?
*it : (!m_views.empty() ? m_views.front(): NULL)));
setActiveView(
(it != m_views.end() ?
*it : (!m_views.empty() ? m_views.back(): nullptr)));
}
void WorkspacePart::setActiveView(WorkspaceView* view)

View File

@ -29,7 +29,11 @@ namespace app {
// from the original view.
virtual void onClonedFrom(WorkspaceView* from) = 0;
virtual void onCloseView(Workspace* workspace) = 0;
// Returns true if the view was closed successfully or false if
// the user cancels the operation.
virtual bool onCloseView(Workspace* workspace) = 0;
virtual void onTabPopup(Workspace* workspace) = 0;
};
} // namespace app

View File

@ -127,7 +127,7 @@ DocumentView* UIContext::getFirstDocumentView(Document* document) const
{
Workspace* workspace = App::instance()->getMainWindow()->getWorkspace();
for (auto view : *workspace) {
for (WorkspaceView* view : *workspace) {
if (DocumentView* docView = dynamic_cast<DocumentView*>(view)) {
if (docView->getDocument() == document) {
return docView;
@ -177,8 +177,7 @@ void UIContext::onRemoveDocument(doc::Document* doc)
DocumentViews docViews;
// Collect all views related to the document.
for (Workspace::iterator it=workspace->begin(); it != workspace->end(); ++it) {
WorkspaceView* view = *it;
for (WorkspaceView* view : *workspace) {
if (DocumentView* docView = dynamic_cast<DocumentView*>(view)) {
if (docView->getDocument() == doc) {
docViews.push_back(docView);
@ -186,8 +185,7 @@ void UIContext::onRemoveDocument(doc::Document* doc)
}
}
for (DocumentViews::iterator it=docViews.begin(); it != docViews.end(); ++it) {
DocumentView* docView = *it;
for (DocumentView* docView : docViews) {
workspace->removeView(docView);
delete docView;
}