diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index affa6231d..e3b7b64d3 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -285,6 +285,7 @@ add_library(app-lib tools/tool_box.cpp tools/tool_loop_manager.cpp transaction.cpp + ui/ani_controls.cpp ui/app_menuitem.cpp ui/button_set.cpp ui/color_bar.cpp diff --git a/src/app/commands/cmd_play_animation.cpp b/src/app/commands/cmd_play_animation.cpp index 12cfa0c4c..7a18fcd15 100644 --- a/src/app/commands/cmd_play_animation.cpp +++ b/src/app/commands/cmd_play_animation.cpp @@ -14,7 +14,6 @@ #include "app/context_access.h" #include "app/modules/editors.h" #include "app/ui/editor/editor.h" -#include "app/ui/status_bar.h" namespace app { @@ -61,9 +60,6 @@ void PlayAnimationCommand::onExecute(Context* context) current_editor->stop(); else current_editor->play(); - - // TODO this is an ugly hack, StatusBar should observe Editor changes - StatusBar::instance()->updateUsingEditor(current_editor); } Command* CommandFactory::createPlayAnimationCommand() diff --git a/src/app/ui/ani_controls.cpp b/src/app/ui/ani_controls.cpp new file mode 100644 index 000000000..aac1c5053 --- /dev/null +++ b/src/app/ui/ani_controls.cpp @@ -0,0 +1,108 @@ +// Aseprite +// Copyright (C) 2001-2015 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 +// published by the Free Software Foundation. + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "app/app.h" +#include "app/commands/commands.h" +#include "app/commands/params.h" +#include "app/context_access.h" +#include "app/document_access.h" +#include "app/document_range.h" +#include "app/modules/editors.h" +#include "app/modules/gfx.h" +#include "app/modules/gui.h" +#include "app/modules/palettes.h" +#include "app/settings/settings.h" +#include "app/tools/tool.h" +#include "app/ui/button_set.h" +#include "app/ui/color_button.h" +#include "app/ui/editor/editor.h" +#include "app/ui/keyboard_shortcuts.h" +#include "app/ui/main_window.h" +#include "app/ui/skin/skin_theme.h" +#include "app/ui/status_bar.h" +#include "app/ui/timeline.h" +#include "app/ui_context.h" +#include "app/util/range_utils.h" +#include "base/bind.h" +#include "doc/cel.h" +#include "doc/image.h" +#include "doc/layer.h" +#include "doc/sprite.h" +#include "gfx/size.h" +#include "she/font.h" +#include "she/surface.h" +#include "ui/ui.h" + +#include +#include +#include +#include + +namespace app { + +using namespace app::skin; +using namespace gfx; +using namespace ui; +using namespace doc; + +enum AniAction { + ACTION_FIRST, + ACTION_PREV, + ACTION_PLAY, + ACTION_NEXT, + ACTION_LAST, +}; + +AniControls::AniControls() + : ButtonSet(5) +{ + SkinTheme* theme = static_cast(this->getTheme()); + + addItem(theme->get_part(PART_ANI_FIRST)); + addItem(theme->get_part(PART_ANI_PREVIOUS)); + addItem(theme->get_part(PART_ANI_PLAY)); + addItem(theme->get_part(PART_ANI_NEXT)); + addItem(theme->get_part(PART_ANI_LAST)); + ItemChange.connect(Bind(&AniControls::onPlayButton, this)); + + setTriggerOnMouseUp(true); + setTransparent(true); + setBgColor(theme->colors.workspace()); +} + +void AniControls::updateUsingEditor(Editor* editor) +{ + SkinTheme* theme = static_cast(this->getTheme()); + getItem(ACTION_PLAY)->setIcon( + theme->get_part( + (editor && editor->isPlaying()) ? PART_ANI_STOP: PART_ANI_PLAY)); +} + +void AniControls::onPlayButton() +{ + int item = selectedItem(); + deselectItems(); + + Command* cmd = nullptr; + switch (item) { + case ACTION_FIRST: cmd = CommandsModule::instance()->getCommandByName(CommandId::GotoFirstFrame); break; + case ACTION_PREV: cmd = CommandsModule::instance()->getCommandByName(CommandId::GotoPreviousFrame); break; + case ACTION_PLAY: cmd = CommandsModule::instance()->getCommandByName(CommandId::PlayAnimation); break; + case ACTION_NEXT: cmd = CommandsModule::instance()->getCommandByName(CommandId::GotoNextFrame); break; + case ACTION_LAST: cmd = CommandsModule::instance()->getCommandByName(CommandId::GotoLastFrame); break; + } + if (cmd) { + UIContext::instance()->executeCommand(cmd); + updateUsingEditor(current_editor); + } +} + +} // namespace app diff --git a/src/app/ui/ani_controls.h b/src/app/ui/ani_controls.h new file mode 100644 index 000000000..fdf78074f --- /dev/null +++ b/src/app/ui/ani_controls.h @@ -0,0 +1,33 @@ +// Aseprite +// Copyright (C) 2001-2015 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 +// published by the Free Software Foundation. + +#ifndef APP_UI_ANI_CONTROLS_H_INCLUDED +#define APP_UI_ANI_CONTROLS_H_INCLUDED +#pragma once + +#include "app/ui/button_set.h" +#include "ui/widget.h" + +#include +#include + +namespace app { + class Editor; + + class AniControls : public ButtonSet { + public: + AniControls(); + + void updateUsingEditor(Editor* editor); + + private: + void onPlayButton(); + }; + +} // namespace app + +#endif diff --git a/src/app/ui/status_bar.cpp b/src/app/ui/status_bar.cpp index 77478cbba..2e28ada41 100644 --- a/src/app/ui/status_bar.cpp +++ b/src/app/ui/status_bar.cpp @@ -53,14 +53,6 @@ using namespace gfx; using namespace ui; using namespace doc; -enum AniAction { - ACTION_FIRST, - ACTION_PREV, - ACTION_PLAY, - ACTION_NEXT, - ACTION_LAST, -}; - static const char* kStatusBarText = "status_bar_text"; static const char* kStatusBarFace = "status_bar_face"; @@ -175,18 +167,9 @@ StatusBar::StatusBar() m_newFrame = new Button("+"); m_newFrame->Click.connect(Bind(&StatusBar::newFrame, this)); - setup_mini_look(m_slider); setup_mini_look(m_currentFrame); setup_mini_look(m_newFrame); - - m_buttonSet = new ButtonSet(5); - m_buttonSet->setTriggerOnMouseUp(true); - m_buttonSet->addItem(theme->get_part(PART_ANI_FIRST)); - m_buttonSet->addItem(theme->get_part(PART_ANI_PREVIOUS)); - m_buttonSet->addItem(theme->get_part(PART_ANI_PLAY)); - m_buttonSet->addItem(theme->get_part(PART_ANI_NEXT)); - m_buttonSet->addItem(theme->get_part(PART_ANI_LAST)); - m_buttonSet->ItemChange.connect(Bind(&StatusBar::onPlayButton, this)); + setup_mini_look(m_slider); m_slider->Change.connect(Bind(&slider_change_hook, m_slider)); m_slider->setMinSize(gfx::Size(ui::display_w()/5, 0)); @@ -197,7 +180,6 @@ StatusBar::StatusBar() box4->addChild(m_newFrame); box1->addChild(box4); - box1->addChild(m_buttonSet); box1->addChild(m_slider); m_commandsBox = box1; @@ -382,9 +364,16 @@ void StatusBar::onResize(ResizeEvent& ev) { setBoundsQuietly(ev.getBounds()); + Border border = getBorder(); Rect rc = ev.getBounds(); - rc.x += rc.w/2; - rc.w /= 2; + int w = rc.w/2 - border.getSize().w; + rc.x += w + border.left(); + rc.w = w; + + m_currentFrame->setVisible(w > 250*ui::guiscale()); + m_newFrame->setVisible(w > 250*ui::guiscale()); + m_slider->setVisible(w > 200*ui::guiscale()); + m_commandsBox->setBounds(rc); } @@ -476,7 +465,6 @@ void StatusBar::updateUsingEditor(Editor* editor) { updateFromDocument(editor); updateCurrentFrame(editor); - updatePlayButton(editor); } bool StatusBar::CustomizedTipWindow::onProcessMessage(Message* msg) @@ -517,25 +505,6 @@ static void slider_change_hook(Slider* slider) } } -void StatusBar::onPlayButton() -{ - int item = m_buttonSet->selectedItem(); - m_buttonSet->deselectItems(); - - Command* cmd = nullptr; - switch (item) { - case ACTION_FIRST: cmd = CommandsModule::instance()->getCommandByName(CommandId::GotoFirstFrame); break; - case ACTION_PREV: cmd = CommandsModule::instance()->getCommandByName(CommandId::GotoPreviousFrame); break; - case ACTION_PLAY: cmd = CommandsModule::instance()->getCommandByName(CommandId::PlayAnimation); break; - case ACTION_NEXT: cmd = CommandsModule::instance()->getCommandByName(CommandId::GotoNextFrame); break; - case ACTION_LAST: cmd = CommandsModule::instance()->getCommandByName(CommandId::GotoLastFrame); break; - } - if (cmd) { - UIContext::instance()->executeCommand(cmd); - updatePlayButton(current_editor); - } -} - void StatusBar::updateFromDocument(Editor* editor) { try { @@ -574,14 +543,6 @@ void StatusBar::updateCurrentFrame(Editor* editor) m_currentFrame->setTextf("%d", editor->frame()+1); } -void StatusBar::updatePlayButton(Editor* editor) -{ - SkinTheme* theme = static_cast(this->getTheme()); - m_buttonSet->getItem(ACTION_PLAY)->setIcon( - theme->get_part( - (editor && editor->isPlaying()) ? PART_ANI_STOP: PART_ANI_PLAY)); -} - void StatusBar::newFrame() { Command* cmd = CommandsModule::instance()->getCommandByName(CommandId::NewFrame); diff --git a/src/app/ui/status_bar.h b/src/app/ui/status_bar.h index 87124987f..a1d150a98 100644 --- a/src/app/ui/status_bar.h +++ b/src/app/ui/status_bar.h @@ -81,9 +81,7 @@ namespace app { void onCurrentToolChange(); void updateFromDocument(Editor* editor); void updateCurrentFrame(Editor* editor); - void updatePlayButton(Editor* editor); void newFrame(); - void onPlayButton(); enum State { SHOW_TEXT, SHOW_COLOR, SHOW_TOOL }; @@ -107,7 +105,6 @@ namespace app { ui::Slider* m_slider; // Opacity slider ui::Entry* m_currentFrame; // Current frame and go to frame entry ui::Button* m_newFrame; // Button to create a new frame - ButtonSet* m_buttonSet; // Tip window class CustomizedTipWindow; diff --git a/src/app/ui/timeline.cpp b/src/app/ui/timeline.cpp index 04aabde86..e19bb20a3 100644 --- a/src/app/ui/timeline.cpp +++ b/src/app/ui/timeline.cpp @@ -122,6 +122,7 @@ Timeline::Timeline() m_context->documents().addObserver(this); setDoubleBuffered(true); + addChild(&m_aniControls); } Timeline::~Timeline() @@ -135,6 +136,8 @@ Timeline::~Timeline() void Timeline::updateUsingEditor(Editor* editor) { + m_aniControls.updateUsingEditor(editor); + // As a sprite editor was selected, it looks like the user wants to // execute commands targetting the editor instead of the // timeline. Here we disable the selected range, so commands like @@ -417,7 +420,7 @@ bool Timeline::onProcessMessage(Message* msg) // tracked to the mouse's released). if (m_clk.part == PART_SEPARATOR) { m_separator_x = MAX(0, mousePos.x); - invalidate(); + layout(); return true; } } @@ -781,6 +784,18 @@ void Timeline::onPreferredSize(PreferredSizeEvent& ev) ev.setPreferredSize(Size(32, 32)); } +void Timeline::onResize(ui::ResizeEvent& ev) +{ + gfx::Rect rc = ev.getBounds(); + setBoundsQuietly(rc); + + gfx::Size sz = m_aniControls.getPreferredSize(); + m_aniControls.setBounds( + gfx::Rect(rc.x, rc.y, MIN(sz.w, m_separator_x), + getFont()->height() + + skinTheme()->dimensions.timelineTagsAreaHeight())); +} + void Timeline::onPaint(ui::PaintEvent& ev) { Graphics* g = ev.getGraphics(); @@ -964,6 +979,11 @@ void Timeline::onSelectionChanged(doc::DocumentEvent& ev) invalidate(); } +void Timeline::onStateChanged(Editor* editor) +{ + m_aniControls.updateUsingEditor(editor); +} + void Timeline::onAfterFrameChanged(Editor* editor) { setFrame(editor->frame()); @@ -1333,13 +1353,11 @@ void Timeline::drawFrameTags(ui::Graphics* g) SkinTheme* theme = skinTheme(); SkinTheme::Styles& styles = theme->styles; - if (!m_sprite->frameTags().empty()) { - g->fillRect(theme->colors.workspace(), - gfx::Rect( - 0, getFont()->height(), - getClientBounds().w, - theme->dimensions.timelineTagsAreaHeight())); - } + g->fillRect(theme->colors.workspace(), + gfx::Rect( + 0, getFont()->height(), + getClientBounds().w, + theme->dimensions.timelineTagsAreaHeight())); for (FrameTag* frameTag : m_sprite->frameTags()) { gfx::Rect bounds1 = getPartBounds(Hit(PART_HEADER_FRAME, firstLayer(), frameTag->fromFrame())); @@ -2303,10 +2321,8 @@ int Timeline::topHeight() const int h = 0; if (m_document && m_sprite) { h += skinTheme()->dimensions.timelineTopBorder(); - if (!m_sprite->frameTags().empty()) { - h += getFont()->height(); - h += skinTheme()->dimensions.timelineTagsAreaHeight(); - } + h += getFont()->height(); + h += skinTheme()->dimensions.timelineTagsAreaHeight(); } return h; } diff --git a/src/app/ui/timeline.h b/src/app/ui/timeline.h index 28df0f573..a80a0e2d7 100644 --- a/src/app/ui/timeline.h +++ b/src/app/ui/timeline.h @@ -11,6 +11,7 @@ #include "app/document_range.h" #include "app/pref/preferences.h" +#include "app/ui/ani_controls.h" #include "app/ui/editor/editor_observer.h" #include "base/connection.h" #include "doc/document_observer.h" @@ -96,6 +97,7 @@ namespace app { protected: bool onProcessMessage(ui::Message* msg) override; void onPreferredSize(ui::PreferredSizeEvent& ev) override; + void onResize(ui::ResizeEvent& ev) override; void onPaint(ui::PaintEvent& ev) override; // DocumentObserver impl. @@ -112,6 +114,7 @@ namespace app { void onRemoveDocument(doc::Document* document) override; // EditorObserver impl. + void onStateChanged(Editor* editor) override; void onAfterFrameChanged(Editor* editor) override; void onAfterLayerChanged(Editor* editor) override; void onDestroyEditor(Editor* editor) override; @@ -253,6 +256,8 @@ namespace app { bool m_scroll; // True if the drag-and-drop operation is a scroll operation. bool m_copy; // True if the drag-and-drop operation is a copy. + + AniControls m_aniControls; }; } // namespace app