mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-26 17:37:07 +00:00
Merge branch 'master' into beta
This commit is contained in:
commit
396f5fb78f
14
data/gui.xml
14
data/gui.xml
@ -205,7 +205,8 @@
|
||||
</key>
|
||||
|
||||
<!-- Zoom -->
|
||||
<key command="Zoom" shortcut="~"><param name="percentage" value="50" /></key>
|
||||
<key command="Zoom" shortcut="~"><param name="percentage" value="50" /></key> <!-- US PC -->
|
||||
<key command="Zoom" shortcut="`"><param name="percentage" value="50" /></key> <!-- US Mac -->
|
||||
<key command="Zoom" shortcut="1"><param name="percentage" value="100" /></key>
|
||||
<key command="Zoom" shortcut="2"><param name="percentage" value="200" /></key>
|
||||
<key command="Zoom" shortcut="3"><param name="percentage" value="400" /></key>
|
||||
@ -226,6 +227,17 @@
|
||||
<param name="action" value="in" />
|
||||
</key>
|
||||
|
||||
<!-- Layer opacity -->
|
||||
<key command="LayerOpacity"><param name="opacity" value="0" /></key>
|
||||
<key command="LayerOpacity"><param name="opacity" value="32" /></key>
|
||||
<key command="LayerOpacity" shortcut="Shift+1"><param name="opacity" value="64" /></key>
|
||||
<key command="LayerOpacity"><param name="opacity" value="96" /></key>
|
||||
<key command="LayerOpacity" shortcut="Shift+2"><param name="opacity" value="128" /></key>
|
||||
<key command="LayerOpacity"><param name="opacity" value="160" /></key>
|
||||
<key command="LayerOpacity" shortcut="Shift+3"><param name="opacity" value="192" /></key>
|
||||
<key command="LayerOpacity"><param name="opacity" value="224" /></key>
|
||||
<key command="LayerOpacity" shortcut="Shift+4"><param name="opacity" value="255" /></key>
|
||||
|
||||
<!-- Scroll to center -->
|
||||
<key command="ScrollCenter" shortcut="Shift+C" />
|
||||
<key command="FitScreen" shortcut="Ctrl+0" mac="Cmd+0" />
|
||||
|
@ -10,6 +10,8 @@
|
||||
<value id="PICK_FGCOLOR" value="1" />
|
||||
<value id="ERASE" value="2" />
|
||||
<value id="SCROLL" value="3" />
|
||||
<value id="RECTANGULAR_MARQUEE" value="4" />
|
||||
<value id="LASSO" value="5" />
|
||||
</enum>
|
||||
<enum id="OnionskinType">
|
||||
<value id="MERGE" value="0" />
|
||||
@ -117,6 +119,7 @@
|
||||
<option id="right_click_mode" type="RightClickMode" default="RightClickMode::PAINT_BGCOLOR" migrate="Options.RightClickMode" />
|
||||
<option id="auto_select_layer" type="bool" default="false" migrate="Options.AutoSelectLayer" />
|
||||
<option id="play_once" type="bool" default="false" />
|
||||
<option id="play_all" type="bool" default="false" />
|
||||
</section>
|
||||
<section id="cursor">
|
||||
<option id="use_native_cursor" type="bool" default="false" migrate="experimental.use_native_cursor" />
|
||||
@ -127,6 +130,7 @@
|
||||
</section>
|
||||
<section id="preview" text="Preview">
|
||||
<option id="play_once" type="bool" default="false" />
|
||||
<option id="play_all" type="bool" default="false" />
|
||||
</section>
|
||||
<section id="theme" text="Theme">
|
||||
<option id="selected" type="std::string" default=""default"" migrate="Skin.Selected" />
|
||||
|
@ -231,6 +231,7 @@ add_library(app-lib
|
||||
commands/cmd_keyboard_shortcuts.cpp
|
||||
commands/cmd_launch.cpp
|
||||
commands/cmd_layer_from_background.cpp
|
||||
commands/cmd_layer_opacity.cpp
|
||||
commands/cmd_layer_properties.cpp
|
||||
commands/cmd_layer_visibility.cpp
|
||||
commands/cmd_link_cels.cpp
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/app_menus.h"
|
||||
#include "app/commands/command.h"
|
||||
#include "app/context.h"
|
||||
@ -15,6 +16,7 @@
|
||||
#include "app/modules/gui.h"
|
||||
#include "app/resource_finder.h"
|
||||
#include "app/tools/tool.h"
|
||||
#include "app/tools/tool_box.h"
|
||||
#include "app/ui/app_menuitem.h"
|
||||
#include "app/ui/keyboard_shortcuts.h"
|
||||
#include "app/ui/search_entry.h"
|
||||
@ -38,8 +40,9 @@
|
||||
|
||||
namespace app {
|
||||
|
||||
using namespace ui;
|
||||
using namespace skin;
|
||||
using namespace tools;
|
||||
using namespace ui;
|
||||
|
||||
static int g_sep = 0;
|
||||
|
||||
@ -410,7 +413,13 @@ private:
|
||||
|
||||
// Load keyboard shortcuts
|
||||
fillList(this->menus(), AppMenus::instance()->getRootMenu(), 0);
|
||||
fillList(this->tools(), App::instance()->toolBox());
|
||||
for (Key* key : *app::KeyboardShortcuts::instance()) {
|
||||
if (key->type() == KeyType::Tool ||
|
||||
key->type() == KeyType::Quicktool) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string text = key->triggerString();
|
||||
switch (key->keycontext()) {
|
||||
case KeyContext::SelectionTool:
|
||||
@ -437,16 +446,11 @@ private:
|
||||
}
|
||||
KeyItem* keyItem = new KeyItem(text, key, NULL, 0);
|
||||
|
||||
ListBox* listBox = NULL;
|
||||
ListBox* listBox = nullptr;
|
||||
switch (key->type()) {
|
||||
case KeyType::Command:
|
||||
listBox = this->commands();
|
||||
break;
|
||||
case KeyType::Tool:
|
||||
case KeyType::Quicktool: {
|
||||
listBox = this->tools();
|
||||
break;
|
||||
}
|
||||
case KeyType::Action:
|
||||
listBox = this->actions();
|
||||
break;
|
||||
@ -608,6 +612,23 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void fillList(ListBox* listbox, ToolBox* toolbox) {
|
||||
for (Tool* tool : *toolbox) {
|
||||
std::string text = tool->getText();
|
||||
|
||||
Key* key = app::KeyboardShortcuts::instance()->tool(tool);
|
||||
KeyItem* keyItem = new KeyItem(text, key, nullptr, 0);
|
||||
m_allKeyItems.push_back(keyItem);
|
||||
listbox->addChild(keyItem);
|
||||
|
||||
text += " (quick)";
|
||||
key = app::KeyboardShortcuts::instance()->quicktool(tool);
|
||||
keyItem = new KeyItem(text, key, nullptr, 0);
|
||||
m_allKeyItems.push_back(keyItem);
|
||||
listbox->addChild(keyItem);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<KeyItem*> m_allKeyItems;
|
||||
bool m_searchChange;
|
||||
};
|
||||
|
110
src/app/commands/cmd_layer_opacity.cpp
Normal file
110
src/app/commands/cmd_layer_opacity.cpp
Normal file
@ -0,0 +1,110 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 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/app.h"
|
||||
#include "app/cmd/set_layer_opacity.h"
|
||||
#include "app/commands/command.h"
|
||||
#include "app/commands/params.h"
|
||||
#include "app/context.h"
|
||||
#include "app/context_access.h"
|
||||
#include "app/modules/gui.h"
|
||||
#include "app/transaction.h"
|
||||
#include "app/ui/timeline.h"
|
||||
#include "base/convert_to.h"
|
||||
#include "doc/layer.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace app {
|
||||
|
||||
class LayerOpacityCommand : public Command {
|
||||
public:
|
||||
LayerOpacityCommand();
|
||||
|
||||
protected:
|
||||
void onLoadParams(const Params& params) override;
|
||||
bool onEnabled(Context* context) override;
|
||||
void onExecute(Context* context) override;
|
||||
std::string onGetFriendlyName() const override;
|
||||
|
||||
private:
|
||||
int m_opacity;
|
||||
};
|
||||
|
||||
LayerOpacityCommand::LayerOpacityCommand()
|
||||
: Command("LayerOpacity",
|
||||
"Layer Opacity",
|
||||
CmdUIOnlyFlag)
|
||||
{
|
||||
m_opacity = 255;
|
||||
}
|
||||
|
||||
void LayerOpacityCommand::onLoadParams(const Params& params)
|
||||
{
|
||||
m_opacity = params.get_as<int>("opacity");
|
||||
m_opacity = MID(0, m_opacity, 255);
|
||||
}
|
||||
|
||||
bool LayerOpacityCommand::onEnabled(Context* context)
|
||||
{
|
||||
return context->checkFlags(ContextFlags::ActiveDocumentIsWritable |
|
||||
ContextFlags::HasActiveLayer);
|
||||
}
|
||||
|
||||
void LayerOpacityCommand::onExecute(Context* context)
|
||||
{
|
||||
ContextWriter writer(context);
|
||||
Layer* layer = writer.layer();
|
||||
if (!layer ||
|
||||
!layer->isImage() ||
|
||||
static_cast<LayerImage*>(layer)->opacity() == m_opacity)
|
||||
return;
|
||||
|
||||
{
|
||||
Transaction transaction(writer.context(), "Set Layer Opacity");
|
||||
|
||||
// TODO the range of selected frames should be in doc::Site.
|
||||
SelectedLayers selLayers;
|
||||
auto range = App::instance()->timeline()->range();
|
||||
if (range.enabled()) {
|
||||
selLayers = range.selectedLayers();
|
||||
}
|
||||
else {
|
||||
selLayers.insert(writer.layer());
|
||||
}
|
||||
|
||||
for (auto layer : selLayers) {
|
||||
if (layer->isImage())
|
||||
transaction.execute(
|
||||
new cmd::SetLayerOpacity(static_cast<LayerImage*>(layer), m_opacity));
|
||||
}
|
||||
|
||||
transaction.commit();
|
||||
}
|
||||
|
||||
update_screen_for_document(writer.document());
|
||||
}
|
||||
|
||||
std::string LayerOpacityCommand::onGetFriendlyName() const
|
||||
{
|
||||
std::string text = "Set Layer Opacity to ";
|
||||
text += base::convert_to<std::string>(m_opacity);
|
||||
text += " (";
|
||||
text += base::convert_to<std::string>(int(100.0 * m_opacity / 255.0));
|
||||
text += "%)";
|
||||
return text;
|
||||
}
|
||||
|
||||
Command* CommandFactory::createLayerOpacityCommand()
|
||||
{
|
||||
return new LayerOpacityCommand;
|
||||
}
|
||||
|
||||
} // namespace app
|
@ -187,10 +187,20 @@ public:
|
||||
}
|
||||
|
||||
// Right-click
|
||||
|
||||
static_assert(int(app::gen::RightClickMode::PAINT_BGCOLOR) == 0, "");
|
||||
static_assert(int(app::gen::RightClickMode::PICK_FGCOLOR) == 1, "");
|
||||
static_assert(int(app::gen::RightClickMode::ERASE) == 2, "");
|
||||
static_assert(int(app::gen::RightClickMode::SCROLL) == 3, "");
|
||||
static_assert(int(app::gen::RightClickMode::RECTANGULAR_MARQUEE) == 4, "");
|
||||
static_assert(int(app::gen::RightClickMode::LASSO) == 5, "");
|
||||
|
||||
rightClickBehavior()->addItem("Paint with background color");
|
||||
rightClickBehavior()->addItem("Pick foreground color");
|
||||
rightClickBehavior()->addItem("Erase");
|
||||
rightClickBehavior()->addItem("Scroll");
|
||||
rightClickBehavior()->addItem("Rectangular Marquee");
|
||||
rightClickBehavior()->addItem("Lasso");
|
||||
rightClickBehavior()->setSelectedItemIndex((int)m_pref.editor.rightClickMode());
|
||||
|
||||
// Zoom with Scroll Wheel
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -51,10 +51,9 @@ void PaletteSizeCommand::onLoadParams(const Params& params)
|
||||
|
||||
void PaletteSizeCommand::onExecute(Context* context)
|
||||
{
|
||||
ContextWriter writer(context);
|
||||
Sprite* sprite = writer.sprite();
|
||||
frame_t frame = writer.frame();
|
||||
Palette palette(*sprite->palette(frame));
|
||||
ContextReader reader(context);
|
||||
frame_t frame = reader.frame();
|
||||
Palette palette(*reader.sprite()->palette(frame));
|
||||
|
||||
app::gen::PaletteSize window;
|
||||
window.colors()->setTextf("%d", palette.size());
|
||||
@ -66,8 +65,9 @@ void PaletteSizeCommand::onExecute(Context* context)
|
||||
|
||||
palette.resize(MID(1, ncolors, INT_MAX));
|
||||
|
||||
ContextWriter writer(reader);
|
||||
Transaction transaction(context, "Palette Size", ModifyDocument);
|
||||
transaction.execute(new cmd::SetPalette(sprite, frame, &palette));
|
||||
transaction.execute(new cmd::SetPalette(writer.sprite(), frame, &palette));
|
||||
transaction.commit();
|
||||
|
||||
set_current_palette(&palette, false);
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -59,7 +59,8 @@ void PlayAnimationCommand::onExecute(Context* context)
|
||||
if (current_editor->isPlaying())
|
||||
current_editor->stop();
|
||||
else
|
||||
current_editor->play(Preferences::instance().editor.playOnce());
|
||||
current_editor->play(Preferences::instance().editor.playOnce(),
|
||||
Preferences::instance().editor.playAll());
|
||||
}
|
||||
|
||||
Command* CommandFactory::createPlayAnimationCommand()
|
||||
|
@ -61,6 +61,7 @@ FOR_EACH_COMMAND(InvertMask)
|
||||
FOR_EACH_COMMAND(KeyboardShortcuts)
|
||||
FOR_EACH_COMMAND(Launch)
|
||||
FOR_EACH_COMMAND(LayerFromBackground)
|
||||
FOR_EACH_COMMAND(LayerOpacity)
|
||||
FOR_EACH_COMMAND(LayerProperties)
|
||||
FOR_EACH_COMMAND(LayerVisibility)
|
||||
FOR_EACH_COMMAND(LinkCels)
|
||||
|
@ -16,6 +16,8 @@
|
||||
#include "base/fs.h"
|
||||
#include "base/time.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace app {
|
||||
namespace crash {
|
||||
|
||||
@ -53,6 +55,12 @@ DataRecovery::DataRecovery(doc::Context* ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// Sort sessions from the most recent one to the oldest one
|
||||
std::sort(m_sessions.begin(), m_sessions.end(),
|
||||
[](const SessionPtr& a, const SessionPtr& b) {
|
||||
return a->name() > b->name();
|
||||
});
|
||||
|
||||
// Create a new session
|
||||
base::pid pid = base::get_current_process_id();
|
||||
std::string newSessionDir;
|
||||
|
@ -23,7 +23,7 @@ using namespace base;
|
||||
|
||||
class PcxFormat : public FileFormat {
|
||||
const char* onGetName() const override { return "pcx"; }
|
||||
const char* onGetExtensions() const override { return "pcx"; }
|
||||
const char* onGetExtensions() const override { return "pcx,pcc"; }
|
||||
docio::FileFormat onGetDocioFormat() const override { return docio::FileFormat::PCX_IMAGE; }
|
||||
int onGetFlags() const override {
|
||||
return
|
||||
|
@ -177,6 +177,14 @@ void ActiveToolManager::pressButton(const Pointer& pointer)
|
||||
tool = m_toolbox->getToolById(WellKnownTools::Hand);
|
||||
ink = m_toolbox->getInkById(tools::WellKnownInks::Scroll);
|
||||
break;
|
||||
case app::gen::RightClickMode::RECTANGULAR_MARQUEE:
|
||||
tool = m_toolbox->getToolById(WellKnownTools::RectangularMarquee);
|
||||
ink = m_toolbox->getInkById(tools::WellKnownInks::Selection);
|
||||
break;
|
||||
case app::gen::RightClickMode::LASSO:
|
||||
tool = m_toolbox->getToolById(WellKnownTools::Lasso);
|
||||
ink = m_toolbox->getInkById(tools::WellKnownInks::Selection);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -212,7 +220,8 @@ bool ActiveToolManager::isToolAffectedByRightClickMode(Tool* tool)
|
||||
return
|
||||
((tool->getInk(0)->isPaint() && !shadingMode) ||
|
||||
(tool->getInk(0)->isEffect())) &&
|
||||
(!tool->getInk(0)->isEraser());
|
||||
(!tool->getInk(0)->isEraser()) &&
|
||||
(!tool->getInk(0)->isSelection());
|
||||
}
|
||||
|
||||
} // namespace tools
|
||||
|
@ -42,6 +42,7 @@ namespace tools {
|
||||
using namespace gfx;
|
||||
|
||||
const char* WellKnownTools::RectangularMarquee = "rectangular_marquee";
|
||||
const char* WellKnownTools::Lasso = "lasso";
|
||||
const char* WellKnownTools::Pencil = "pencil";
|
||||
const char* WellKnownTools::Eraser = "eraser";
|
||||
const char* WellKnownTools::Eyedropper = "eyedropper";
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -22,6 +22,7 @@ namespace app {
|
||||
|
||||
namespace WellKnownTools {
|
||||
extern const char* RectangularMarquee;
|
||||
extern const char* Lasso;
|
||||
extern const char* Pencil;
|
||||
extern const char* Eraser;
|
||||
extern const char* Eyedropper;
|
||||
|
@ -112,7 +112,8 @@ void AniControls::onRightClick(Item* item)
|
||||
|
||||
if (item == getItem(ACTION_PLAY) && current_editor)
|
||||
current_editor->showAnimationSpeedMultiplierPopup(
|
||||
Preferences::instance().editor.playOnce, true);
|
||||
Preferences::instance().editor.playOnce,
|
||||
Preferences::instance().editor.playAll, true);
|
||||
}
|
||||
|
||||
const char* AniControls::getCommandId(int index) const
|
||||
|
@ -48,7 +48,9 @@ enum {
|
||||
};
|
||||
|
||||
ColorPopup::ColorPopup(bool canPin)
|
||||
: PopupWindowPin("Color Selector", ClickBehavior::CloseOnClickInOtherWindow)
|
||||
: PopupWindowPin("Color Selector",
|
||||
ClickBehavior::CloseOnClickInOtherWindow,
|
||||
canPin)
|
||||
, m_vbox(VERTICAL)
|
||||
, m_topBox(HORIZONTAL)
|
||||
, m_color(app::Color::fromMask())
|
||||
@ -77,12 +79,22 @@ ColorPopup::ColorPopup(bool canPin)
|
||||
m_topBox.addChild(&m_colorType);
|
||||
m_topBox.addChild(new Separator("", VERTICAL));
|
||||
m_topBox.addChild(&m_hexColorEntry);
|
||||
|
||||
// Move close button (decorative widget) inside the m_topBox
|
||||
{
|
||||
Box* miniVbox = new Box(VERTICAL);
|
||||
miniVbox->addChild(getPin());
|
||||
Widget* closeButton = nullptr;
|
||||
WidgetsList decorators;
|
||||
for (auto child : children()) {
|
||||
if (child->isDecorative()) {
|
||||
closeButton = child;
|
||||
removeChild(child);
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_topBox.addChild(new BoxFiller);
|
||||
m_topBox.addChild(miniVbox);
|
||||
m_topBox.addChild(closeButton);
|
||||
}
|
||||
|
||||
m_vbox.addChild(&m_topBox);
|
||||
m_vbox.addChild(&m_colorPaletteContainer);
|
||||
m_vbox.addChild(&m_rgbSliders);
|
||||
@ -98,9 +110,6 @@ ColorPopup::ColorPopup(bool canPin)
|
||||
m_graySlider.ColorChange.connect(&ColorPopup::onColorSlidersChange, this);
|
||||
m_hexColorEntry.ColorChange.connect(&ColorPopup::onColorHexEntryChange, this);
|
||||
|
||||
if (!m_canPin)
|
||||
showPin(false);
|
||||
|
||||
selectColorType(app::Color::RgbType);
|
||||
setSizeHint(gfx::Size(300*guiscale(), sizeHint().h));
|
||||
|
||||
@ -112,7 +121,6 @@ ColorPopup::ColorPopup(bool canPin)
|
||||
|
||||
ColorPopup::~ColorPopup()
|
||||
{
|
||||
getPin()->parent()->removeChild(getPin());
|
||||
}
|
||||
|
||||
void ColorPopup::setColor(const app::Color& color, SetColorOptions options)
|
||||
|
@ -1195,6 +1195,7 @@ public:
|
||||
}
|
||||
|
||||
void setupTooltips(TooltipManager* tooltipManager) {
|
||||
// TODO use real shortcuts in tooltips
|
||||
tooltipManager->addTooltipFor(at(0), "Replace selection", BOTTOM);
|
||||
tooltipManager->addTooltipFor(at(1), "Add to selection\n(Shift)", BOTTOM);
|
||||
tooltipManager->addTooltipFor(at(2), "Subtract from selection\n(Shift+Alt)", BOTTOM);
|
||||
@ -1547,7 +1548,8 @@ void ContextBar::updateForTool(tools::Tool* tool)
|
||||
// target to implement this new IContextBarUser and ask for
|
||||
// ContextBar elements.
|
||||
|
||||
base::ScopedValue<bool> lockFlag(g_updatingFromCode, true, g_updatingFromCode);
|
||||
const bool oldUpdatingFromCode = g_updatingFromCode;
|
||||
base::ScopedValue<bool> lockFlag(g_updatingFromCode, true, oldUpdatingFromCode);
|
||||
|
||||
ToolPreferences* toolPref = nullptr;
|
||||
ToolPreferences::Brush* brushPref = nullptr;
|
||||
@ -1569,8 +1571,10 @@ void ContextBar::updateForTool(tools::Tool* tool)
|
||||
m_brushType->updateBrush(tool);
|
||||
|
||||
if (brushPref) {
|
||||
m_brushSize->setTextf("%d", brushPref->size());
|
||||
m_brushAngle->setTextf("%d", brushPref->angle());
|
||||
if (!oldUpdatingFromCode) {
|
||||
m_brushSize->setTextf("%d", brushPref->size());
|
||||
m_brushAngle->setTextf("%d", brushPref->angle());
|
||||
}
|
||||
}
|
||||
|
||||
m_brushPatternField->setBrushPattern(
|
||||
|
@ -25,10 +25,13 @@
|
||||
#include "ui/listitem.h"
|
||||
#include "ui/message.h"
|
||||
#include "ui/resize_event.h"
|
||||
#include "ui/separator.h"
|
||||
#include "ui/size_hint_event.h"
|
||||
#include "ui/system.h"
|
||||
#include "ui/view.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace app {
|
||||
|
||||
using namespace ui;
|
||||
@ -39,137 +42,61 @@ namespace {
|
||||
class Item : public ListItem {
|
||||
public:
|
||||
Item(crash::Session* session, crash::Session::Backup* backup)
|
||||
: ListItem(backup ? " > " + backup->description(): session->name())
|
||||
: ListItem(backup->description())
|
||||
, m_session(session)
|
||||
, m_backup(backup)
|
||||
, m_openButton(backup ? "Open": "Open All")
|
||||
, m_deleteButton(backup ? "Delete": "Delete All")
|
||||
{
|
||||
m_hbox.setBgColor(gfx::ColorNone);
|
||||
m_hbox.setTransparent(true);
|
||||
m_hbox.addChild(&m_openButton);
|
||||
m_hbox.addChild(&m_deleteButton);
|
||||
addChild(&m_hbox);
|
||||
|
||||
m_openButton.Click.connect(base::Bind(&Item::onOpen, this));
|
||||
m_openButton.DropDownClick.connect(base::Bind<void>(&Item::onOpenMenu, this));
|
||||
m_deleteButton.Click.connect(base::Bind(&Item::onDelete, this));
|
||||
|
||||
setup_mini_look(&m_openButton);
|
||||
setup_mini_look(&m_deleteButton);
|
||||
}
|
||||
|
||||
obs::signal<void()> Regenerate;
|
||||
crash::Session* session() const { return m_session; }
|
||||
crash::Session::Backup* backup() const { return m_backup; }
|
||||
|
||||
protected:
|
||||
private:
|
||||
void onSizeHint(SizeHintEvent& ev) override {
|
||||
gfx::Size sz = m_deleteButton.sizeHint();
|
||||
ListItem::onSizeHint(ev);
|
||||
gfx::Size sz = ev.sizeHint();
|
||||
sz.h += 4*guiscale();
|
||||
ev.setSizeHint(sz);
|
||||
}
|
||||
|
||||
void onResize(ResizeEvent& ev) override {
|
||||
ListItem::onResize(ev);
|
||||
|
||||
gfx::Rect rc = ev.bounds();
|
||||
gfx::Size sz = m_hbox.sizeHint();
|
||||
m_hbox.setBounds(
|
||||
gfx::Rect(
|
||||
rc.x+rc.w-sz.w-2*guiscale(), rc.y+rc.h/2-sz.h/2, sz.w, sz.h));
|
||||
}
|
||||
|
||||
void onOpen() {
|
||||
if (m_backup)
|
||||
m_session->restoreBackup(m_backup);
|
||||
else
|
||||
for (auto backup : m_session->backups())
|
||||
m_session->restoreBackup(backup);
|
||||
}
|
||||
|
||||
void onOpenRaw(crash::RawImagesAs as) {
|
||||
if (m_backup)
|
||||
m_session->restoreRawImages(m_backup, as);
|
||||
else
|
||||
for (auto backup : m_session->backups())
|
||||
m_session->restoreRawImages(backup, as);
|
||||
}
|
||||
|
||||
void onOpenMenu() {
|
||||
gfx::Rect bounds = m_openButton.bounds();
|
||||
|
||||
Menu menu;
|
||||
MenuItem rawFrames("Raw Images as Frames");
|
||||
MenuItem rawLayers("Raw Images as Layers");
|
||||
menu.addChild(&rawFrames);
|
||||
menu.addChild(&rawLayers);
|
||||
|
||||
rawFrames.Click.connect(base::Bind(&Item::onOpenRaw, this, crash::RawImagesAs::kFrames));
|
||||
rawLayers.Click.connect(base::Bind(&Item::onOpenRaw, this, crash::RawImagesAs::kLayers));
|
||||
|
||||
menu.showPopup(gfx::Point(bounds.x, bounds.y+bounds.h));
|
||||
}
|
||||
|
||||
void onDelete() {
|
||||
Widget* parent = this->parent();
|
||||
|
||||
if (m_backup) {
|
||||
// Delete one backup
|
||||
if (Alert::show(PACKAGE
|
||||
"<<Do you really want to delete this backup?"
|
||||
"||&Yes||&No") != 1)
|
||||
return;
|
||||
|
||||
m_session->deleteBackup(m_backup);
|
||||
|
||||
Widget* parent = this->parent(); // TODO remove this line
|
||||
parent->removeChild(this);
|
||||
deferDelete();
|
||||
}
|
||||
else {
|
||||
// Delete the whole session
|
||||
if (!m_session->isEmpty()) {
|
||||
if (Alert::show(PACKAGE
|
||||
"<<Do you want to delete the whole session?"
|
||||
"<<You will lost all backups related to this session."
|
||||
"||&Yes||&No") != 1)
|
||||
return;
|
||||
}
|
||||
|
||||
crash::Session::Backups backups = m_session->backups();
|
||||
for (auto backup : backups)
|
||||
m_session->deleteBackup(backup);
|
||||
|
||||
m_session->removeFromDisk();
|
||||
Regenerate();
|
||||
}
|
||||
|
||||
parent->layout();
|
||||
View::getView(parent)->updateView();
|
||||
}
|
||||
|
||||
private:
|
||||
crash::Session* m_session;
|
||||
crash::Session::Backup* m_backup;
|
||||
ui::HBox m_hbox;
|
||||
DropDownButton m_openButton;
|
||||
ui::Button m_deleteButton;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
DataRecoveryView::DataRecoveryView(crash::DataRecovery* dataRecovery)
|
||||
: Box(VERTICAL)
|
||||
, m_dataRecovery(dataRecovery)
|
||||
: m_dataRecovery(dataRecovery)
|
||||
, m_openButton("Recover Sprite")
|
||||
, m_deleteButton("Delete")
|
||||
{
|
||||
SkinTheme* theme = static_cast<SkinTheme*>(this->theme());
|
||||
setBgColor(theme->colors.workspace());
|
||||
|
||||
addChild(&m_view);
|
||||
m_openButton.mainButton()->setSizeHint(
|
||||
gfx::Size(std::max(m_openButton.mainButton()->sizeHint().w, 100*guiscale()),
|
||||
m_openButton.mainButton()->sizeHint().h));
|
||||
|
||||
m_listBox.setMultiselect(true);
|
||||
m_view.setExpansive(true);
|
||||
m_view.attachToView(&m_listBox);
|
||||
m_view.setProperty(SkinStylePropertyPtr(new SkinStyleProperty(theme->styles.workspaceView())));
|
||||
|
||||
HBox* hbox = new HBox;
|
||||
hbox->setBorder(gfx::Border(2, 0, 2, 0)*guiscale());
|
||||
hbox->addChild(&m_openButton);
|
||||
hbox->addChild(&m_deleteButton);
|
||||
addChild(hbox);
|
||||
addChild(&m_view);
|
||||
|
||||
fillList();
|
||||
onChangeSelection();
|
||||
|
||||
m_openButton.Click.connect(base::Bind(&DataRecoveryView::onOpen, this));
|
||||
m_openButton.DropDownClick.connect(base::Bind<void>(&DataRecoveryView::onOpenMenu, this));
|
||||
m_deleteButton.Click.connect(base::Bind(&DataRecoveryView::onDelete, this));
|
||||
m_listBox.Change.connect(base::Bind(&DataRecoveryView::onChangeSelection, this));
|
||||
m_listBox.DoubleClickItem.connect(base::Bind(&DataRecoveryView::onOpen, this));
|
||||
}
|
||||
|
||||
DataRecoveryView::~DataRecoveryView()
|
||||
@ -188,13 +115,13 @@ void DataRecoveryView::fillList()
|
||||
if (session->isEmpty())
|
||||
continue;
|
||||
|
||||
Item* item = new Item(session.get(), nullptr);
|
||||
item->Regenerate.connect(&DataRecoveryView::fillList, this);
|
||||
m_listBox.addChild(item);
|
||||
auto sep = new Separator(session->name(), HORIZONTAL);
|
||||
sep->setBgColor(SkinTheme::instance()->colors.background());
|
||||
sep->setBorder(sep->border() + gfx::Border(0, 8, 0, 8)*guiscale());
|
||||
m_listBox.addChild(sep);
|
||||
|
||||
for (auto& backup : session->backups()) {
|
||||
item = new Item(session.get(), backup);
|
||||
item->Regenerate.connect(&DataRecoveryView::fillList, this);
|
||||
auto item = new Item(session.get(), backup);
|
||||
m_listBox.addChild(item);
|
||||
}
|
||||
}
|
||||
@ -232,4 +159,101 @@ void DataRecoveryView::onTabPopup(Workspace* workspace)
|
||||
menu->showPopup(ui::get_mouse_position());
|
||||
}
|
||||
|
||||
void DataRecoveryView::onOpen()
|
||||
{
|
||||
for (auto widget : m_listBox.children()) {
|
||||
if (!widget->isSelected())
|
||||
continue;
|
||||
|
||||
if (auto item = dynamic_cast<Item*>(widget)) {
|
||||
if (item->backup())
|
||||
item->session()->restoreBackup(item->backup());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DataRecoveryView::onOpenRaw(crash::RawImagesAs as)
|
||||
{
|
||||
for (auto widget : m_listBox.children()) {
|
||||
if (!widget->isSelected())
|
||||
continue;
|
||||
|
||||
if (auto item = dynamic_cast<Item*>(widget)) {
|
||||
if (item->backup())
|
||||
item->session()->restoreRawImages(item->backup(), as);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DataRecoveryView::onOpenMenu()
|
||||
{
|
||||
gfx::Rect bounds = m_openButton.bounds();
|
||||
|
||||
Menu menu;
|
||||
MenuItem rawFrames("Raw Images as Frames");
|
||||
MenuItem rawLayers("Raw Images as Layers");
|
||||
menu.addChild(&rawFrames);
|
||||
menu.addChild(&rawLayers);
|
||||
|
||||
rawFrames.Click.connect(base::Bind(&DataRecoveryView::onOpenRaw, this, crash::RawImagesAs::kFrames));
|
||||
rawLayers.Click.connect(base::Bind(&DataRecoveryView::onOpenRaw, this, crash::RawImagesAs::kLayers));
|
||||
|
||||
menu.showPopup(gfx::Point(bounds.x, bounds.y+bounds.h));
|
||||
}
|
||||
|
||||
void DataRecoveryView::onDelete()
|
||||
{
|
||||
std::vector<Item*> items;
|
||||
|
||||
for (auto widget : m_listBox.children()) {
|
||||
if (!widget->isSelected())
|
||||
continue;
|
||||
|
||||
if (auto item = dynamic_cast<Item*>(widget)) {
|
||||
if (item->backup())
|
||||
items.push_back(item);
|
||||
}
|
||||
}
|
||||
|
||||
if (items.empty())
|
||||
return;
|
||||
|
||||
// Delete one backup
|
||||
if (Alert::show(PACKAGE
|
||||
"<<Do you really want to delete the selected %d backup(s)?"
|
||||
"||&Yes||&No",
|
||||
int(items.size())) != 1)
|
||||
return; // Cancel
|
||||
|
||||
for (auto item : items) {
|
||||
item->session()->deleteBackup(item->backup());
|
||||
m_listBox.removeChild(item);
|
||||
delete item;
|
||||
}
|
||||
onChangeSelection();
|
||||
|
||||
m_listBox.layout();
|
||||
m_view.updateView();
|
||||
}
|
||||
|
||||
void DataRecoveryView::onChangeSelection()
|
||||
{
|
||||
int count = 0;
|
||||
for (auto widget : m_listBox.children()) {
|
||||
if (!widget->isSelected())
|
||||
continue;
|
||||
|
||||
if (dynamic_cast<Item*>(widget)) {
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
m_deleteButton.setEnabled(count > 0);
|
||||
m_openButton.setEnabled(count > 0);
|
||||
if (count < 2)
|
||||
m_openButton.mainButton()->setText("Recover Sprite");
|
||||
else
|
||||
m_openButton.mainButton()->setTextf("Recover %d Sprites", count);
|
||||
}
|
||||
|
||||
} // namespace app
|
||||
|
@ -8,9 +8,12 @@
|
||||
#define APP_UI_DATA_RECOVERY_VIEW_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "app/crash/raw_images_as.h"
|
||||
#include "app/ui/drop_down_button.h"
|
||||
#include "app/ui/tabs.h"
|
||||
#include "app/ui/workspace_view.h"
|
||||
#include "ui/box.h"
|
||||
#include "ui/button.h"
|
||||
#include "ui/listbox.h"
|
||||
#include "ui/view.h"
|
||||
|
||||
@ -19,7 +22,7 @@ namespace app {
|
||||
class DataRecovery;
|
||||
}
|
||||
|
||||
class DataRecoveryView : public ui::Box
|
||||
class DataRecoveryView : public ui::VBox
|
||||
, public TabView
|
||||
, public WorkspaceView {
|
||||
public:
|
||||
@ -43,9 +46,17 @@ namespace app {
|
||||
private:
|
||||
void fillList();
|
||||
|
||||
void onOpen();
|
||||
void onOpenRaw(crash::RawImagesAs as);
|
||||
void onOpenMenu();
|
||||
void onDelete();
|
||||
void onChangeSelection();
|
||||
|
||||
crash::DataRecovery* m_dataRecovery;
|
||||
ui::View m_view;
|
||||
ui::ListBox m_listBox;
|
||||
DropDownButton m_openButton;
|
||||
ui::Button m_deleteButton;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
@ -273,6 +273,17 @@ void Editor::setStateInternal(const EditorStatePtr& newState)
|
||||
else {
|
||||
m_state->onBeforePopState(this);
|
||||
|
||||
// Save the current state into "m_deletedStates" just to keep a
|
||||
// reference to it to avoid delete it right now. We'll delete it
|
||||
// in the next Editor::onProcessMessage().
|
||||
//
|
||||
// This is necessary for PlayState because it removes itself
|
||||
// calling Editor::stop() from PlayState::onPlaybackTick(). If we
|
||||
// delete the PlayState inside the "Tick" timer signal, the
|
||||
// program will crash (because we're iterating the
|
||||
// PlayState::m_playTimer slots).
|
||||
m_deletedStates.push(m_state);
|
||||
|
||||
m_statesHistory.pop();
|
||||
m_state = m_statesHistory.top();
|
||||
}
|
||||
@ -1221,10 +1232,17 @@ void Editor::updateToolLoopModifiersIndicators()
|
||||
action = m_customizationDelegate->getPressedKeyAction(KeyContext::SelectionTool);
|
||||
|
||||
gen::SelectionMode mode = Preferences::instance().selection.mode();
|
||||
if (int(action & KeyAction::AddSelection))
|
||||
mode = gen::SelectionMode::ADD;
|
||||
if (int(action & KeyAction::SubtractSelection) || m_secondaryButton)
|
||||
if (int(action & KeyAction::SubtractSelection) ||
|
||||
// Don't use "subtract" mode if the selection was activated
|
||||
// with the "right click mode = a selection-like tool"
|
||||
(m_secondaryButton &&
|
||||
App::instance()->activeToolManager()->selectedTool() &&
|
||||
App::instance()->activeToolManager()->selectedTool()->getInk(0)->isSelection())) {
|
||||
mode = gen::SelectionMode::SUBTRACT;
|
||||
}
|
||||
else if (int(action & KeyAction::AddSelection)) {
|
||||
mode = gen::SelectionMode::ADD;
|
||||
}
|
||||
switch (mode) {
|
||||
case gen::SelectionMode::DEFAULT: modifiers |= int(tools::ToolLoopModifiers::kReplaceSelection); break;
|
||||
case gen::SelectionMode::ADD: modifiers |= int(tools::ToolLoopModifiers::kAddSelection); break;
|
||||
@ -1277,6 +1295,10 @@ app::Color Editor::getColorByPosition(const gfx::Point& mousePos)
|
||||
|
||||
bool Editor::onProcessMessage(Message* msg)
|
||||
{
|
||||
// Delete states
|
||||
if (!m_deletedStates.empty())
|
||||
m_deletedStates.clear();
|
||||
|
||||
switch (msg->type()) {
|
||||
|
||||
case kTimerMessage:
|
||||
@ -1313,14 +1335,17 @@ bool Editor::onProcessMessage(Message* msg)
|
||||
m_oldPos = mouseMsg->position();
|
||||
updateToolByTipProximity(mouseMsg->pointerType());
|
||||
|
||||
if (!m_secondaryButton && mouseMsg->right()) {
|
||||
m_secondaryButton = mouseMsg->right();
|
||||
|
||||
updateToolLoopModifiersIndicators();
|
||||
updateQuicktool();
|
||||
setCursor(mouseMsg->position());
|
||||
// Only when we right-click with the regular "paint bg-color
|
||||
// right-click mode" we will mark indicate that the secondary
|
||||
// button was used (m_secondaryButton == true).
|
||||
if (mouseMsg->right() && !m_secondaryButton) {
|
||||
m_secondaryButton = true;
|
||||
}
|
||||
|
||||
updateToolLoopModifiersIndicators();
|
||||
updateQuicktool();
|
||||
setCursor(mouseMsg->position());
|
||||
|
||||
App::instance()->activeToolManager()
|
||||
->pressButton(pointer_from_msg(this, mouseMsg));
|
||||
|
||||
@ -1740,14 +1765,15 @@ void Editor::notifyZoomChanged()
|
||||
m_observers.notifyZoomChanged(this);
|
||||
}
|
||||
|
||||
void Editor::play(bool playOnce)
|
||||
void Editor::play(const bool playOnce,
|
||||
const bool playAll)
|
||||
{
|
||||
ASSERT(m_state);
|
||||
if (!m_state)
|
||||
return;
|
||||
|
||||
if (!dynamic_cast<PlayState*>(m_state.get()))
|
||||
setState(EditorStatePtr(new PlayState(playOnce)));
|
||||
setState(EditorStatePtr(new PlayState(playOnce, playAll)));
|
||||
}
|
||||
|
||||
void Editor::stop()
|
||||
@ -1766,9 +1792,10 @@ bool Editor::isPlaying() const
|
||||
}
|
||||
|
||||
void Editor::showAnimationSpeedMultiplierPopup(Option<bool>& playOnce,
|
||||
bool withStopBehaviorOptions)
|
||||
Option<bool>& playAll,
|
||||
const bool withStopBehaviorOptions)
|
||||
{
|
||||
double options[] = { 0.25, 0.5, 1.0, 1.5, 2.0, 3.0 };
|
||||
const double options[] = { 0.25, 0.5, 1.0, 1.5, 2.0, 3.0 };
|
||||
Menu menu;
|
||||
|
||||
for (double option : options) {
|
||||
@ -1791,6 +1818,17 @@ void Editor::showAnimationSpeedMultiplierPopup(Option<bool>& playOnce,
|
||||
menu.addChild(item);
|
||||
}
|
||||
|
||||
// Play all option
|
||||
{
|
||||
MenuItem* item = new MenuItem("Play All Frames (Ignore Tags)");
|
||||
item->Click.connect(
|
||||
[&playAll]() {
|
||||
playAll(!playAll());
|
||||
});
|
||||
item->setSelected(playAll());
|
||||
menu.addChild(item);
|
||||
}
|
||||
|
||||
if (withStopBehaviorOptions) {
|
||||
MenuItem* item = new MenuItem("Rewind on Stop");
|
||||
item->Click.connect(
|
||||
@ -1804,6 +1842,13 @@ void Editor::showAnimationSpeedMultiplierPopup(Option<bool>& playOnce,
|
||||
}
|
||||
|
||||
menu.showPopup(ui::get_mouse_position());
|
||||
|
||||
if (isPlaying()) {
|
||||
// Re-play
|
||||
stop();
|
||||
play(playOnce(),
|
||||
playAll());
|
||||
}
|
||||
}
|
||||
|
||||
double Editor::getAnimationSpeedMultiplier() const
|
||||
|
@ -206,13 +206,15 @@ namespace app {
|
||||
void notifyZoomChanged();
|
||||
|
||||
// Animation control
|
||||
void play(bool playOnce);
|
||||
void play(const bool playOnce,
|
||||
const bool playAll);
|
||||
void stop();
|
||||
bool isPlaying() const;
|
||||
|
||||
// Shows a popup menu to change the editor animation speed.
|
||||
void showAnimationSpeedMultiplierPopup(Option<bool>& playOnce,
|
||||
bool withStopBehaviorOptions);
|
||||
Option<bool>& playAll,
|
||||
const bool withStopBehaviorOptions);
|
||||
double getAnimationSpeedMultiplier() const;
|
||||
void setAnimationSpeedMultiplier(double speed);
|
||||
|
||||
@ -273,6 +275,7 @@ namespace app {
|
||||
|
||||
// Stack of states. The top element in the stack is the current state (m_state).
|
||||
EditorStatesHistory m_statesHistory;
|
||||
EditorStatesHistory m_deletedStates;
|
||||
|
||||
// Current editor state (it can be shared between several editors to
|
||||
// the same document). This member cannot be NULL.
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -21,6 +21,8 @@ namespace app {
|
||||
EditorStatesHistory();
|
||||
~EditorStatesHistory();
|
||||
|
||||
bool empty() const { return m_states.empty(); }
|
||||
|
||||
// Gets the current state.
|
||||
EditorStatePtr top();
|
||||
|
||||
|
@ -525,8 +525,15 @@ void MovingPixelsState::onBeforeCommandExecution(CommandExecutionEvent& ev)
|
||||
if (command->id() != CommandId::Copy) {
|
||||
m_pixelsMovement->trim();
|
||||
|
||||
// Should we keep the mask after an Edit > Clear command?
|
||||
auto keepMask = PixelsMovement::DontKeepMask;
|
||||
if (command->id() == CommandId::Clear &&
|
||||
Preferences::instance().selection.keepSelectionAfterClear()) {
|
||||
keepMask = PixelsMovement::KeepMask;
|
||||
}
|
||||
|
||||
// Discard the dragged image.
|
||||
m_pixelsMovement->discardImage();
|
||||
m_pixelsMovement->discardImage(PixelsMovement::CommitChanges, keepMask);
|
||||
m_discarded = true;
|
||||
|
||||
// Quit from MovingPixelsState, back to standby.
|
||||
@ -610,7 +617,7 @@ void MovingPixelsState::onDropPixels(ContextBarObserver::DropAction action)
|
||||
break;
|
||||
|
||||
case ContextBarObserver::CancelDrag:
|
||||
m_pixelsMovement->discardImage(false);
|
||||
m_pixelsMovement->discardImage(PixelsMovement::DontCommitChanges);
|
||||
m_discarded = true;
|
||||
|
||||
// Quit from MovingPixelsState, back to standby.
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2015 David Capello
|
||||
// Copyright (C) 2015-2016 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -29,10 +29,6 @@ MovingSymmetryState::MovingSymmetryState(Editor* editor, MouseMessage* msg,
|
||||
editor->captureMouse();
|
||||
}
|
||||
|
||||
MovingSymmetryState::~MovingSymmetryState()
|
||||
{
|
||||
}
|
||||
|
||||
bool MovingSymmetryState::onMouseUp(Editor* editor, MouseMessage* msg)
|
||||
{
|
||||
editor->backToPreviousState();
|
||||
|
@ -19,7 +19,6 @@ namespace app {
|
||||
MovingSymmetryState(Editor* editor, ui::MouseMessage* msg,
|
||||
app::gen::SymmetryMode mode,
|
||||
Option<int>& symmetryAxis);
|
||||
virtual ~MovingSymmetryState();
|
||||
|
||||
virtual bool onMouseUp(Editor* editor, ui::MouseMessage* msg) override;
|
||||
virtual bool onMouseMove(Editor* editor, ui::MouseMessage* msg) override;
|
||||
|
@ -581,14 +581,16 @@ void PixelsMovement::dropImage()
|
||||
m_document->setExtraCel(ExtraCelRef(nullptr));
|
||||
}
|
||||
|
||||
void PixelsMovement::discardImage(bool commit)
|
||||
void PixelsMovement::discardImage(const CommitChangesOption commit,
|
||||
const KeepMaskOption keepMask)
|
||||
{
|
||||
m_isDragging = false;
|
||||
|
||||
// Deselect the mask (here we don't stamp the image)
|
||||
m_transaction.execute(new cmd::DeselectMask(m_document));
|
||||
if (keepMask == DontKeepMask)
|
||||
m_transaction.execute(new cmd::DeselectMask(m_document));
|
||||
|
||||
if (commit)
|
||||
if (commit == CommitChanges)
|
||||
m_transaction.commit();
|
||||
|
||||
// Destroy the extra cel and regenerate the mask boundaries (we've
|
||||
|
@ -47,6 +47,16 @@ namespace app {
|
||||
ScaleFromPivot = 32,
|
||||
};
|
||||
|
||||
enum CommitChangesOption {
|
||||
DontCommitChanges,
|
||||
CommitChanges,
|
||||
};
|
||||
|
||||
enum KeepMaskOption {
|
||||
DontKeepMask,
|
||||
KeepMask,
|
||||
};
|
||||
|
||||
PixelsMovement(Context* context,
|
||||
Site site,
|
||||
const Image* moveThis,
|
||||
@ -76,7 +86,8 @@ namespace app {
|
||||
|
||||
void dropImageTemporarily();
|
||||
void dropImage();
|
||||
void discardImage(bool commit = true);
|
||||
void discardImage(const CommitChangesOption commit = CommitChanges,
|
||||
const KeepMaskOption keepMask = DontKeepMask);
|
||||
bool isDragging() const;
|
||||
|
||||
gfx::Rect getImageBounds();
|
||||
|
@ -27,13 +27,17 @@ namespace app {
|
||||
|
||||
using namespace ui;
|
||||
|
||||
PlayState::PlayState(bool playOnce)
|
||||
PlayState::PlayState(const bool playOnce,
|
||||
const bool playAll)
|
||||
: m_editor(nullptr)
|
||||
, m_playOnce(playOnce)
|
||||
, m_playAll(playAll)
|
||||
, m_toScroll(false)
|
||||
, m_playTimer(10)
|
||||
, m_nextFrameTime(-1)
|
||||
, m_pingPongForward(true)
|
||||
, m_refFrame(0)
|
||||
, m_tag(nullptr)
|
||||
{
|
||||
m_playTimer.Tick.connect(&PlayState::onPlaybackTick, this);
|
||||
|
||||
@ -52,16 +56,18 @@ void PlayState::onEnterState(Editor* editor)
|
||||
m_refFrame = editor->frame();
|
||||
}
|
||||
|
||||
// Get the tag
|
||||
if (!m_playAll)
|
||||
m_tag = get_animation_tag(m_editor->sprite(), m_refFrame);
|
||||
|
||||
// Go to the first frame of the animation or active frame tag
|
||||
if (m_playOnce) {
|
||||
frame_t frame = 0;
|
||||
|
||||
doc::FrameTag* tag = get_animation_tag(
|
||||
m_editor->sprite(), m_refFrame);
|
||||
if (tag) {
|
||||
frame = (tag->aniDir() == AniDir::REVERSE ?
|
||||
tag->toFrame():
|
||||
tag->fromFrame());
|
||||
if (m_tag) {
|
||||
frame = (m_tag->aniDir() == AniDir::REVERSE ?
|
||||
m_tag->toFrame():
|
||||
m_tag->fromFrame());
|
||||
}
|
||||
|
||||
m_editor->setFrame(frame);
|
||||
@ -147,24 +153,23 @@ void PlayState::onPlaybackTick()
|
||||
m_nextFrameTime -= (base::current_tick() - m_curFrameTick);
|
||||
|
||||
doc::Sprite* sprite = m_editor->sprite();
|
||||
doc::FrameTag* tag = get_animation_tag(sprite, m_refFrame);
|
||||
|
||||
while (m_nextFrameTime <= 0) {
|
||||
doc::frame_t frame = m_editor->frame();
|
||||
|
||||
if (m_playOnce) {
|
||||
bool atEnd = false;
|
||||
if (tag) {
|
||||
switch (tag->aniDir()) {
|
||||
if (m_tag) {
|
||||
switch (m_tag->aniDir()) {
|
||||
case AniDir::FORWARD:
|
||||
atEnd = (frame == tag->toFrame());
|
||||
atEnd = (frame == m_tag->toFrame());
|
||||
break;
|
||||
case AniDir::REVERSE:
|
||||
atEnd = (frame == tag->fromFrame());
|
||||
atEnd = (frame == m_tag->fromFrame());
|
||||
break;
|
||||
case AniDir::PING_PONG:
|
||||
atEnd = (!m_pingPongForward &&
|
||||
frame == tag->fromFrame());
|
||||
frame == m_tag->fromFrame());
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -178,7 +183,7 @@ void PlayState::onPlaybackTick()
|
||||
}
|
||||
|
||||
frame = calculate_next_frame(
|
||||
sprite, frame, frame_t(1), tag,
|
||||
sprite, frame, frame_t(1), m_tag,
|
||||
m_pingPongForward);
|
||||
|
||||
m_editor->setFrame(frame);
|
||||
|
@ -14,13 +14,18 @@
|
||||
#include "obs/connection.h"
|
||||
#include "ui/timer.h"
|
||||
|
||||
namespace doc {
|
||||
class FrameTag;
|
||||
}
|
||||
|
||||
namespace app {
|
||||
|
||||
class CommandExecutionEvent;
|
||||
|
||||
class PlayState : public StateWithWheelBehavior {
|
||||
public:
|
||||
PlayState(bool playOnce);
|
||||
PlayState(const bool playOnce,
|
||||
const bool playAll);
|
||||
|
||||
void onEnterState(Editor* editor) override;
|
||||
LeaveAction onLeaveState(Editor* editor, EditorState* newState) override;
|
||||
@ -40,6 +45,7 @@ namespace app {
|
||||
|
||||
Editor* m_editor;
|
||||
bool m_playOnce;
|
||||
bool m_playAll;
|
||||
bool m_toScroll;
|
||||
ui::Timer m_playTimer;
|
||||
|
||||
@ -50,6 +56,7 @@ namespace app {
|
||||
|
||||
bool m_pingPongForward;
|
||||
doc::frame_t m_refFrame;
|
||||
doc::FrameTag* m_tag;
|
||||
|
||||
obs::scoped_connection m_ctxConn;
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -27,10 +27,6 @@ ScrollingState::ScrollingState()
|
||||
{
|
||||
}
|
||||
|
||||
ScrollingState::~ScrollingState()
|
||||
{
|
||||
}
|
||||
|
||||
bool ScrollingState::onMouseDown(Editor* editor, MouseMessage* msg)
|
||||
{
|
||||
m_oldPos = msg->position();
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -16,7 +16,6 @@ namespace app {
|
||||
class ScrollingState : public EditorState {
|
||||
public:
|
||||
ScrollingState();
|
||||
virtual ~ScrollingState();
|
||||
virtual bool isTemporalState() const override { return true; }
|
||||
virtual bool onMouseDown(Editor* editor, ui::MouseMessage* msg) override;
|
||||
virtual bool onMouseUp(Editor* editor, ui::MouseMessage* msg) override;
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "app/document_range.h"
|
||||
#include "app/ini_file.h"
|
||||
#include "app/pref/preferences.h"
|
||||
#include "app/tools/active_tool.h"
|
||||
#include "app/tools/ink.h"
|
||||
#include "app/tools/pick_ink.h"
|
||||
#include "app/tools/tool.h"
|
||||
@ -254,7 +255,16 @@ bool StandbyState::onMouseDown(Editor* editor, MouseMessage* msg)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (clickedInk->isSelection()) {
|
||||
// Only if the selected tool or quick tool is selection, we give the
|
||||
// possibility to transform/move the selection. In other case,
|
||||
// e.g. when selection is used with right-click mode, the
|
||||
// transformation is disabled.
|
||||
auto activeToolManager = App::instance()->activeToolManager();
|
||||
if (clickedInk->isSelection() &&
|
||||
((activeToolManager->selectedTool() &&
|
||||
activeToolManager->selectedTool()->getInk(0)->isSelection()) ||
|
||||
(activeToolManager->quickTool() &&
|
||||
activeToolManager->quickTool()->getInk(0)->isSelection()))) {
|
||||
// Transform selected pixels
|
||||
if (editor->isActive() &&
|
||||
document->isMaskVisible() &&
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -34,10 +34,6 @@ ZoomingState::ZoomingState()
|
||||
{
|
||||
}
|
||||
|
||||
ZoomingState::~ZoomingState()
|
||||
{
|
||||
}
|
||||
|
||||
bool ZoomingState::onMouseDown(Editor* editor, MouseMessage* msg)
|
||||
{
|
||||
m_startPos = msg->position();
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -17,7 +17,6 @@ namespace app {
|
||||
class ZoomingState : public EditorState {
|
||||
public:
|
||||
ZoomingState();
|
||||
virtual ~ZoomingState();
|
||||
virtual bool isTemporalState() const override { return true; }
|
||||
virtual bool onMouseDown(Editor* editor, ui::MouseMessage* msg) override;
|
||||
virtual bool onMouseUp(Editor* editor, ui::MouseMessage* msg) override;
|
||||
|
@ -26,39 +26,20 @@ namespace app {
|
||||
using namespace app::skin;
|
||||
using namespace ui;
|
||||
|
||||
PopupWindowPin::PopupWindowPin(const std::string& text, ClickBehavior clickBehavior)
|
||||
: PopupWindow(text, clickBehavior)
|
||||
, m_pin("")
|
||||
PopupWindowPin::PopupWindowPin(const std::string& text,
|
||||
const ClickBehavior clickBehavior,
|
||||
const bool canPin)
|
||||
: PopupWindow(text, clickBehavior,
|
||||
EnterBehavior::CloseOnEnter, canPin)
|
||||
, m_pinned(false)
|
||||
{
|
||||
SkinTheme* theme = SkinTheme::instance();
|
||||
|
||||
m_pin.setFocusStop(false);
|
||||
m_pin.Click.connect(&PopupWindowPin::onPinClick, this);
|
||||
m_pin.setIconInterface(
|
||||
new ButtonIconImpl(theme->parts.unpinned(),
|
||||
theme->parts.pinned(),
|
||||
theme->parts.unpinned(),
|
||||
CENTER | MIDDLE));
|
||||
}
|
||||
|
||||
void PopupWindowPin::showPin(bool state)
|
||||
void PopupWindowPin::setPinned(const bool pinned)
|
||||
{
|
||||
m_pin.setVisible(state);
|
||||
}
|
||||
|
||||
void PopupWindowPin::setPinned(bool pinned)
|
||||
{
|
||||
m_pin.setSelected(pinned);
|
||||
|
||||
Event ev(this);
|
||||
onPinClick(ev);
|
||||
}
|
||||
|
||||
void PopupWindowPin::onPinClick(Event& ev)
|
||||
{
|
||||
if (m_pin.isSelected()) {
|
||||
m_pinned = pinned;
|
||||
if (m_pinned)
|
||||
makeFloating();
|
||||
}
|
||||
else {
|
||||
gfx::Rect rc = bounds();
|
||||
rc.enlarge(8);
|
||||
@ -72,8 +53,16 @@ bool PopupWindowPin::onProcessMessage(Message* msg)
|
||||
switch (msg->type()) {
|
||||
|
||||
case kOpenMessage: {
|
||||
if (!isPinned())
|
||||
makeFixed();
|
||||
if (!m_pinned)
|
||||
setPinned(false);
|
||||
break;
|
||||
}
|
||||
|
||||
case kCloseMessage: {
|
||||
// If the closer() wasn't the hot region or the window, it might
|
||||
// be because the user pressed the close button.
|
||||
if (closer() && closer() != this)
|
||||
m_pinned = false;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -88,7 +77,7 @@ void PopupWindowPin::onWindowMovement()
|
||||
|
||||
// If the window isn't pinned and we move it, we can automatically
|
||||
// pin it.
|
||||
if (!m_pin.isSelected())
|
||||
if (!m_pinned)
|
||||
setPinned(true);
|
||||
}
|
||||
|
||||
|
@ -15,25 +15,19 @@ namespace app {
|
||||
|
||||
class PopupWindowPin : public ui::PopupWindow {
|
||||
public:
|
||||
PopupWindowPin(const std::string& text, ClickBehavior clickBehavior);
|
||||
PopupWindowPin(const std::string& text,
|
||||
const ClickBehavior clickBehavior,
|
||||
const bool canPin = false);
|
||||
|
||||
void showPin(bool state);
|
||||
bool isPinned() const { return m_pin.isSelected(); }
|
||||
void setPinned(bool pinned);
|
||||
bool isPinned() const { return m_pinned; }
|
||||
void setPinned(const bool pinned);
|
||||
|
||||
protected:
|
||||
virtual bool onProcessMessage(ui::Message* msg) override;
|
||||
virtual void onWindowMovement() override;
|
||||
|
||||
// The pin. Your derived class must add this pin in some place of
|
||||
// the frame as a children, and you must to remove the pin from the
|
||||
// parent in your class's dtor.
|
||||
ui::CheckBox* getPin() { return &m_pin; }
|
||||
|
||||
private:
|
||||
void onPinClick(ui::Event& ev);
|
||||
|
||||
ui::CheckBox m_pin;
|
||||
bool m_pinned;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
@ -299,7 +299,8 @@ void PreviewEditorWindow::onPlayClicked()
|
||||
|
||||
if (m_playButton->isPlaying()) {
|
||||
m_refFrame = miniEditor->frame();
|
||||
miniEditor->play(Preferences::instance().preview.playOnce());
|
||||
miniEditor->play(Preferences::instance().preview.playOnce(),
|
||||
Preferences::instance().preview.playAll());
|
||||
}
|
||||
else
|
||||
miniEditor->stop();
|
||||
@ -311,8 +312,12 @@ void PreviewEditorWindow::onPopupSpeed()
|
||||
if (!miniEditor || !miniEditor->document())
|
||||
return;
|
||||
|
||||
auto& pref = Preferences::instance();
|
||||
|
||||
miniEditor->showAnimationSpeedMultiplierPopup(
|
||||
Preferences::instance().preview.playOnce, false);
|
||||
pref.preview.playOnce,
|
||||
pref.preview.playAll,
|
||||
false);
|
||||
m_aniSpeed = miniEditor->getAnimationSpeedMultiplier();
|
||||
}
|
||||
|
||||
@ -383,7 +388,8 @@ void PreviewEditorWindow::updateUsingEditor(Editor* editor)
|
||||
if (!miniEditor->isPlaying())
|
||||
miniEditor->setFrame(m_refFrame = editor->frame());
|
||||
|
||||
miniEditor->play(Preferences::instance().preview.playOnce());
|
||||
miniEditor->play(Preferences::instance().preview.playOnce(),
|
||||
Preferences::instance().preview.playAll());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,6 +60,11 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
void onSizeHint(SizeHintEvent& ev) override {
|
||||
ev.setSizeHint(SkinTheme::instance()->parts.windowCloseButtonNormal()->size());
|
||||
}
|
||||
|
||||
void onClick(Event& ev) override {
|
||||
Button::onClick(ev);
|
||||
closeWindow();
|
||||
@ -1666,6 +1671,7 @@ void SkinTheme::paintWindowButton(ui::PaintEvent& ev)
|
||||
else
|
||||
part = parts.windowCloseButtonNormal();
|
||||
|
||||
g->fillRect(BGCOLOR, rc);
|
||||
g->drawRgbaSurface(part->bitmap(0), rc.x, rc.y);
|
||||
}
|
||||
|
||||
|
@ -346,7 +346,8 @@ void Timeline::setFrame(frame_t frame, bool byUser)
|
||||
m_editor->setFrame(m_frame);
|
||||
|
||||
if (isPlaying)
|
||||
m_editor->play(false);
|
||||
m_editor->play(false,
|
||||
Preferences::instance().editor.playAll());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -32,6 +32,7 @@ using namespace ui;
|
||||
|
||||
ZoomEntry::ZoomEntry()
|
||||
: IntEntry(0, render::Zoom::linearValues()-1, this)
|
||||
, m_locked(false)
|
||||
{
|
||||
setSuffix("%");
|
||||
setup_mini_look(this);
|
||||
@ -41,11 +42,15 @@ ZoomEntry::ZoomEntry()
|
||||
|
||||
void ZoomEntry::setZoom(const render::Zoom& zoom)
|
||||
{
|
||||
if (m_locked)
|
||||
return;
|
||||
|
||||
setText(onGetTextFromValue(zoom.linearScale()));
|
||||
}
|
||||
|
||||
void ZoomEntry::onValueChange()
|
||||
{
|
||||
base::ScopedValue<bool> lock(m_locked, true, m_locked);
|
||||
IntEntry::onValueChange();
|
||||
|
||||
render::Zoom zoom = render::Zoom::fromLinearScale(getValue());
|
||||
|
@ -29,6 +29,8 @@ namespace app {
|
||||
int onGetValueFromText(const std::string& text) override;
|
||||
|
||||
void onValueChange() override;
|
||||
|
||||
bool m_locked;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
@ -119,7 +119,8 @@ FileFormat detect_format_by_file_extension(const std::string& filename)
|
||||
if (ext == "pal")
|
||||
return FileFormat::PAL_PALETTE;
|
||||
|
||||
if (ext == "pcx")
|
||||
if (ext == "pcx" ||
|
||||
ext == "pcc")
|
||||
return FileFormat::PCX_IMAGE;
|
||||
|
||||
if (ext == "anim")
|
||||
|
@ -118,10 +118,13 @@ bool IntEntry::onProcessMessage(Message* msg)
|
||||
if (hasFocus() && !isReadOnly()) {
|
||||
KeyMessage* keymsg = static_cast<KeyMessage*>(msg);
|
||||
int chr = keymsg->unicodeChar();
|
||||
if (chr && (chr < '0' || chr > '9')) {
|
||||
// By-pass Entry::onProcessMessage()
|
||||
return Widget::onProcessMessage(msg);
|
||||
if (chr >= 32 && (chr < '0' || chr > '9')) {
|
||||
// "Eat" all keys that aren't number
|
||||
return true;
|
||||
}
|
||||
// Else we use the default Entry processing function which
|
||||
// will process keys like Left/Right arrows, clipboard
|
||||
// handling, etc.
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -229,7 +229,7 @@ bool ListBox::onProcessMessage(Message* msg)
|
||||
}
|
||||
|
||||
if (pick_item) {
|
||||
Widget* picked;
|
||||
Widget* picked = nullptr;
|
||||
|
||||
if (view) {
|
||||
picked = view->viewport()->pick(mousePos);
|
||||
@ -239,11 +239,8 @@ bool ListBox::onProcessMessage(Message* msg)
|
||||
}
|
||||
|
||||
// If the picked widget is a child of the list, select it
|
||||
if (picked && hasChild(picked)) {
|
||||
if (ListItem* pickedItem = dynamic_cast<ListItem*>(picked)) {
|
||||
selectChild(pickedItem, msg);
|
||||
}
|
||||
}
|
||||
if (picked && hasChild(picked))
|
||||
selectChild(picked, msg);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -365,7 +362,7 @@ void ListBox::onSizeHint(SizeHintEvent& ev)
|
||||
int w = 0, h = 0;
|
||||
|
||||
UI_FOREACH_WIDGET_WITH_END(children(), it, end) {
|
||||
Size reqSize = static_cast<ListItem*>(*it)->sizeHint();
|
||||
Size reqSize = (*it)->sizeHint();
|
||||
|
||||
w = MAX(w, reqSize.w);
|
||||
h += reqSize.h + (it+1 != end ? this->childSpacing(): 0);
|
||||
|
@ -20,8 +20,9 @@ namespace ui {
|
||||
using namespace gfx;
|
||||
|
||||
PopupWindow::PopupWindow(const std::string& text,
|
||||
ClickBehavior clickBehavior,
|
||||
EnterBehavior enterBehavior)
|
||||
const ClickBehavior clickBehavior,
|
||||
const EnterBehavior enterBehavior,
|
||||
const bool withCloseButton)
|
||||
: Window(text.empty() ? WithoutTitleBar: WithTitleBar, text)
|
||||
, m_clickBehavior(clickBehavior)
|
||||
, m_enterBehavior(enterBehavior)
|
||||
@ -33,7 +34,8 @@ PopupWindow::PopupWindow(const std::string& text,
|
||||
setWantFocus(false);
|
||||
setAlign(LEFT | TOP);
|
||||
|
||||
removeDecorativeWidgets();
|
||||
if (!withCloseButton)
|
||||
removeDecorativeWidgets();
|
||||
|
||||
initTheme();
|
||||
noBorderNoChildSpacing();
|
||||
|
@ -26,8 +26,9 @@ namespace ui {
|
||||
};
|
||||
|
||||
PopupWindow(const std::string& text = "",
|
||||
ClickBehavior clickBehavior = ClickBehavior::CloseOnClickOutsideHotRegion,
|
||||
EnterBehavior enterBehavior = EnterBehavior::CloseOnEnter);
|
||||
const ClickBehavior clickBehavior = ClickBehavior::CloseOnClickOutsideHotRegion,
|
||||
const EnterBehavior enterBehavior = EnterBehavior::CloseOnEnter,
|
||||
const bool withCloseButton = false);
|
||||
~PopupWindow();
|
||||
|
||||
// Sets the hot region. This region indicates the area where the
|
||||
|
Loading…
x
Reference in New Issue
Block a user