diff --git a/data/skins/default/sheet.png b/data/skins/default/sheet.png index 932b131dd..27bea5736 100644 Binary files a/data/skins/default/sheet.png and b/data/skins/default/sheet.png differ diff --git a/data/skins/default/skin.xml b/data/skins/default/skin.xml index e2e220767..9dcd78d78 100644 --- a/data/skins/default/skin.xml +++ b/data/skins/default/skin.xml @@ -413,6 +413,8 @@ + + diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index add7b0ac6..3d129d41d 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -350,6 +350,7 @@ add_library(app-lib transformation.cpp ui/ani_controls.cpp ui/app_menuitem.cpp + ui/backup_indicator.cpp ui/brush_popup.cpp ui/button_set.cpp ui/color_bar.cpp diff --git a/src/app/app.cpp b/src/app/app.cpp index c5803261c..1abb084bb 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -40,6 +40,7 @@ #include "app/shell.h" #include "app/tools/active_tool.h" #include "app/tools/tool_box.h" +#include "app/ui/backup_indicator.h" #include "app/ui/color_bar.h" #include "app/ui/document_view.h" #include "app/ui/editor/editor.h" @@ -56,6 +57,7 @@ #include "base/convert_to.h" #include "base/exception.h" #include "base/fs.h" +#include "base/scoped_lock.h" #include "base/split_string.h" #include "base/unique_ptr.h" #include "doc/document_observer.h" @@ -148,6 +150,7 @@ App::App() , m_isGui(false) , m_isShell(false) , m_exporter(NULL) + , m_backupIndicator(nullptr) { ASSERT(m_instance == NULL); m_instance = this; @@ -750,6 +753,11 @@ App::~App() // Save brushes m_brushes.reset(nullptr); + if (m_backupIndicator) { + delete m_backupIndicator; + m_backupIndicator = nullptr; + } + delete m_legacy; delete m_modules; delete m_coreModules; @@ -842,6 +850,20 @@ void App::showNotification(INotificationDelegate* del) m_mainWindow->showNotification(del); } +void App::showBackupNotification(bool state) +{ + base::scoped_lock lock(m_backupIndicatorMutex); + if (state) { + if (!m_backupIndicator) + m_backupIndicator = new BackupIndicator; + m_backupIndicator->start(); + } + else { + if (m_backupIndicator) + m_backupIndicator->stop(); + } +} + void App::updateDisplayTitleBar() { std::string defaultTitle = PACKAGE " v" VERSION; diff --git a/src/app/app.h b/src/app/app.h index 7b23b7cc6..ebd520e39 100644 --- a/src/app/app.h +++ b/src/app/app.h @@ -9,6 +9,7 @@ #pragma once #include "app/app_brushes.h" +#include "base/mutex.h" #include "base/unique_ptr.h" #include "doc/pixel_format.h" #include "obs/signal.h" @@ -27,6 +28,7 @@ namespace ui { namespace app { class AppOptions; + class BackupIndicator; class ContextBar; class Document; class DocumentExporter; @@ -83,6 +85,8 @@ namespace app { } void showNotification(INotificationDelegate* del); + // This can be called from a non-UI thread. + void showBackupNotification(bool state); void updateDisplayTitleBar(); InputChain& inputChain(); @@ -108,6 +112,8 @@ namespace app { FileList m_files; base::UniquePtr m_exporter; base::UniquePtr m_brushes; + BackupIndicator* m_backupIndicator; + base::mutex m_backupIndicatorMutex; }; void app_refresh_screen(); diff --git a/src/app/crash/backup_observer.cpp b/src/app/crash/backup_observer.cpp index 29889e16b..2f33e09ef 100644 --- a/src/app/crash/backup_observer.cpp +++ b/src/app/crash/backup_observer.cpp @@ -23,6 +23,24 @@ namespace app { namespace crash { +namespace { + +class SwitchBackupIcon { +public: + SwitchBackupIcon() { + App* app = App::instance(); + if (app) + app->showBackupNotification(true); + } + ~SwitchBackupIcon() { + App* app = App::instance(); + if (app) + app->showBackupNotification(false); + } +}; + +} + BackupObserver::BackupObserver(Session* session, doc::Context* ctx) : m_session(session) , m_ctx(ctx) @@ -66,7 +84,7 @@ void BackupObserver::backgroundThread() { int normalPeriod = 60*Preferences::instance().general.dataRecoveryPeriod(); int lockedPeriod = 10; -#if 0 // Just for testing purposes +#if 1 // Just for testing purposes normalPeriod = 5; lockedPeriod = 5; #endif @@ -79,6 +97,7 @@ void BackupObserver::backgroundThread() if (seconds >= waitUntil) { TRACE("RECO: Start backup process for %d documents\n", m_documents.size()); + SwitchBackupIcon icon; base::scoped_lock hold(m_mutex); base::Chrono chrono; bool somethingLocked = false; diff --git a/src/app/ui/backup_indicator.cpp b/src/app/ui/backup_indicator.cpp new file mode 100644 index 000000000..74df51674 --- /dev/null +++ b/src/app/ui/backup_indicator.cpp @@ -0,0 +1,58 @@ +// Aseprite +// Copyright (C) 2001-2016 David Capello +// +// This program is distributed under the terms of +// the End-User License Agreement for Aseprite. + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "app/ui/backup_indicator.h" + +#include "app/ui/status_bar.h" +#include "base/bind.h" +#include "ui/manager.h" + +namespace app { + +BackupIndicator::BackupIndicator() + : m_timer(100) + , m_small(false) + , m_running(false) +{ + m_timer.Tick.connect(base::Bind(&BackupIndicator::onTick, this)); +} + +BackupIndicator::~BackupIndicator() +{ + m_timer.stop(); +} + +void BackupIndicator::start() +{ + m_running = true; + m_timer.start(); +} + +void BackupIndicator::stop() +{ + m_running = false; +} + +void BackupIndicator::onTick() +{ + if (!m_running) { + StatusBar::instance()->showBackupIcon(StatusBar::BackupIcon::None); + m_timer.stop(); + return; + } + + StatusBar::instance()->showBackupIcon( + m_small ? StatusBar::BackupIcon::Small: + StatusBar::BackupIcon::Normal); + + m_small = !m_small; +} + +} // namespace app diff --git a/src/app/ui/backup_indicator.h b/src/app/ui/backup_indicator.h new file mode 100644 index 000000000..bb4861ccd --- /dev/null +++ b/src/app/ui/backup_indicator.h @@ -0,0 +1,33 @@ +// Aseprite +// Copyright (C) 2001-2016 David Capello +// +// This program is distributed under the terms of +// the End-User License Agreement for Aseprite. + +#ifndef APP_UI_BACKUP_INDICATOR_H_INCLUDED +#define APP_UI_BACKUP_INDICATOR_H_INCLUDED +#pragma once + +#include "ui/timer.h" + +namespace app { + + class BackupIndicator { + public: + BackupIndicator(); + ~BackupIndicator(); + + void start(); + void stop(); + + private: + void onTick(); + + ui::Timer m_timer; + bool m_small; + bool m_running; + }; + +} // namespace app + +#endif diff --git a/src/app/ui/status_bar.cpp b/src/app/ui/status_bar.cpp index ea601a582..5f454a355 100644 --- a/src/app/ui/status_bar.cpp +++ b/src/app/ui/status_bar.cpp @@ -182,7 +182,16 @@ class StatusBar::Indicators : public HBox { public: - Indicators() { + Indicators() : m_backupIcon(BackupIcon::None) { + m_leftArea.setBorder(gfx::Border(0)); + m_leftArea.setVisible(true); + m_leftArea.setExpansive(true); + + m_rightArea.setBorder(gfx::Border(0)); + m_rightArea.setVisible(false); + + addChild(&m_leftArea); + addChild(&m_rightArea); } void startIndicators() { @@ -210,7 +219,7 @@ public: auto indicator = new TextIndicator(text); m_indicators.push_back(indicator); m_iterator = m_indicators.end(); - addChild(indicator); + m_leftArea.addChild(indicator); } void addIconIndicator(she::Surface* icon, bool colored) { @@ -228,7 +237,7 @@ public: auto indicator = new IconIndicator(icon, colored); m_indicators.push_back(indicator); m_iterator = m_indicators.end(); - addChild(indicator); + m_leftArea.addChild(indicator); } void addColorIndicator(const app::Color& color) { @@ -246,7 +255,29 @@ public: auto indicator = new ColorIndicator(color); m_indicators.push_back(indicator); m_iterator = m_indicators.end(); - addChild(indicator); + m_leftArea.addChild(indicator); + } + + void showBackupIcon(BackupIcon icon) { + m_backupIcon = icon; + if (m_backupIcon != BackupIcon::None) { + she::Surface* icon = + (m_backupIcon == BackupIcon::Normal ? + SkinTheme::instance()->parts.iconSave()->bitmap(0): + SkinTheme::instance()->parts.iconSaveSmall()->bitmap(0)); + + m_rightArea.setVisible(true); + if (m_rightArea.children().empty()) { + m_rightArea.addChild(new IconIndicator(icon, true)); + } + else { + ((IconIndicator*)m_rightArea.lastChild())->updateIndicator(icon, true); + } + } + else { + m_rightArea.setVisible(false); + } + layout(); } private: @@ -255,7 +286,7 @@ private: auto end = m_indicators.end(); for (; it != end; ++it) { auto indicator = *it; - removeChild(indicator); + m_leftArea.removeChild(indicator); delete indicator; } m_indicators.erase(m_iterator, end); @@ -263,6 +294,9 @@ private: std::vector m_indicators; std::vector::iterator m_iterator; + BackupIcon m_backupIcon; + HBox m_leftArea; + HBox m_rightArea; }; class StatusBar::IndicatorsGeneration { @@ -572,6 +606,11 @@ void StatusBar::updateFromEditor(Editor* editor) m_zoomEntry->setZoom(editor->zoom()); } +void StatusBar::showBackupIcon(BackupIcon icon) +{ + m_indicators->showBackupIcon(icon); +} + bool StatusBar::setStatusText(int msecs, const char *format, ...) { if ((base::current_tick() > m_timeout) || (msecs > 0)) { diff --git a/src/app/ui/status_bar.h b/src/app/ui/status_bar.h index e87919850..7b9ce2ce2 100644 --- a/src/app/ui/status_bar.h +++ b/src/app/ui/status_bar.h @@ -51,6 +51,8 @@ namespace app { public: static StatusBar* instance() { return m_instance; } + enum BackupIcon { None, Normal, Small }; + StatusBar(); ~StatusBar(); @@ -65,6 +67,8 @@ namespace app { // Used by AppEditor to update the zoom level in the status bar. void updateFromEditor(Editor* editor); + void showBackupIcon(BackupIcon icon); + protected: void onResize(ui::ResizeEvent& ev) override; diff --git a/src/observable b/src/observable index 83dfe2753..27fa7f6a4 160000 --- a/src/observable +++ b/src/observable @@ -1 +1 @@ -Subproject commit 83dfe27536785acedb258e55ac762486d95e2ab0 +Subproject commit 27fa7f6a4bc7f686da49e6e895ff7dde864b88ad