diff --git a/data/skins/default/sheet.png b/data/skins/default/sheet.png index b2628d875..150ff3d51 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 617269e4d..5ab0a8869 100644 --- a/data/skins/default/skin.xml +++ b/data/skins/default/skin.xml @@ -400,6 +400,15 @@ + + + + + + + + + diff --git a/src/app/tools/controllers.h b/src/app/tools/controllers.h index 35bab4bed..4cbca8a75 100644 --- a/src/app/tools/controllers.h +++ b/src/app/tools/controllers.h @@ -81,7 +81,7 @@ public: return; char buf[1024]; - sprintf(buf, "Start %3d %3d End %3d %3d", + sprintf(buf, ":start: %3d %3d :end: %3d %3d", stroke.firstPoint().x, stroke.firstPoint().y, stroke.lastPoint().x, @@ -202,7 +202,7 @@ public: return; char buf[1024]; - sprintf(buf, "Start %3d %3d End %3d %3d (Size %3d %3d) Angle %.1f", + sprintf(buf, ":start: %3d %3d :end: %3d %3d :size: %3d %3d :angle: %.1f", stroke[0].x, stroke[0].y, stroke[1].x, stroke[1].y, ABS(stroke[1].x-stroke[0].x)+1, @@ -264,7 +264,7 @@ public: return; char buf[1024]; - sprintf(buf, "Start %3d %3d End %3d %3d", + sprintf(buf, ":start: %3d %3d :end: %3d %3d", stroke.firstPoint().x, stroke.firstPoint().y, stroke.lastPoint().x, @@ -303,7 +303,7 @@ public: return; char buf[1024]; - sprintf(buf, "Pos %3d %3d", stroke[0].x, stroke[0].y); + sprintf(buf, ":pos: %3d %3d", stroke[0].x, stroke[0].y); text = buf; } @@ -358,7 +358,7 @@ public: return; char buf[1024]; - sprintf(buf, "Start %3d %3d End %3d %3d (%3d %3d - %3d %3d)", + sprintf(buf, ":start: %3d %3d :end: %3d %3d (%3d %3d - %3d %3d)", stroke[0].x, stroke[0].y, stroke[3].x, stroke[3].y, stroke[1].x, stroke[1].y, diff --git a/src/app/ui/editor/moving_cel_state.cpp b/src/app/ui/editor/moving_cel_state.cpp index 65cbf8edf..ffbc33e25 100644 --- a/src/app/ui/editor/moving_cel_state.cpp +++ b/src/app/ui/editor/moving_cel_state.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2015 David Capello +// Copyright (C) 2001-2016 David Capello // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -163,7 +163,7 @@ bool MovingCelState::onUpdateStatusBar(Editor* editor) { StatusBar::instance()->setStatusText (0, - "Pos %3d %3d Offset %3d %3d", + ":pos: %3d %3d :offset: %3d %3d", (int)m_cursorStart.x, (int)m_cursorStart.y, (int)m_celOffset.x, diff --git a/src/app/ui/editor/moving_pixels_state.cpp b/src/app/ui/editor/moving_pixels_state.cpp index 0be8360c2..c214e96b9 100644 --- a/src/app/ui/editor/moving_pixels_state.cpp +++ b/src/app/ui/editor/moving_pixels_state.cpp @@ -437,10 +437,10 @@ bool MovingPixelsState::onUpdateStatusBar(Editor* editor) gfx::Size imageSize = m_pixelsMovement->getInitialImageSize(); StatusBar::instance()->setStatusText - (100, "Moving Pixels - Pos %d %d, Size %d %d, Orig: %3d %3d (%.02f%% %.02f%%), Angle %.1f", + (100, "Moving Pixels: :pos: %d %d :size: %3d %3d :selsize: %d %d [%.02f%% %.02f%%] :angle: %.1f", transform.bounds().x, transform.bounds().y, - transform.bounds().w, transform.bounds().h, imageSize.w, imageSize.h, + transform.bounds().w, transform.bounds().h, (double)transform.bounds().w*100.0/imageSize.w, (double)transform.bounds().h*100.0/imageSize.h, 180.0 * transform.angle() / PI); diff --git a/src/app/ui/editor/standby_state.cpp b/src/app/ui/editor/standby_state.cpp index 1244198f1..4f4b348d8 100644 --- a/src/app/ui/editor/standby_state.cpp +++ b/src/app/ui/editor/standby_state.cpp @@ -445,7 +445,7 @@ bool StandbyState::onUpdateStatusBar(Editor* editor) cmd.pickSample(editor->getSite(), spritePos, color); char buf[256]; - sprintf(buf, "- Pos %d %d", spritePos.x, spritePos.y); + sprintf(buf, " :pos: %d %d", spritePos.x, spritePos.y); StatusBar::instance()->showColor(0, buf, color); } @@ -454,13 +454,21 @@ bool StandbyState::onUpdateStatusBar(Editor* editor) (editor->document()->isMaskVisible() ? editor->document()->mask(): NULL); - StatusBar::instance()->setStatusText(0, - "Pos %d %d, Size %d %d, Frame %d [%d msecs]", + char buf[1024]; + sprintf( + buf, ":pos: %d %d :%s: %d %d", spritePos.x, spritePos.y, + (mask ? "selsize": "size"), (mask ? mask->bounds().w: sprite->width()), - (mask ? mask->bounds().h: sprite->height()), - editor->frame()+1, - sprite->frameDuration(editor->frame())); + (mask ? mask->bounds().h: sprite->height())); + if (sprite->totalFrames() > 1) { + sprintf( + buf+strlen(buf), " :frame: %d :clock: %d", + editor->frame()+1, + sprite->frameDuration(editor->frame())); + } + + StatusBar::instance()->setStatusText(0, buf); } return true; diff --git a/src/app/ui/status_bar.cpp b/src/app/ui/status_bar.cpp index 62951eed9..bf836f67d 100644 --- a/src/app/ui/status_bar.cpp +++ b/src/app/ui/status_bar.cpp @@ -45,9 +45,11 @@ #include "ui/ui.h" #include +#include #include #include #include +#include namespace app { @@ -56,6 +58,313 @@ using namespace gfx; using namespace ui; using namespace doc; +class StatusBar::Indicators : public HBox { + + class Indicator : public Widget { + public: + enum IndicatorType { + kText, + kIcon, + kColor + }; + Indicator(IndicatorType type) : m_type(type) { } + IndicatorType indicatorType() const { return m_type; } + private: + IndicatorType m_type; + }; + + class TextIndicator : public Indicator { + public: + TextIndicator(const char* text) : Indicator(kText) { + updateIndicator(text); + } + + void updateIndicator(const char* text) { + if (this->text() == text) + return; + + setText(text); + + if (minSize().w > textSize().w*2) + setMinSize(textSize()); + else + setMinSize(minSize().createUnion(textSize())); + } + + private: + void onPaint(ui::PaintEvent& ev) override { + SkinTheme* theme = static_cast(this->theme()); + gfx::Color textColor = theme->colors.statusBarText(); + Rect rc = clientBounds(); + Graphics* g = ev.graphics(); + + g->fillRect(bgColor(), rc); + if (textLength() > 0) { + g->drawString(text(), textColor, ColorNone, + Point(rc.x, rc.y + rc.h/2 - font()->height()/2)); + } + } + }; + + class IconIndicator : public Indicator { + public: + IconIndicator(she::Surface* icon, bool colored) + : Indicator(kIcon) + , m_icon(nullptr) + , m_colored(colored) { + updateIndicator(icon, colored); + } + + void updateIndicator(she::Surface* icon, bool colored) { + if (m_icon == icon && m_colored == colored) + return; + + ASSERT(icon); + + m_icon = icon; + m_colored = colored; + setMinSize(minSize().createUnion(Size(m_icon->width(), + m_icon->height()))); + } + + private: + void onPaint(ui::PaintEvent& ev) override { + SkinTheme* theme = static_cast(this->theme()); + gfx::Color textColor = theme->colors.statusBarText(); + Rect rc = clientBounds(); + Graphics* g = ev.graphics(); + + g->fillRect(bgColor(), rc); + if (m_colored) + g->drawColoredRgbaSurface( + m_icon, textColor, + rc.x, rc.y + rc.h/2 - m_icon->height()/2); + else + g->drawRgbaSurface( + m_icon, + rc.x, rc.y + rc.h/2 - m_icon->height()/2); + } + + she::Surface* m_icon; + bool m_colored; + }; + + class ColorIndicator : public Indicator { + public: + ColorIndicator(const app::Color& color) + : Indicator(kColor) + , m_color(Color::fromMask()) { + updateIndicator(color, true); + } + + void updateIndicator(const app::Color& color, bool first = false) { + if (m_color == color && !first) + return; + + m_color = color; + setMinSize(minSize().createUnion(Size(32*guiscale(), 1))); + } + + private: + void onPaint(ui::PaintEvent& ev) override { + SkinTheme* theme = static_cast(this->theme()); + Rect rc = clientBounds(); + Graphics* g = ev.graphics(); + + g->fillRect(bgColor(), rc); + draw_color_button( + g, Rect(rc.x, rc.y, 32*guiscale(), rc.h), + m_color, + (doc::ColorMode)app_get_current_pixel_format(), false, false); + } + + app::Color m_color; + }; + +public: + + Indicators() { + } + + void startIndicators() { + m_iterator = m_indicators.begin(); + } + + void endIndicators() { + removeAllNextIndicators(); + layout(); + } + + void addTextIndicator(const char* text) { + // Re-use indicator + if (m_iterator != m_indicators.end()) { + if ((*m_iterator)->indicatorType() == Indicator::kText) { + static_cast(*m_iterator) + ->updateIndicator(text); + ++m_iterator; + return; + } + else + removeAllNextIndicators(); + } + + auto indicator = new TextIndicator(text); + m_indicators.push_back(indicator); + m_iterator = m_indicators.end(); + addChild(indicator); + } + + void addIconIndicator(she::Surface* icon, bool colored) { + if (m_iterator != m_indicators.end()) { + if ((*m_iterator)->indicatorType() == Indicator::kIcon) { + static_cast(*m_iterator) + ->updateIndicator(icon, colored); + ++m_iterator; + return; + } + else + removeAllNextIndicators(); + } + + auto indicator = new IconIndicator(icon, colored); + m_indicators.push_back(indicator); + m_iterator = m_indicators.end(); + addChild(indicator); + } + + void addColorIndicator(const app::Color& color) { + if (m_iterator != m_indicators.end()) { + if ((*m_iterator)->indicatorType() == Indicator::kColor) { + static_cast(*m_iterator) + ->updateIndicator(color); + ++m_iterator; + return; + } + else + removeAllNextIndicators(); + } + + auto indicator = new ColorIndicator(color); + m_indicators.push_back(indicator); + m_iterator = m_indicators.end(); + addChild(indicator); + } + +private: + void removeAllNextIndicators() { + auto it = m_iterator; + auto end = m_indicators.end(); + for (; it != end; ++it) { + auto indicator = *it; + removeChild(indicator); + delete indicator; + } + m_indicators.erase(m_iterator, end); + } + + std::vector m_indicators; + std::vector::iterator m_iterator; +}; + +class StatusBar::IndicatorsGeneration { +public: + IndicatorsGeneration(StatusBar::Indicators* indicators) + : m_indicators(indicators) { + m_indicators->startIndicators(); + } + + ~IndicatorsGeneration() { + m_indicators->endIndicators(); + } + + IndicatorsGeneration& add(const char* text) { + auto theme = SkinTheme::instance(); + + for (auto i = text; *i; ) { + // Icon + if (*i == ':' && (i == text || *(i-1) == ' ')) { + const char* j = i+1; + for (; *j; ++j) { + if (*j == ':') + break; + } + + if (*(j+1) == 0 || *(j+1) == ' ') { + if (i != text) { + // Here i is ':' and i-1 is a whitespace ' ' + m_indicators->addTextIndicator(std::string(text, i-1).c_str()); + } + + auto part = theme->getPartById("icon_" + std::string(i+1, j)); + if (part) + add(part.get(), true); + + text = i = (*(j+1) == ' ' ? j+2: j+1); + } + } + else + ++i; + } + + if (*text != 0) + m_indicators->addTextIndicator(text); + + return *this; + } + + IndicatorsGeneration& add(she::Surface* icon, bool colored) { + if (icon) + m_indicators->addIconIndicator(icon, colored); + return *this; + } + + IndicatorsGeneration& add(const skin::SkinPart* part, bool colored) { + return add(part->bitmap(0), colored); + } + + IndicatorsGeneration& add(const app::Color& color) { + auto theme = SkinTheme::instance(); + + // Eyedropper icon + add(theme->getToolIcon("eyedropper"), false); + + // Color + m_indicators->addColorIndicator(color); + + // Color description + std::string str = color.toHumanReadableString( + app_get_current_pixel_format(), + app::Color::LongHumanReadableString); + if (color.getAlpha() < 255) { + char buf[256]; + sprintf(buf, " \xCE\xB1%d", color.getAlpha()); + str += buf; + } + m_indicators->addTextIndicator(str.c_str()); + + return *this; + } + + IndicatorsGeneration& add(tools::Tool* tool) { + auto theme = SkinTheme::instance(); + + // Tool icon + text + add(theme->getToolIcon(tool->getId().c_str()), false); + m_indicators->addTextIndicator(tool->getText().c_str()); + + // Tool shortcut + Key* key = KeyboardShortcuts::instance()->tool(tool); + if (key && !key->accels().empty()) { + add(theme->parts.iconKey()->bitmap(0), true); + m_indicators->addTextIndicator(key->accels().front().toString().c_str()); + } + return *this; + } + +private: + StatusBar::Indicators* m_indicators; +}; + class StatusBar::CustomizedTipWindow : public ui::TipWindow { public: CustomizedTipWindow(const std::string& text) @@ -77,7 +386,14 @@ public: } protected: - bool onProcessMessage(Message* msg); + bool onProcessMessage(Message* msg) override { + switch (msg->type()) { + case kTimerMessage: + closeWindow(NULL); + break; + } + return ui::TipWindow::onProcessMessage(msg); + } private: base::UniquePtr m_timer; @@ -112,14 +428,6 @@ private: ui::Button m_button; }; -static WidgetType statusbar_type() -{ - static WidgetType type = kGenericWidget; - if (type == kGenericWidget) - type = register_widget_type(); - return type; -} - // This widget is used to show the current frame. class GotoFrameEntry : public Entry { public: @@ -164,10 +472,8 @@ public: StatusBar* StatusBar::m_instance = NULL; StatusBar::StatusBar() - : Widget(statusbar_type()) - , m_timeout(0) - , m_state(SHOW_TEXT) - , m_color(app::Color::fromMask()) + : m_timeout(0) + , m_indicators(new Indicators) , m_docControls(new HBox) , m_doc(nullptr) , m_tipwindow(nullptr) @@ -180,14 +486,15 @@ StatusBar::StatusBar() SkinTheme* theme = static_cast(this->theme()); setBgColor(theme->colors.statusBarFace()); - this->setFocusStop(true); + setFocusStop(true); + setBorder(gfx::Border(6*guiscale(), 0, 6*guiscale(), 0)); - // The extra pixel in left and right borders are necessary so - // m_commandsBox and m_movePixelsBox do not overlap the upper-left - // and upper-right pixels drawn in onPaint() event (see putpixels) - setBorder(gfx::Border(1*guiscale(), 0, 1*guiscale(), 0)); + setMinSize(Size(0, textHeight()+8*guiscale())); + setMaxSize(Size(INT_MAX, textHeight()+8*guiscale())); + m_indicators->setExpansive(true); m_docControls->setVisible(false); + addChild(m_indicators); addChild(m_docControls); // Construct the commands box @@ -271,12 +578,8 @@ bool StatusBar::setStatusText(int msecs, const char *format, ...) vsprintf(buf, format, ap); va_end(ap); + IndicatorsGeneration(m_indicators).add(buf); m_timeout = base::current_tick() + msecs; - m_state = SHOW_TEXT; - - setText(buf); - invalidate(); - return true; } else @@ -315,39 +618,21 @@ void StatusBar::showTip(int msecs, const char *format, ...) m_tipwindow->startTimer(); // Set the text in status-bar (with inmediate timeout) + IndicatorsGeneration(m_indicators).add(buf); m_timeout = base::current_tick(); - setText(buf); - invalidate(); } void StatusBar::showColor(int msecs, const char* text, const app::Color& color) { if (setStatusText(msecs, text)) { - m_state = SHOW_COLOR; - m_color = color; + IndicatorsGeneration(m_indicators).add(color); } } void StatusBar::showTool(int msecs, tools::Tool* tool) { ASSERT(tool != NULL); - - // Tool name - std::string text = tool->getText(); - - // Tool shortcut - Key* key = KeyboardShortcuts::instance()->tool(tool); - if (key && !key->accels().empty()) { - text += ", Shortcut: "; - text += key->accels().front().toString(); - } - - // Set text - if (setStatusText(msecs, text.c_str())) { - // Show tool - m_state = SHOW_TOOL; - m_tool = tool; - } + IndicatorsGeneration(m_indicators).add(tool); } void StatusBar::showSnapToGridWarning(bool state) @@ -387,106 +672,10 @@ void StatusBar::showSnapToGridWarning(bool state) void StatusBar::onResize(ResizeEvent& ev) { - setBoundsQuietly(ev.bounds()); - - Border border = this->border(); Rect rc = ev.bounds(); - bool docControls = (rc.w > 300*ui::guiscale()); - if (docControls) { - int prefWidth = m_docControls->sizeHint().w; - int toolBarWidth = ToolBar::instance()->sizeHint().w; + m_docControls->setVisible(m_doc && rc.w > 300*ui::guiscale()); - rc.x += rc.w - prefWidth - border.right() - toolBarWidth; - rc.w = prefWidth; - - m_docControls->setVisible(m_doc != nullptr); - m_docControls->setBounds(rc); - } - else - m_docControls->setVisible(false); -} - -void StatusBar::onSizeHint(SizeHintEvent& ev) -{ - int s = 4*guiscale() + textHeight() + 4*guiscale(); - ev.setSizeHint(Size(s, s)); -} - -void StatusBar::onPaint(ui::PaintEvent& ev) -{ - SkinTheme* theme = static_cast(this->theme()); - gfx::Color textColor = theme->colors.statusBarText(); - Rect rc = clientBounds(); - Graphics* g = ev.graphics(); - - g->fillRect(bgColor(), rc); - - rc.shrink(Border(2, 1, 2, 2)*guiscale()); - - int x = rc.x + 4*guiscale(); - - // Color - if (m_state == SHOW_COLOR) { - // Draw eyedropper icon - she::Surface* icon = theme->getToolIcon("eyedropper"); - if (icon) { - g->drawRgbaSurface(icon, x, rc.y + rc.h/2 - icon->height()/2); - x += icon->width() + 4*guiscale(); - } - - // Draw color - draw_color_button( - g, gfx::Rect(x, rc.y, 32*guiscale(), rc.h), - m_color, - (doc::ColorMode)app_get_current_pixel_format(), false, false); - - x += (32+4)*guiscale(); - - // Draw color description - std::string str = m_color.toHumanReadableString( - app_get_current_pixel_format(), - app::Color::LongHumanReadableString); - if (m_color.getAlpha() < 255) { - char buf[256]; - sprintf(buf, " \xCE\xB1%d", m_color.getAlpha()); - str += buf; - } - - g->drawString(str, textColor, ColorNone, - gfx::Point(x, rc.y + rc.h/2 - font()->height()/2)); - - x += font()->textLength(str.c_str()) + 4*guiscale(); - } - - // Show tool - if (m_state == SHOW_TOOL) { - // Draw eyedropper icon - she::Surface* icon = theme->getToolIcon(m_tool->getId().c_str()); - if (icon) { - g->drawRgbaSurface(icon, x, rc.y + rc.h/2 - icon->height()/2); - x += icon->width() + 4*guiscale(); - } - } - - // Status bar text - if (textLength() > 0) { - g->drawString(text(), textColor, ColorNone, - gfx::Point(x, rc.y + rc.h/2 - font()->height()/2)); - - x += font()->textLength(text().c_str()) + 4*guiscale(); - } -} - -bool StatusBar::CustomizedTipWindow::onProcessMessage(Message* msg) -{ - switch (msg->type()) { - - case kTimerMessage: - closeWindow(NULL); - break; - } - - return ui::TipWindow::onProcessMessage(msg); + HBox::onResize(ev); } void StatusBar::onActiveSiteChange(const doc::Site& site) diff --git a/src/app/ui/status_bar.h b/src/app/ui/status_bar.h index 1a53a3167..06a3bdd00 100644 --- a/src/app/ui/status_bar.h +++ b/src/app/ui/status_bar.h @@ -17,7 +17,7 @@ #include "doc/documents_observer.h" #include "doc/layer_index.h" #include "ui/base.h" -#include "ui/widget.h" +#include "ui/box.h" #include #include @@ -43,7 +43,7 @@ namespace app { class Tool; } - class StatusBar : public ui::Widget + class StatusBar : public ui::HBox , public doc::ContextObserver , public doc::DocumentsObserver , public doc::DocumentObserver { @@ -67,8 +67,6 @@ namespace app { protected: void onResize(ui::ResizeEvent& ev) override; - void onSizeHint(ui::SizeHintEvent& ev) override; - void onPaint(ui::PaintEvent& ev) override; // ContextObserver impl void onActiveSiteChange(const doc::Site& site) override; @@ -85,16 +83,12 @@ namespace app { void newFrame(); void onChangeZoom(const render::Zoom& zoom); - enum State { SHOW_TEXT, SHOW_COLOR, SHOW_TOOL }; - base::tick_t m_timeout; - State m_state; - // Showing a tool - tools::Tool* m_tool; - - // Showing a color - Color m_color; + // Indicators + class Indicators; + class IndicatorsGeneration; + Indicators* m_indicators; // Box of main commands ui::Widget* m_docControls; diff --git a/src/app/ui/timeline.cpp b/src/app/ui/timeline.cpp index 8b4260f99..790875621 100644 --- a/src/app/ui/timeline.cpp +++ b/src/app/ui/timeline.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2015 David Capello +// Copyright (C) 2001-2016 David Capello // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -2181,7 +2181,7 @@ void Timeline::updateStatusBar(ui::Message* msg) case PART_HEADER_FRAME: if (validFrame(m_hot.frame)) { sb->setStatusText(0, - "Frame %d [%d msecs]", + ":frame: %d :clock: %d", (int)m_hot.frame+1, m_sprite->frameDuration(m_hot.frame)); return;