Move animation controls from StatusBar to Timeline

This commit is contained in:
David Capello 2015-04-07 10:48:04 -03:00
parent fbb660da8d
commit 0576bafaa3
8 changed files with 185 additions and 68 deletions

View File

@ -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

View File

@ -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()

108
src/app/ui/ani_controls.cpp Normal file
View File

@ -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 <algorithm>
#include <cstdarg>
#include <cstdio>
#include <cstring>
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<SkinTheme*>(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<SkinTheme*>(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

33
src/app/ui/ani_controls.h Normal file
View File

@ -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 <string>
#include <vector>
namespace app {
class Editor;
class AniControls : public ButtonSet {
public:
AniControls();
void updateUsingEditor(Editor* editor);
private:
void onPlayButton();
};
} // namespace app
#endif

View File

@ -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<void>(&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<void>(&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<SkinTheme*>(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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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