Merge branch 'master' into beta

This commit is contained in:
David Capello 2016-12-02 19:12:46 -03:00
commit 396f5fb78f
46 changed files with 574 additions and 263 deletions

View File

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

View File

@ -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="&quot;default&quot;" migrate="Skin.Selected" />

View File

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

View File

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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -29,6 +29,8 @@ namespace app {
int onGetValueFromText(const std::string& text) override;
void onValueChange() override;
bool m_locked;
};
} // namespace app

View File

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

View File

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

View File

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

View File

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

View File

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