diff --git a/data/pref.xml b/data/pref.xml
index 47f9cdcef..2d845e4c5 100644
--- a/data/pref.xml
+++ b/data/pref.xml
@@ -310,6 +310,7 @@
+
diff --git a/data/strings/en.ini b/data/strings/en.ini
index 4614892ae..d899a9b83 100644
--- a/data/strings/en.ini
+++ b/data/strings/en.ini
@@ -538,6 +538,11 @@ sprite = Sprite
sprite_tooltip = Source of sprite samples to export in the sprite sheet
borders = Borders
borders_tooltip = Add or trim borders from each sprite in the sprite sheet
+expand_all_sections_tooltip = <<
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -34,6 +61,10 @@
+
+
+
+
@@ -68,29 +99,12 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
@@ -122,7 +136,6 @@
-
diff --git a/src/app/commands/cmd_export_sprite_sheet.cpp b/src/app/commands/cmd_export_sprite_sheet.cpp
index 6f68466d0..b99ba76a7 100644
--- a/src/app/commands/cmd_export_sprite_sheet.cpp
+++ b/src/app/commands/cmd_export_sprite_sheet.cpp
@@ -21,6 +21,7 @@
#include "app/i18n/strings.h"
#include "app/job.h"
#include "app/modules/editors.h"
+#include "app/modules/gui.h"
#include "app/pref/preferences.h"
#include "app/restore_visible_layers.h"
#include "app/task.h"
@@ -39,6 +40,7 @@
#include "doc/layer.h"
#include "doc/tag.h"
#include "fmt/format.h"
+#include "ui/message.h"
#include "ui/system.h"
#include "export_sprite_sheet.xml.h"
@@ -86,9 +88,9 @@ struct ExportSpriteSheetParams : public NewParams {
#ifdef ENABLE_UI
enum Section {
+ kSectionLayout,
kSectionSprite,
kSectionBorders,
- kSectionLayout,
kSectionOutput,
};
@@ -288,7 +290,8 @@ class ExportSpriteSheetWindow : public app::gen::ExportSpriteSheet {
public:
ExportSpriteSheetWindow(DocExporter& exporter,
Site& site,
- ExportSpriteSheetParams& params)
+ ExportSpriteSheetParams& params,
+ Preferences& pref)
: m_exporter(exporter)
, m_frontBuffer(std::make_shared())
, m_backBuffer(std::make_shared())
@@ -302,6 +305,10 @@ public:
, m_filenameFormat(params.filenameFormat())
{
sectionTabs()->ItemChange.connect(base::Bind(&ExportSpriteSheetWindow::onChangeSection, this));
+ expandSections()->Click.connect(base::Bind(&ExportSpriteSheetWindow::onExpandSections, this));
+ closeSpriteSection()->Click.connect(base::Bind(&ExportSpriteSheetWindow::onCloseSection, this, kSectionSprite));
+ closeBordersSection()->Click.connect(base::Bind(&ExportSpriteSheetWindow::onCloseSection, this, kSectionBorders));
+ closeOutputSection()->Click.connect(base::Bind(&ExportSpriteSheetWindow::onCloseSection, this, kSectionOutput));
static_assert(
(int)app::SpriteSheetType::None == 0 &&
@@ -397,7 +404,7 @@ public:
if (m_filename.empty() ||
m_filename == kSpecifiedFilename) {
- std::string defExt = Preferences::instance().spriteSheet.defaultExtension();
+ std::string defExt = pref.spriteSheet.defaultExtension();
if (base::utf8_icmp(base::get_file_extension(site.document()->filename()), defExt) == 0)
m_filename = base + "-sheet." + defExt;
@@ -436,13 +443,30 @@ public:
preview()->Click.connect(base::Bind(&ExportSpriteSheetWindow::generatePreview, this));
m_genTimer.Tick.connect(base::Bind(&ExportSpriteSheetWindow::onGenTimerTick, this));
+ // Select tabs
+ {
+ const std::string s = pref.spriteSheet.sections();
+ const bool layout = (s.find("layout") != std::string::npos);
+ const bool sprite = (s.find("sprite") != std::string::npos);
+ const bool borders = (s.find("borders") != std::string::npos);
+ const bool output = (s.find("output") != std::string::npos);
+ sectionTabs()->getItem(kSectionLayout)->setSelected(layout || (!sprite & !borders && !output));
+ sectionTabs()->getItem(kSectionSprite)->setSelected(sprite);
+ sectionTabs()->getItem(kSectionBorders)->setSelected(borders);
+ sectionTabs()->getItem(kSectionOutput)->setSelected(output);
+ }
+
onChangeSection();
onSheetTypeChange();
onFileNamesChange();
updateExportButton();
- preview()->setSelected(Preferences::instance().spriteSheet.preview());
+ preview()->setSelected(pref.spriteSheet.preview());
generatePreview();
+
+ remapWindow();
+ centerWindow();
+ load_window_pos(this, "ExportSpriteSheet");
}
~ExportSpriteSheetWindow() {
@@ -456,6 +480,19 @@ public:
}
}
+ std::string selectedSectionsString() const {
+ const bool layout = sectionTabs()->getItem(kSectionLayout)->isSelected();
+ const bool sprite = sectionTabs()->getItem(kSectionSprite)->isSelected();
+ const bool borders = sectionTabs()->getItem(kSectionBorders)->isSelected();
+ const bool output = sectionTabs()->getItem(kSectionOutput)->isSelected();
+ return
+ fmt::format("{} {} {} {}",
+ (layout ? "layout": ""),
+ (sprite ? "sprite": ""),
+ (borders ? "borders": ""),
+ (output ? "output": ""));
+ }
+
bool ok() const {
return closer() == exportButton();
}
@@ -491,6 +528,15 @@ public:
private:
+ bool onProcessMessage(ui::Message* msg) override {
+ switch (msg->type()) {
+ case kCloseMessage:
+ save_window_pos(this, "ExportSpriteSheet");
+ break;
+ }
+ return Window::onProcessMessage(msg);
+ }
+
void onBroadcastMouseMessage(WidgetsList& targets) override {
Window::onBroadcastMouseMessage(targets);
@@ -500,17 +546,38 @@ private:
}
void onChangeSection() {
- Widget* section = nullptr;
- switch (sectionTabs()->selectedItem()) {
- case kSectionSprite: section = sectionSprite(); break;
- case kSectionBorders: section = sectionBorders(); break;
- case kSectionLayout: section = sectionLayout(); break;
- case kSectionOutput: section = sectionOutput(); break;
- }
- panel()->showChild(section);
+ panel()->showAllChildren();
+
+ const bool layout = sectionTabs()->getItem(kSectionLayout)->isSelected();
+ const bool sprite = sectionTabs()->getItem(kSectionSprite)->isSelected();
+ const bool borders = sectionTabs()->getItem(kSectionBorders)->isSelected();
+ const bool output = sectionTabs()->getItem(kSectionOutput)->isSelected();
+
+ sectionLayout()->setVisible(layout);
+ sectionSpriteSeparator()->setVisible(sprite && layout);
+ sectionSprite()->setVisible(sprite);
+ sectionBordersSeparator()->setVisible(borders && (layout || sprite));
+ sectionBorders()->setVisible(borders);
+ sectionOutputSeparator()->setVisible(output && (layout || sprite || borders));
+ sectionOutput()->setVisible(output);
+
resize();
}
+ void onExpandSections() {
+ sectionTabs()->getItem(kSectionLayout)->setSelected(true);
+ sectionTabs()->getItem(kSectionSprite)->setSelected(true);
+ sectionTabs()->getItem(kSectionBorders)->setSelected(true);
+ sectionTabs()->getItem(kSectionOutput)->setSelected(true);
+ onChangeSection();
+ }
+
+ void onCloseSection(const Section section) {
+ if (sectionTabs()->countSelectedItems() > 1)
+ sectionTabs()->getItem(section)->setSelected(false);
+ onChangeSection();
+ }
+
app::SpriteSheetType spriteSheetTypeValue() const {
return (app::SpriteSheetType)(sheetType()->getSelectedItemIndex()+1);
}
@@ -1158,14 +1225,19 @@ void ExportSpriteSheetCommand::onExecute(Context* context)
bool askOverwrite = params.askOverwrite();
if (showUI) {
- ExportSpriteSheetWindow window(exporter, site, params);
+ auto& pref = Preferences::instance();
+
+ ExportSpriteSheetWindow window(exporter, site, params, pref);
window.openWindowInForeground();
+
+ // Save global sprite sheet generation settings anyway (even if
+ // the user cancel the dialog, the global settings are stored).
+ pref.spriteSheet.preview(window.preview()->isSelected());
+ pref.spriteSheet.sections(window.selectedSectionsString());
+
if (!window.ok())
return;
- // Preview sprite sheet generation
- Preferences::instance().spriteSheet.preview(window.preview()->isSelected());
-
window.updateParams(params);
docPref.spriteSheet.defined(true);
docPref.spriteSheet.type (params.type());
diff --git a/src/app/commands/filters/filter_target_buttons.cpp b/src/app/commands/filters/filter_target_buttons.cpp
index 7795be650..1488e63a5 100644
--- a/src/app/commands/filters/filter_target_buttons.cpp
+++ b/src/app/commands/filters/filter_target_buttons.cpp
@@ -1,4 +1,5 @@
// Aseprite
+// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@@ -41,7 +42,7 @@ FilterTargetButtons::FilterTargetButtons(int imgtype, bool withChannels)
, m_index(nullptr)
, m_cels(nullptr)
{
- setMultipleSelection(true);
+ setMultiMode(MultiMode::Set);
addChild(&m_tooltips);
if (withChannels) {
diff --git a/src/app/ui/button_set.cpp b/src/app/ui/button_set.cpp
index f0460e8fc..826adf18d 100644
--- a/src/app/ui/button_set.cpp
+++ b/src/app/ui/button_set.cpp
@@ -1,5 +1,5 @@
// Aseprite
-// Copyright (C) 2018 Igara Studio S.A.
+// Copyright (C) 2018-2019 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
@@ -299,7 +299,7 @@ ButtonSet::ButtonSet(int columns)
: Grid(columns, false)
, m_offerCapture(true)
, m_triggerOnMouseUp(false)
- , m_multipleSelection(false)
+ , m_multiMode(MultiMode::One)
{
InitTheme.connect(
[this]{
@@ -357,6 +357,15 @@ int ButtonSet::selectedItem() const
return -1;
}
+int ButtonSet::countSelectedItems() const
+{
+ int count = 0;
+ for (auto child : children())
+ if (child->isSelected())
+ ++count;
+ return count;
+}
+
void ButtonSet::setSelectedItem(int index, bool focusItem)
{
if (index >= 0 && index < (int)children().size())
@@ -372,16 +381,38 @@ void ButtonSet::setSelectedItem(Item* item, bool focusItem)
void ButtonSet::onSelectItem(Item* item, bool focusItem, ui::Message* msg)
{
- if (!m_multipleSelection) {
- if (item && item->isSelected())
+ const int count = countSelectedItems();
+
+ if ((m_multiMode == MultiMode::One) ||
+ (m_multiMode == MultiMode::OneOrMore &&
+ msg &&
+ !msg->shiftPressed() &&
+ !msg->altPressed() &&
+ !msg->ctrlPressed() &&
+ !msg->cmdPressed())) {
+ if (item && item->isSelected() &&
+ ((m_multiMode == MultiMode::One) ||
+ (m_multiMode == MultiMode::OneOrMore && count == 1)))
return;
- Item* sel = findSelectedItem();
- if (sel)
- sel->setSelected(false);
+ if (m_multiMode == MultiMode::One) {
+ if (auto sel = findSelectedItem())
+ sel->setSelected(false);
+ }
+ else if (m_multiMode == MultiMode::OneOrMore) {
+ for (auto child : children())
+ child->setSelected(false);
+ }
}
if (item) {
+ if (m_multiMode == MultiMode::OneOrMore) {
+ // Item already selected
+ if (count == 1 && item == findSelectedItem())
+ return;
+ }
+
+ // Toggle item
item->setSelected(!item->isSelected());
if (focusItem)
item->requestFocus();
@@ -405,9 +436,9 @@ void ButtonSet::setTriggerOnMouseUp(bool state)
m_triggerOnMouseUp = state;
}
-void ButtonSet::setMultipleSelection(bool state)
+void ButtonSet::setMultiMode(MultiMode mode)
{
- m_multipleSelection = state;
+ m_multiMode = mode;
}
void ButtonSet::onItemChange(Item* item)
diff --git a/src/app/ui/button_set.h b/src/app/ui/button_set.h
index 66789769d..5a15da0aa 100644
--- a/src/app/ui/button_set.h
+++ b/src/app/ui/button_set.h
@@ -1,4 +1,5 @@
// Aseprite
+// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@@ -38,6 +39,12 @@ namespace app {
gfx::Color m_hotColor;
};
+ enum class MultiMode {
+ One, // Only one button can be selected (like radio buttons)
+ Set, // Each button is a checkbox
+ OneOrMore, // One click selects one button, ctrl+click multiple selections
+ };
+
ButtonSet(int columns);
Item* addItem(const std::string& text, int hspan = 1, int vspan = 1);
@@ -47,6 +54,7 @@ namespace app {
int getItemIndex(const Item* item) const;
int selectedItem() const;
+ int countSelectedItems() const;
Item* findSelectedItem() const;
void setSelectedItem(int index, bool focusItem = true);
void setSelectedItem(Item* item, bool focusItem = true);
@@ -54,7 +62,7 @@ namespace app {
void setOfferCapture(bool state);
void setTriggerOnMouseUp(bool state);
- void setMultipleSelection(bool state);
+ void setMultiMode(MultiMode mode);
obs::signal ItemChange;
obs::signal RightClick;
@@ -67,7 +75,7 @@ namespace app {
private:
bool m_offerCapture;
bool m_triggerOnMouseUp;
- bool m_multipleSelection;
+ MultiMode m_multiMode;
};
} // namespace app
diff --git a/src/app/ui/color_popup.cpp b/src/app/ui/color_popup.cpp
index 94d14353a..f64e564f1 100644
--- a/src/app/ui/color_popup.cpp
+++ b/src/app/ui/color_popup.cpp
@@ -125,15 +125,6 @@ ColorPopup::CustomButtonSet::CustomButtonSet()
{
}
-int ColorPopup::CustomButtonSet::countSelectedItems()
-{
- int count = 0;
- for (int i=0; iisSelected())
- ++count;
- return count;
-}
-
void ColorPopup::CustomButtonSet::onSelectItem(Item* item, bool focusItem, ui::Message* msg)
{
int count = countSelectedItems();
diff --git a/src/app/ui/color_popup.h b/src/app/ui/color_popup.h
index c3bca521f..8795afd1d 100644
--- a/src/app/ui/color_popup.h
+++ b/src/app/ui/color_popup.h
@@ -71,7 +71,6 @@ namespace app {
class CustomButtonSet : public ButtonSet {
public:
CustomButtonSet();
- int countSelectedItems();
private:
void onSelectItem(Item* item, bool focusItem, ui::Message* msg) override;
};
diff --git a/src/app/ui/context_bar.cpp b/src/app/ui/context_bar.cpp
index 6853bcccd..72f7ac1dc 100644
--- a/src/app/ui/context_bar.cpp
+++ b/src/app/ui/context_bar.cpp
@@ -1107,7 +1107,7 @@ protected:
class ContextBar::SymmetryField : public ButtonSet {
public:
SymmetryField() : ButtonSet(2) {
- setMultipleSelection(true);
+ setMultiMode(MultiMode::Set);
SkinTheme* theme = SkinTheme::instance();
addItem(theme->parts.horizontalSymmetry());
addItem(theme->parts.verticalSymmetry());
diff --git a/src/app/widget_loader.cpp b/src/app/widget_loader.cpp
index ad1aa91d9..b5db9e349 100644
--- a/src/app/widget_loader.cpp
+++ b/src/app/widget_loader.cpp
@@ -1,4 +1,5 @@
// Aseprite
+// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@@ -451,9 +452,10 @@ Widget* WidgetLoader::convertXmlElementToWidget(const TiXmlElement* elem, Widget
widget = new ButtonSet(strtol(columns, NULL, 10));
if (ButtonSet* buttonset = dynamic_cast(widget)) {
- bool multiple = bool_attr_is_true(elem, "multiple");
- if (multiple)
- buttonset->setMultipleSelection(multiple);
+ if (bool multiple = bool_attr_is_true(elem, "multiple"))
+ buttonset->setMultiMode(ButtonSet::MultiMode::Set);
+ if (bool oneormore = bool_attr_is_true(elem, "oneormore"))
+ buttonset->setMultiMode(ButtonSet::MultiMode::OneOrMore);
}
}
else if (elem_name == "item") {
diff --git a/src/ui/panel.cpp b/src/ui/panel.cpp
index 616c69c61..b523ecfe4 100644
--- a/src/ui/panel.cpp
+++ b/src/ui/panel.cpp
@@ -1,5 +1,6 @@
// Aseprite UI Library
-// Copyright (C) 2001-2013, 2015 David Capello
+// Copyright (C) 2019 Igara Studio S.A.
+// Copyright (C) 2001-2015 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
@@ -13,15 +14,18 @@
#include "ui/resize_event.h"
#include "ui/size_hint_event.h"
+#include
+
namespace ui {
Panel::Panel()
- : Widget(kPanelWidget)
+ : m_multiple(false)
{
}
void Panel::showChild(Widget* widget)
{
+ m_multiple = false;
for (auto child : children()) {
if (!child->isDecorative())
child->setVisible(child == widget);
@@ -29,8 +33,23 @@ void Panel::showChild(Widget* widget)
layout();
}
+void Panel::showAllChildren()
+{
+ m_multiple = true;
+ for (auto child : children()) {
+ if (!child->isDecorative())
+ child->setVisible(true);
+ }
+ layout();
+}
+
void Panel::onResize(ResizeEvent& ev)
{
+ if (m_multiple) {
+ VBox::onResize(ev);
+ return;
+ }
+
// Copy the new position rectangle
setBoundsQuietly(ev.bounds());
@@ -44,6 +63,11 @@ void Panel::onResize(ResizeEvent& ev)
void Panel::onSizeHint(SizeHintEvent& ev)
{
+ if (m_multiple) {
+ VBox::onSizeHint(ev);
+ return;
+ }
+
gfx::Size maxSize(0, 0);
gfx::Size reqSize;
@@ -51,13 +75,13 @@ void Panel::onSizeHint(SizeHintEvent& ev)
if (!child->isDecorative()) {
reqSize = child->sizeHint();
- maxSize.w = MAX(maxSize.w, reqSize.w);
- maxSize.h = MAX(maxSize.h, reqSize.h);
+ maxSize.w = std::max(maxSize.w, reqSize.w);
+ maxSize.h = std::max(maxSize.h, reqSize.h);
}
}
if (hasText())
- maxSize.w = MAX(maxSize.w, textWidth());
+ maxSize.w = std::max(maxSize.w, textWidth());
ev.setSizeHint(
maxSize.w + border().width(),
diff --git a/src/ui/panel.h b/src/ui/panel.h
index eec9d011f..64717435c 100644
--- a/src/ui/panel.h
+++ b/src/ui/panel.h
@@ -1,4 +1,5 @@
// Aseprite UI Library
+// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2001-2014 David Capello
//
// This file is released under the terms of the MIT license.
@@ -8,19 +9,23 @@
#define UI_PANEL_H_INCLUDED
#pragma once
-#include "ui/widget.h"
+#include "ui/box.h"
namespace ui {
- class Panel : public Widget {
+ class Panel : public VBox {
public:
Panel();
void showChild(Widget* widget);
+ void showAllChildren();
protected:
virtual void onResize(ResizeEvent& ev) override;
virtual void onSizeHint(SizeHintEvent& ev) override;
+
+ private:
+ bool m_multiple;
};
} // namespace ui