mirror of
https://github.com/aseprite/aseprite.git
synced 2025-02-06 12:39:57 +00:00
Improve context bar for slice tool (combobox of slices + action buttons)
This commit is contained in:
parent
33dc3fd354
commit
e8716cbb6e
@ -34,8 +34,7 @@ void AddSlice::onExecute()
|
||||
Sprite* sprite = this->sprite();
|
||||
Slice* slice = this->slice();
|
||||
|
||||
sprite->slices().add(slice);
|
||||
sprite->incrementVersion();
|
||||
addSlice(sprite, slice);
|
||||
}
|
||||
|
||||
void AddSlice::onUndo()
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
// Copyright (C) 2017-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -10,6 +11,7 @@
|
||||
|
||||
#include "app/cmd/set_slice_name.h"
|
||||
|
||||
#include "app/doc.h"
|
||||
#include "app/doc_event.h"
|
||||
#include "doc/document.h"
|
||||
#include "doc/slice.h"
|
||||
@ -27,14 +29,27 @@ SetSliceName::SetSliceName(Slice* slice, const std::string& name)
|
||||
|
||||
void SetSliceName::onExecute()
|
||||
{
|
||||
slice()->setName(m_newName);
|
||||
slice()->incrementVersion();
|
||||
Slice* slice = this->slice();
|
||||
slice->setName(m_newName);
|
||||
slice->incrementVersion();
|
||||
}
|
||||
|
||||
void SetSliceName::onUndo()
|
||||
{
|
||||
slice()->setName(m_oldName);
|
||||
slice()->incrementVersion();
|
||||
Slice* slice = this->slice();
|
||||
slice->setName(m_oldName);
|
||||
slice->incrementVersion();
|
||||
}
|
||||
|
||||
void SetSliceName::onFireNotifications()
|
||||
{
|
||||
Slice* slice = this->slice();
|
||||
Sprite* sprite = slice->owner()->sprite();
|
||||
Doc* doc = static_cast<Doc*>(sprite->document());
|
||||
DocEvent ev(doc);
|
||||
ev.sprite(sprite);
|
||||
ev.slice(slice);
|
||||
doc->notify_observers<DocEvent&>(&DocObserver::onSliceNameChange, ev);
|
||||
}
|
||||
|
||||
} // namespace cmd
|
||||
|
@ -25,11 +25,13 @@ namespace cmd {
|
||||
protected:
|
||||
void onExecute() override;
|
||||
void onUndo() override;
|
||||
void onFireNotifications() override;
|
||||
size_t onMemSize() const override {
|
||||
return sizeof(*this);
|
||||
return sizeof(*this)
|
||||
+ m_oldName.size()
|
||||
+ m_newName.size();
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_oldName;
|
||||
std::string m_newName;
|
||||
};
|
||||
|
@ -182,7 +182,7 @@ private:
|
||||
|
||||
private:
|
||||
void fill(bool all) {
|
||||
removeAllItems();
|
||||
deleteAllItems();
|
||||
|
||||
MatchWords match(getEntryWidget()->text());
|
||||
|
||||
|
@ -898,7 +898,7 @@ private:
|
||||
}
|
||||
|
||||
void refillLanguages() {
|
||||
language()->removeAllItems();
|
||||
language()->deleteAllItems();
|
||||
loadLanguages();
|
||||
}
|
||||
|
||||
|
@ -72,6 +72,9 @@ namespace app {
|
||||
virtual void onSelectionChanged(DocEvent& ev) { }
|
||||
virtual void onSelectionBoundariesChanged(DocEvent& ev) { }
|
||||
|
||||
// Slices
|
||||
virtual void onSliceNameChange(DocEvent& ev) { }
|
||||
|
||||
// Called to destroy the observable. (Here you could call "delete this".)
|
||||
virtual void dispose() { }
|
||||
};
|
||||
|
@ -18,9 +18,13 @@
|
||||
#include "app/commands/commands.h"
|
||||
#include "app/commands/quick_command.h"
|
||||
#include "app/doc.h"
|
||||
#include "app/doc_event.h"
|
||||
#include "app/ini_file.h"
|
||||
#include "app/match_words.h"
|
||||
#include "app/modules/editors.h"
|
||||
#include "app/pref/preferences.h"
|
||||
#include "app/shade.h"
|
||||
#include "app/site.h"
|
||||
#include "app/tools/active_tool.h"
|
||||
#include "app/tools/controller.h"
|
||||
#include "app/tools/ink.h"
|
||||
@ -34,18 +38,22 @@
|
||||
#include "app/ui/color_button.h"
|
||||
#include "app/ui/color_shades.h"
|
||||
#include "app/ui/dithering_selector.h"
|
||||
#include "app/ui/editor/editor.h"
|
||||
#include "app/ui/icon_button.h"
|
||||
#include "app/ui/keyboard_shortcuts.h"
|
||||
#include "app/ui/selection_mode_field.h"
|
||||
#include "app/ui/skin/skin_theme.h"
|
||||
#include "app/ui_context.h"
|
||||
#include "base/bind.h"
|
||||
#include "base/fs.h"
|
||||
#include "base/scoped_value.h"
|
||||
#include "doc/brush.h"
|
||||
#include "doc/conversion_to_surface.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/palette.h"
|
||||
#include "doc/remap.h"
|
||||
#include "doc/selected_objects.h"
|
||||
#include "doc/slice.h"
|
||||
#include "obs/connection.h"
|
||||
#include "os/surface.h"
|
||||
#include "os/system.h"
|
||||
@ -56,6 +64,7 @@
|
||||
#include "ui/combobox.h"
|
||||
#include "ui/int_entry.h"
|
||||
#include "ui/label.h"
|
||||
#include "ui/listbox.h"
|
||||
#include "ui/listitem.h"
|
||||
#include "ui/menu.h"
|
||||
#include "ui/message.h"
|
||||
@ -66,6 +75,8 @@
|
||||
#include "ui/theme.h"
|
||||
#include "ui/tooltips.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace app {
|
||||
|
||||
using namespace app::skin;
|
||||
@ -1139,8 +1150,247 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
class ContextBar::SliceFields : public HBox {
|
||||
class Item : public ListItem {
|
||||
public:
|
||||
Item(const doc::Slice* slice)
|
||||
: ListItem(slice->name())
|
||||
, m_slice(slice) { }
|
||||
const doc::Slice* slice() const { return m_slice; }
|
||||
private:
|
||||
const doc::Slice* m_slice;
|
||||
};
|
||||
|
||||
class Combo : public ComboBox {
|
||||
SliceFields* m_sliceFields;
|
||||
public:
|
||||
Combo(SliceFields* sliceFields)
|
||||
: m_sliceFields(sliceFields) {
|
||||
}
|
||||
protected:
|
||||
void onChange() override {
|
||||
ComboBox::onChange();
|
||||
m_sliceFields->onSelectSliceFromComboBox();
|
||||
}
|
||||
void onEntryChange() override {
|
||||
ComboBox::onEntryChange();
|
||||
m_sliceFields->onComboBoxEntryChange();
|
||||
}
|
||||
void onBeforeOpenListBox() override {
|
||||
ComboBox::onBeforeOpenListBox();
|
||||
m_sliceFields->fillSlices();
|
||||
}
|
||||
void onEnterOnEditableEntry() override {
|
||||
ComboBox::onEnterOnEditableEntry();
|
||||
|
||||
const Slice* slice = nullptr;
|
||||
if (auto item = dynamic_cast<Item*>(getSelectedItem())) {
|
||||
if (item->slice()->name() == getValue()) {
|
||||
slice = item->slice();
|
||||
}
|
||||
}
|
||||
if (!slice && current_editor)
|
||||
slice = current_editor->sprite()->slices().getByName(getValue());
|
||||
if (slice)
|
||||
m_sliceFields->scrollToSlice(slice);
|
||||
|
||||
closeListBox();
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
SliceFields()
|
||||
: m_doc(nullptr)
|
||||
, m_sel(2)
|
||||
, m_combobox(this)
|
||||
, m_action(2)
|
||||
{
|
||||
SkinTheme* theme = SkinTheme::instance();
|
||||
|
||||
m_sel.addItem("All");
|
||||
m_sel.addItem("None");
|
||||
m_sel.ItemChange.connect(
|
||||
[this](ButtonSet::Item* item){
|
||||
onSelAction(m_sel.selectedItem());
|
||||
});
|
||||
|
||||
m_combobox.setEditable(true);
|
||||
m_combobox.setExpansive(true);
|
||||
m_combobox.setMinSize(gfx::Size(256*guiscale(), 0));
|
||||
|
||||
m_action.addItem(theme->parts.iconUserData())->setMono(true);
|
||||
m_action.addItem(theme->parts.iconClose())->setMono(true);
|
||||
m_action.ItemChange.connect(
|
||||
[this](ButtonSet::Item* item){
|
||||
onAction(m_action.selectedItem());
|
||||
});
|
||||
|
||||
addChild(&m_sel);
|
||||
addChild(&m_combobox);
|
||||
addChild(&m_action);
|
||||
|
||||
m_combobox.setVisible(false);
|
||||
m_action.setVisible(false);
|
||||
}
|
||||
|
||||
void setupTooltips(TooltipManager* tooltipManager) {
|
||||
tooltipManager->addTooltipFor(m_sel.at(0), "Select All Slices", BOTTOM);
|
||||
tooltipManager->addTooltipFor(m_sel.at(1), "Deselect Slices", BOTTOM);
|
||||
tooltipManager->addTooltipFor(m_action.at(0), "Slice Properties", BOTTOM);
|
||||
tooltipManager->addTooltipFor(m_action.at(1), "Delete Slice", BOTTOM);
|
||||
}
|
||||
|
||||
void setDoc(Doc* doc) {
|
||||
m_doc = doc;
|
||||
}
|
||||
|
||||
void addSlice(const doc::Slice* slice) {
|
||||
m_changeFromEntry = true;
|
||||
m_combobox.setValue(slice->name());
|
||||
updateLayout();
|
||||
m_changeFromEntry = false;
|
||||
}
|
||||
|
||||
void removeSlice(const doc::Slice* slice) {
|
||||
m_combobox.setValue(std::string());
|
||||
updateLayout();
|
||||
}
|
||||
|
||||
void updateSlice(const doc::Slice* slice) {
|
||||
m_combobox.setValue(slice->name());
|
||||
updateLayout();
|
||||
}
|
||||
|
||||
void selectSlices(const doc::Sprite* sprite,
|
||||
const doc::SelectedObjects& slices) {
|
||||
if (!slices.empty()) {
|
||||
auto selected = slices.frontAs<doc::Slice>();
|
||||
m_combobox.setValue(selected->name());
|
||||
}
|
||||
else {
|
||||
m_combobox.setValue(std::string());
|
||||
}
|
||||
updateLayout();
|
||||
}
|
||||
|
||||
void closeComboBox() {
|
||||
m_combobox.closeListBox();
|
||||
}
|
||||
|
||||
private:
|
||||
void onVisible(bool visible) override {
|
||||
HBox::onVisible(visible);
|
||||
m_combobox.closeListBox();
|
||||
}
|
||||
|
||||
void fillSlices() {
|
||||
m_combobox.deleteAllItems();
|
||||
if (m_doc && m_doc->sprite()) {
|
||||
MatchWords match(m_filter);
|
||||
|
||||
std::vector<doc::Slice*> slices;
|
||||
for (auto slice : m_doc->sprite()->slices()) {
|
||||
if (match(slice->name()))
|
||||
slices.push_back(slice);
|
||||
}
|
||||
std::sort(slices.begin(), slices.end(),
|
||||
[](const doc::Slice* a, const doc::Slice* b){
|
||||
return (base::compare_filenames(a->name(), b->name()) < 0);
|
||||
});
|
||||
|
||||
for (auto slice : slices) {
|
||||
Item* item = new Item(slice);
|
||||
m_combobox.addItem(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void scrollToSlice(const Slice* slice) {
|
||||
if (current_editor && slice) {
|
||||
if (const SliceKey* key = slice->getByFrame(current_editor->frame())) {
|
||||
current_editor->centerInSpritePoint(key->bounds().center());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void updateLayout() {
|
||||
const bool visible = (m_doc && !m_doc->sprite()->slices().empty());
|
||||
m_combobox.setVisible(visible);
|
||||
m_action.setVisible(visible);
|
||||
|
||||
parent()->layout();
|
||||
}
|
||||
|
||||
void onSelAction(const int item) {
|
||||
m_sel.deselectItems();
|
||||
switch (item) {
|
||||
case 0:
|
||||
if (current_editor)
|
||||
current_editor->selectAllSlices();
|
||||
break;
|
||||
case 1:
|
||||
if (current_editor)
|
||||
current_editor->clearSlicesSelection();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void onSelectSliceFromComboBox() {
|
||||
if (m_changeFromEntry)
|
||||
return;
|
||||
|
||||
m_filter.clear();
|
||||
|
||||
if (auto item = dynamic_cast<Item*>(m_combobox.getSelectedItem())) {
|
||||
if (current_editor) {
|
||||
const doc::Slice* slice = item->slice();
|
||||
current_editor->clearSlicesSelection();
|
||||
current_editor->selectSlice(slice);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void onComboBoxEntryChange() {
|
||||
m_changeFromEntry = true;
|
||||
m_combobox.closeListBox();
|
||||
|
||||
m_filter = m_combobox.getValue();
|
||||
|
||||
m_combobox.openListBox();
|
||||
m_changeFromEntry = false;
|
||||
}
|
||||
|
||||
void onAction(const int item) {
|
||||
m_action.deselectItems();
|
||||
|
||||
Command* cmd = nullptr;
|
||||
Params params;
|
||||
|
||||
switch (item) {
|
||||
case 0:
|
||||
cmd = Commands::instance()->byId(CommandId::SliceProperties());
|
||||
break;
|
||||
case 1:
|
||||
cmd = Commands::instance()->byId(CommandId::RemoveSlice());
|
||||
break;
|
||||
}
|
||||
|
||||
if (cmd)
|
||||
UIContext::instance()->executeCommand(cmd, params);
|
||||
|
||||
updateLayout();
|
||||
}
|
||||
|
||||
Doc* m_doc;
|
||||
ButtonSet m_sel;
|
||||
Combo m_combobox;
|
||||
ButtonSet m_action;
|
||||
bool m_changeFromEntry;
|
||||
std::string m_filter;
|
||||
};
|
||||
|
||||
ContextBar::ContextBar(TooltipManager* tooltipManager)
|
||||
: Box(HORIZONTAL)
|
||||
{
|
||||
addChild(m_selectionOptionsBox = new HBox());
|
||||
m_selectionOptionsBox->addChild(m_dropPixels = new DropPixelsField());
|
||||
@ -1174,10 +1424,6 @@ ContextBar::ContextBar(TooltipManager* tooltipManager)
|
||||
|
||||
addChild(m_autoSelectLayer = new AutoSelectLayerField());
|
||||
|
||||
// addChild(new InkChannelTargetField());
|
||||
// addChild(new InkShadeField());
|
||||
// addChild(new InkSelectionField());
|
||||
|
||||
addChild(m_sprayBox = new HBox());
|
||||
m_sprayBox->addChild(m_sprayLabel = new Label("Spray:"));
|
||||
m_sprayBox->addChild(m_sprayWidth = new SprayWidthField());
|
||||
@ -1191,9 +1437,12 @@ ContextBar::ContextBar(TooltipManager* tooltipManager)
|
||||
addChild(m_symmetry = new SymmetryField());
|
||||
m_symmetry->setVisible(Preferences::instance().symmetryMode.enabled());
|
||||
|
||||
addChild(m_sliceFields = new SliceFields);
|
||||
|
||||
setupTooltips(tooltipManager);
|
||||
|
||||
App::instance()->activeToolManager()->add_observer(this);
|
||||
UIContext::instance()->add_observer(this);
|
||||
|
||||
auto& pref = Preferences::instance();
|
||||
pref.symmetryMode.enabled.AfterChange.connect(
|
||||
@ -1216,6 +1465,7 @@ ContextBar::ContextBar(TooltipManager* tooltipManager)
|
||||
|
||||
ContextBar::~ContextBar()
|
||||
{
|
||||
UIContext::instance()->remove_observer(this);
|
||||
App::instance()->activeToolManager()->remove_observer(this);
|
||||
}
|
||||
|
||||
@ -1265,6 +1515,40 @@ void ContextBar::onToolSetContiguous()
|
||||
}
|
||||
}
|
||||
|
||||
void ContextBar::onActiveSiteChange(const Site& site)
|
||||
{
|
||||
DocObserverWidget<ui::HBox>::onActiveSiteChange(site);
|
||||
if (site.sprite())
|
||||
m_sliceFields->selectSlices(site.sprite(),
|
||||
site.selectedSlices());
|
||||
else
|
||||
m_sliceFields->closeComboBox();
|
||||
}
|
||||
|
||||
void ContextBar::onDocChange(Doc* doc)
|
||||
{
|
||||
DocObserverWidget<ui::HBox>::onDocChange(doc);
|
||||
m_sliceFields->setDoc(doc);
|
||||
}
|
||||
|
||||
void ContextBar::onAddSlice(DocEvent& ev)
|
||||
{
|
||||
if (ev.slice())
|
||||
m_sliceFields->addSlice(ev.slice());
|
||||
}
|
||||
|
||||
void ContextBar::onRemoveSlice(DocEvent& ev)
|
||||
{
|
||||
if (ev.slice())
|
||||
m_sliceFields->removeSlice(ev.slice());
|
||||
}
|
||||
|
||||
void ContextBar::onSliceNameChange(DocEvent& ev)
|
||||
{
|
||||
if (ev.slice())
|
||||
m_sliceFields->updateSlice(ev.slice());
|
||||
}
|
||||
|
||||
void ContextBar::onBrushSizeChange()
|
||||
{
|
||||
if (m_activeBrush->type() != kImageBrushType)
|
||||
@ -1438,6 +1722,11 @@ void ContextBar::updateForTool(tools::Tool* tool)
|
||||
(tool->getInk(0)->isCelMovement() ||
|
||||
tool->getInk(1)->isCelMovement());
|
||||
|
||||
// True if the current tool is slice tool.
|
||||
const bool isSlice = tool &&
|
||||
(tool->getInk(0)->isSlice() ||
|
||||
tool->getInk(1)->isSlice());
|
||||
|
||||
// True if the current tool is floodfill
|
||||
const bool isFloodfill = tool &&
|
||||
(tool->getPointShape(0)->isFloodFill() ||
|
||||
@ -1502,6 +1791,8 @@ void ContextBar::updateForTool(tools::Tool* tool)
|
||||
(isPaint || isEffect || hasSelectOptions));
|
||||
m_symmetry->updateWithCurrentDocument();
|
||||
|
||||
m_sliceFields->setVisible(isSlice);
|
||||
|
||||
// Update ink shades with the current selected palette entries
|
||||
if (updateShade)
|
||||
m_inkShades->updateShadeFromColorBarPicks();
|
||||
@ -1815,6 +2106,7 @@ void ContextBar::setupTooltips(TooltipManager* tooltipManager)
|
||||
m_gradientType->setupTooltips(tooltipManager);
|
||||
m_dropPixels->setupTooltips(tooltipManager);
|
||||
m_symmetry->setupTooltips(tooltipManager);
|
||||
m_sliceFields->setupTooltips(tooltipManager);
|
||||
}
|
||||
|
||||
void ContextBar::registerCommands()
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "app/tools/ink_type.h"
|
||||
#include "app/tools/tool_loop_modifiers.h"
|
||||
#include "app/ui/context_bar_observer.h"
|
||||
#include "app/ui/doc_observer_widget.h"
|
||||
#include "doc/brush.h"
|
||||
#include "obs/connection.h"
|
||||
#include "obs/observable.h"
|
||||
@ -51,7 +52,7 @@ namespace app {
|
||||
class DitheringSelector;
|
||||
class GradientTypeSelector;
|
||||
|
||||
class ContextBar : public ui::Box
|
||||
class ContextBar : public DocObserverWidget<ui::HBox>
|
||||
, public obs::observable<ContextBarObserver>
|
||||
, public tools::ActiveToolObserver {
|
||||
public:
|
||||
@ -97,6 +98,17 @@ namespace app {
|
||||
void onToolSetFreehandAlgorithm();
|
||||
void onToolSetContiguous();
|
||||
|
||||
// ContextObserver impl
|
||||
void onActiveSiteChange(const Site& site) override;
|
||||
|
||||
// DocObserverWidget overrides
|
||||
void onDocChange(Doc* doc) override;
|
||||
|
||||
// DocObserver impl
|
||||
void onAddSlice(DocEvent& ev) override;
|
||||
void onRemoveSlice(DocEvent& ev) override;
|
||||
void onSliceNameChange(DocEvent& ev) override;
|
||||
|
||||
private:
|
||||
void onBrushSizeChange();
|
||||
void onBrushAngleChange();
|
||||
@ -135,6 +147,7 @@ namespace app {
|
||||
class DropPixelsField;
|
||||
class AutoSelectLayerField;
|
||||
class SymmetryField;
|
||||
class SliceFields;
|
||||
|
||||
ZoomButtons* m_zoomButtons;
|
||||
BrushBackField* m_brushBack;
|
||||
@ -169,6 +182,7 @@ namespace app {
|
||||
doc::BrushRef m_activeBrush;
|
||||
ui::Label* m_selectBoxHelp;
|
||||
SymmetryField* m_symmetry;
|
||||
SliceFields* m_sliceFields;
|
||||
obs::scoped_connection m_sizeConn;
|
||||
obs::scoped_connection m_angleConn;
|
||||
obs::scoped_connection m_opacityConn;
|
||||
|
@ -181,7 +181,7 @@ DitheringSelector::DitheringSelector(Type type)
|
||||
|
||||
void DitheringSelector::regenerate()
|
||||
{
|
||||
removeAllItems();
|
||||
deleteAllItems();
|
||||
|
||||
Extensions& extensions = App::instance()->extensions();
|
||||
auto ditheringMatrices = extensions.ditheringMatrices();
|
||||
|
90
src/app/ui/doc_observer_widget.h
Normal file
90
src/app/ui/doc_observer_widget.h
Normal file
@ -0,0 +1,90 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifndef APP_UI_DOC_OBSERVER_WIDGET_H_INCLUDED
|
||||
#define APP_UI_DOC_OBSERVER_WIDGET_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "app/context_observer.h"
|
||||
#include "app/doc.h"
|
||||
#include "app/doc_observer.h"
|
||||
#include "app/docs_observer.h"
|
||||
#include "app/site.h"
|
||||
#include "app/ui_context.h"
|
||||
#include "base/debug.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
// A widget that observes the active document.
|
||||
template<typename BaseWidget>
|
||||
class DocObserverWidget : public BaseWidget
|
||||
, public ContextObserver
|
||||
, public DocsObserver
|
||||
, public DocObserver {
|
||||
public:
|
||||
template<typename... Args>
|
||||
DocObserverWidget(Args&&... args)
|
||||
: BaseWidget(std::forward<Args>(args)...)
|
||||
, m_doc(nullptr) {
|
||||
UIContext::instance()->add_observer(this);
|
||||
UIContext::instance()->documents().add_observer(this);
|
||||
}
|
||||
|
||||
~DocObserverWidget() {
|
||||
ASSERT(!m_doc);
|
||||
UIContext::instance()->documents().remove_observer(this);
|
||||
UIContext::instance()->remove_observer(this);
|
||||
}
|
||||
|
||||
protected:
|
||||
Doc* doc() const { return m_doc; }
|
||||
|
||||
virtual void onDocChange(Doc* doc) {
|
||||
}
|
||||
|
||||
// ContextObserver impl
|
||||
void onActiveSiteChange(const Site& site) override {
|
||||
if (m_doc && site.document() != m_doc) {
|
||||
m_doc->remove_observer(this);
|
||||
m_doc = nullptr;
|
||||
}
|
||||
|
||||
if (site.document() && site.sprite()) {
|
||||
if (!m_doc) {
|
||||
m_doc = const_cast<Doc*>(site.document());
|
||||
m_doc->add_observer(this);
|
||||
|
||||
onDocChange(m_doc);
|
||||
}
|
||||
else {
|
||||
ASSERT(m_doc == site.document());
|
||||
}
|
||||
}
|
||||
else {
|
||||
ASSERT(m_doc == nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
// DocsObservers impl
|
||||
void onRemoveDocument(Doc* doc) override {
|
||||
if (m_doc &&
|
||||
m_doc == doc) {
|
||||
m_doc->remove_observer(this);
|
||||
m_doc = nullptr;
|
||||
|
||||
onDocChange(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
// The DocObserver impl will be in the derived class.
|
||||
|
||||
private:
|
||||
Doc* m_doc;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
||||
#endif
|
@ -559,6 +559,16 @@ bool DocView::onPaste(Context* ctx)
|
||||
|
||||
bool DocView::onClear(Context* ctx)
|
||||
{
|
||||
// First we check if there is a selected slice, so we'll delete
|
||||
// those slices.
|
||||
Site site = ctx->activeSite();
|
||||
if (!site.selectedSlices().empty()) {
|
||||
Command* removeSlices = Commands::instance()->byId(CommandId::RemoveSlice());
|
||||
ctx->executeCommand(removeSlices);
|
||||
return true;
|
||||
}
|
||||
|
||||
// In other case we delete the mask or the cel.
|
||||
ContextWriter writer(ctx);
|
||||
Doc* document = writer.document();
|
||||
bool visibleMask = document->isMaskVisible();
|
||||
|
@ -380,8 +380,10 @@ void Editor::getSite(Site* site) const
|
||||
site->sprite(m_sprite);
|
||||
site->layer(m_layer);
|
||||
site->frame(m_frame);
|
||||
if (!m_selectedSlices.empty())
|
||||
if (!m_selectedSlices.empty() &&
|
||||
getCurrentEditorInk()->isSlice()) {
|
||||
site->selectedSlices(m_selectedSlices);
|
||||
}
|
||||
}
|
||||
|
||||
Site Editor::getSite() const
|
||||
@ -1308,12 +1310,12 @@ gfx::Point Editor::autoScroll(MouseMessage* msg, AutoScroll dir)
|
||||
return mousePos;
|
||||
}
|
||||
|
||||
tools::Tool* Editor::getCurrentEditorTool()
|
||||
tools::Tool* Editor::getCurrentEditorTool() const
|
||||
{
|
||||
return App::instance()->activeTool();
|
||||
}
|
||||
|
||||
tools::Ink* Editor::getCurrentEditorInk()
|
||||
tools::Ink* Editor::getCurrentEditorInk() const
|
||||
{
|
||||
tools::Ink* ink = m_state->getStateInk();
|
||||
if (ink)
|
||||
@ -1629,8 +1631,13 @@ bool Editor::isSliceSelected(const doc::Slice* slice) const
|
||||
|
||||
void Editor::clearSlicesSelection()
|
||||
{
|
||||
m_selectedSlices.clear();
|
||||
invalidate();
|
||||
if (!m_selectedSlices.empty()) {
|
||||
m_selectedSlices.clear();
|
||||
invalidate();
|
||||
|
||||
if (isActive())
|
||||
UIContext::instance()->notifyActiveSiteChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void Editor::selectSlice(const doc::Slice* slice)
|
||||
@ -1638,6 +1645,9 @@ void Editor::selectSlice(const doc::Slice* slice)
|
||||
ASSERT(slice);
|
||||
m_selectedSlices.insert(slice->id());
|
||||
invalidate();
|
||||
|
||||
if (isActive())
|
||||
UIContext::instance()->notifyActiveSiteChanged();
|
||||
}
|
||||
|
||||
bool Editor::selectSliceBox(const gfx::Rect& box)
|
||||
@ -1649,12 +1659,26 @@ bool Editor::selectSliceBox(const gfx::Rect& box)
|
||||
m_selectedSlices.insert(slice->id());
|
||||
}
|
||||
invalidate();
|
||||
|
||||
if (isActive())
|
||||
UIContext::instance()->notifyActiveSiteChanged();
|
||||
|
||||
return !m_selectedSlices.empty();
|
||||
}
|
||||
|
||||
void Editor::selectAllSlices()
|
||||
{
|
||||
for (auto slice : m_sprite->slices())
|
||||
m_selectedSlices.insert(slice->id());
|
||||
invalidate();
|
||||
|
||||
if (isActive())
|
||||
UIContext::instance()->notifyActiveSiteChanged();
|
||||
}
|
||||
|
||||
void Editor::cancelSelections()
|
||||
{
|
||||
m_selectedSlices.clear();
|
||||
clearSlicesSelection();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
@ -201,8 +201,8 @@ namespace app {
|
||||
// Control scroll when cursor goes out of the editor viewport.
|
||||
gfx::Point autoScroll(ui::MouseMessage* msg, AutoScroll dir);
|
||||
|
||||
tools::Tool* getCurrentEditorTool();
|
||||
tools::Ink* getCurrentEditorInk();
|
||||
tools::Tool* getCurrentEditorTool() const;
|
||||
tools::Ink* getCurrentEditorInk() const;
|
||||
|
||||
tools::ToolLoopModifiers getToolLoopModifiers() const { return m_toolLoopModifiers; }
|
||||
bool isAutoSelectLayer();
|
||||
@ -283,6 +283,7 @@ namespace app {
|
||||
void clearSlicesSelection();
|
||||
void selectSlice(const doc::Slice* slice);
|
||||
bool selectSliceBox(const gfx::Rect& box);
|
||||
void selectAllSlices();
|
||||
bool hasSelectedSlices() const { return !m_selectedSlices.empty(); }
|
||||
|
||||
// Called by DocView's InputChainElement::onCancel() impl when Esc
|
||||
|
@ -123,7 +123,7 @@ namespace app {
|
||||
virtual bool acceptQuickTool(tools::Tool* tool) { return true; }
|
||||
|
||||
// Custom ink in this state.
|
||||
virtual tools::Ink* getStateInk() { return nullptr; }
|
||||
virtual tools::Ink* getStateInk() const { return nullptr; }
|
||||
|
||||
// Called when a tag is deleted.
|
||||
virtual void onRemoveFrameTag(Editor* editor, doc::FrameTag* tag) { }
|
||||
|
@ -303,7 +303,7 @@ bool SelectBoxState::requireBrushPreview()
|
||||
return false;
|
||||
}
|
||||
|
||||
tools::Ink* SelectBoxState::getStateInk()
|
||||
tools::Ink* SelectBoxState::getStateInk() const
|
||||
{
|
||||
if (hasFlag(Flags::QuickBox))
|
||||
return App::instance()->toolBox()->getInkById(
|
||||
|
@ -98,7 +98,7 @@ namespace app {
|
||||
virtual bool onKeyDown(Editor* editor, ui::KeyMessage* msg) override;
|
||||
virtual bool acceptQuickTool(tools::Tool* tool) override;
|
||||
virtual bool requireBrushPreview() override;
|
||||
virtual tools::Ink* getStateInk() override;
|
||||
virtual tools::Ink* getStateInk() const override;
|
||||
|
||||
// EditorDecorator overrides
|
||||
virtual void postRenderDecorator(EditorPostRender* render) override;
|
||||
|
@ -242,6 +242,7 @@ bool StandbyState::onMouseDown(Editor* editor, MouseMessage* msg)
|
||||
// If we click outside all slices, we clear the selection of slices.
|
||||
if (!hit.slice() || !site.selectedSlices().contains(hit.slice()->id())) {
|
||||
editor->clearSlicesSelection();
|
||||
editor->selectSlice(hit.slice());
|
||||
|
||||
site = Site();
|
||||
editor->getSite(&site);
|
||||
|
@ -127,7 +127,7 @@ protected:
|
||||
if (m_fileList->multipleSelection())
|
||||
m_fileList->deselectedFileItems();
|
||||
|
||||
removeAllItems();
|
||||
deleteAllItems();
|
||||
|
||||
// String to be autocompleted
|
||||
std::string left_part = getEntryWidget()->text();
|
||||
@ -413,7 +413,7 @@ bool FileSelector::show(
|
||||
updateNavigationButtons();
|
||||
|
||||
// fill file-type combo-box
|
||||
fileType()->removeAllItems();
|
||||
fileType()->deleteAllItems();
|
||||
|
||||
// Get the default extension from the given initial file name
|
||||
if (m_defExtension.empty())
|
||||
@ -662,7 +662,7 @@ void FileSelector::updateLocation()
|
||||
}
|
||||
|
||||
// Clear all the items from the combo-box
|
||||
location()->removeAllItems();
|
||||
location()->deleteAllItems();
|
||||
|
||||
// Add item by item (from root to the specific current folder)
|
||||
int level = 0;
|
||||
|
@ -542,7 +542,6 @@ StatusBar::StatusBar(TooltipManager* tooltipManager)
|
||||
: m_timeout(0)
|
||||
, m_indicators(new Indicators)
|
||||
, m_docControls(new HBox)
|
||||
, m_doc(nullptr)
|
||||
, m_tipwindow(nullptr)
|
||||
, m_snapToGridWindow(nullptr)
|
||||
{
|
||||
@ -586,8 +585,6 @@ StatusBar::StatusBar(TooltipManager* tooltipManager)
|
||||
tooltipManager->addTooltipFor(m_zoomEntry, "Zoom Level", BOTTOM);
|
||||
tooltipManager->addTooltipFor(m_newFrame, "New Frame", BOTTOM);
|
||||
|
||||
UIContext::instance()->add_observer(this);
|
||||
UIContext::instance()->documents().add_observer(this);
|
||||
App::instance()->activeToolManager()->add_observer(this);
|
||||
|
||||
initTheme();
|
||||
@ -596,8 +593,6 @@ StatusBar::StatusBar(TooltipManager* tooltipManager)
|
||||
StatusBar::~StatusBar()
|
||||
{
|
||||
App::instance()->activeToolManager()->remove_observer(this);
|
||||
UIContext::instance()->documents().remove_observer(this);
|
||||
UIContext::instance()->remove_observer(this);
|
||||
|
||||
delete m_tipwindow; // widget
|
||||
delete m_snapToGridWindow;
|
||||
@ -719,9 +714,10 @@ void StatusBar::showTool(int msecs, tools::Tool* tool)
|
||||
void StatusBar::showSnapToGridWarning(bool state)
|
||||
{
|
||||
if (state) {
|
||||
// m_doc can be null if "snap to grid" command is pressed without
|
||||
// an opened document. (E.g. to change the default setting)
|
||||
if (!m_doc)
|
||||
// this->doc() can be nullptr if "snap to grid" command is pressed
|
||||
// without an opened document. (E.g. to change the default
|
||||
// setting)
|
||||
if (!doc())
|
||||
return;
|
||||
|
||||
if (!m_snapToGridWindow)
|
||||
@ -733,7 +729,7 @@ void StatusBar::showSnapToGridWarning(bool state)
|
||||
updateSnapToGridWindowPosition();
|
||||
}
|
||||
|
||||
m_snapToGridWindow->setDocument(m_doc);
|
||||
m_snapToGridWindow->setDocument(doc());
|
||||
}
|
||||
else {
|
||||
if (m_snapToGridWindow)
|
||||
@ -768,7 +764,7 @@ void StatusBar::onInitTheme(ui::InitThemeEvent& ev)
|
||||
void StatusBar::onResize(ResizeEvent& ev)
|
||||
{
|
||||
Rect rc = ev.bounds();
|
||||
m_docControls->setVisible(m_doc && rc.w > 300*ui::guiscale());
|
||||
m_docControls->setVisible(doc() && rc.w > 300*ui::guiscale());
|
||||
|
||||
HBox::onResize(ev);
|
||||
|
||||
@ -779,21 +775,10 @@ void StatusBar::onResize(ResizeEvent& ev)
|
||||
|
||||
void StatusBar::onActiveSiteChange(const Site& site)
|
||||
{
|
||||
if (m_doc && site.document() != m_doc) {
|
||||
m_doc->remove_observer(this);
|
||||
m_doc = nullptr;
|
||||
}
|
||||
DocObserverWidget<ui::HBox>::onActiveSiteChange(site);
|
||||
|
||||
if (site.document() && site.sprite()) {
|
||||
if (!m_doc) {
|
||||
m_doc = const_cast<Doc*>(site.document());
|
||||
m_doc->add_observer(this);
|
||||
}
|
||||
else {
|
||||
ASSERT(m_doc == site.document());
|
||||
}
|
||||
|
||||
auto& docPref = Preferences::instance().document(m_doc);
|
||||
if (doc()) {
|
||||
auto& docPref = Preferences::instance().document(doc());
|
||||
|
||||
m_docControls->setVisible(true);
|
||||
showSnapToGridWarning(docPref.grid.snap());
|
||||
@ -803,22 +788,12 @@ void StatusBar::onActiveSiteChange(const Site& site)
|
||||
"%d", site.frame()+docPref.timeline.firstFrame());
|
||||
}
|
||||
else {
|
||||
ASSERT(m_doc == nullptr);
|
||||
m_docControls->setVisible(false);
|
||||
showSnapToGridWarning(false);
|
||||
}
|
||||
layout();
|
||||
}
|
||||
|
||||
void StatusBar::onRemoveDocument(Doc* doc)
|
||||
{
|
||||
if (m_doc &&
|
||||
m_doc == doc) {
|
||||
m_doc->remove_observer(this);
|
||||
m_doc = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void StatusBar::onPixelFormatChanged(DocEvent& ev)
|
||||
{
|
||||
// If this is called from the non-UI thread it means that the pixel
|
||||
|
@ -11,9 +11,8 @@
|
||||
|
||||
#include "app/color.h"
|
||||
#include "app/context_observer.h"
|
||||
#include "app/doc_observer.h"
|
||||
#include "app/docs_observer.h"
|
||||
#include "app/tools/active_tool_observer.h"
|
||||
#include "app/ui/doc_observer_widget.h"
|
||||
#include "base/time.h"
|
||||
#include "ui/base.h"
|
||||
#include "ui/box.h"
|
||||
@ -43,10 +42,7 @@ namespace app {
|
||||
class Tool;
|
||||
}
|
||||
|
||||
class StatusBar : public ui::HBox
|
||||
, public ContextObserver
|
||||
, public DocsObserver
|
||||
, public DocObserver
|
||||
class StatusBar : public DocObserverWidget<ui::HBox>
|
||||
, public tools::ActiveToolObserver {
|
||||
static StatusBar* m_instance;
|
||||
public:
|
||||
@ -79,9 +75,6 @@ namespace app {
|
||||
// ContextObserver impl
|
||||
void onActiveSiteChange(const Site& site) override;
|
||||
|
||||
// DocObservers impl
|
||||
void onRemoveDocument(Doc* doc) override;
|
||||
|
||||
// DocObserver impl
|
||||
void onPixelFormatChanged(DocEvent& ev) override;
|
||||
|
||||
@ -108,7 +101,6 @@ namespace app {
|
||||
ui::Entry* m_currentFrame; // Current frame and go to frame entry
|
||||
ui::Button* m_newFrame; // Button to create a new frame
|
||||
ZoomEntry* m_zoomEntry;
|
||||
Doc* m_doc; // Document used to show the cel slider
|
||||
|
||||
// Tip window
|
||||
class CustomizedTipWindow;
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite UI Library
|
||||
// Copyright (C) 2018 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2019 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
@ -48,6 +48,7 @@ public:
|
||||
protected:
|
||||
bool onProcessMessage(Message* msg) override;
|
||||
void onPaint(PaintEvent& ev) override;
|
||||
void onChange() override;
|
||||
|
||||
private:
|
||||
ComboBox* m_comboBox;
|
||||
@ -112,7 +113,7 @@ ComboBox::ComboBox()
|
||||
ComboBox::~ComboBox()
|
||||
{
|
||||
removeMessageFilters();
|
||||
removeAllItems();
|
||||
deleteAllItems();
|
||||
}
|
||||
|
||||
void ComboBox::setEditable(bool state)
|
||||
@ -186,7 +187,7 @@ void ComboBox::removeItem(Widget* item)
|
||||
// Do not delete the given "item"
|
||||
}
|
||||
|
||||
void ComboBox::removeItem(int itemIndex)
|
||||
void ComboBox::deleteItem(int itemIndex)
|
||||
{
|
||||
ASSERT(itemIndex >= 0 && (std::size_t)itemIndex < m_items.size());
|
||||
|
||||
@ -196,7 +197,7 @@ void ComboBox::removeItem(int itemIndex)
|
||||
delete item;
|
||||
}
|
||||
|
||||
void ComboBox::removeAllItems()
|
||||
void ComboBox::deleteAllItems()
|
||||
{
|
||||
for (Widget* item : m_items)
|
||||
delete item; // widget
|
||||
@ -210,7 +211,7 @@ int ComboBox::getItemCount() const
|
||||
return m_items.size();
|
||||
}
|
||||
|
||||
Widget* ComboBox::getItem(int itemIndex)
|
||||
Widget* ComboBox::getItem(const int itemIndex) const
|
||||
{
|
||||
if (itemIndex >= 0 && (std::size_t)itemIndex < m_items.size()) {
|
||||
return m_items[itemIndex];
|
||||
@ -268,7 +269,7 @@ int ComboBox::findItemIndexByValue(const std::string& value) const
|
||||
|
||||
Widget* ComboBox::getSelectedItem() const
|
||||
{
|
||||
return (!m_items.empty() ? m_items[m_selected]: NULL);
|
||||
return getItem(m_selected);
|
||||
}
|
||||
|
||||
void ComboBox::setSelectedItem(Widget* item)
|
||||
@ -467,6 +468,10 @@ bool ComboBoxEntry::onProcessMessage(Message* msg)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (scancode == kKeyEnter ||
|
||||
scancode == kKeyEnterPad) {
|
||||
m_comboBox->onEnterOnEditableEntry();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -538,6 +543,15 @@ void ComboBoxEntry::onPaint(PaintEvent& ev)
|
||||
theme()->paintComboBoxEntry(ev);
|
||||
}
|
||||
|
||||
void ComboBoxEntry::onChange()
|
||||
{
|
||||
Entry::onChange();
|
||||
if (m_comboBox &&
|
||||
m_comboBox->isEditable()) {
|
||||
m_comboBox->onEntryChange();
|
||||
}
|
||||
}
|
||||
|
||||
bool ComboBoxListBox::onProcessMessage(Message* msg)
|
||||
{
|
||||
switch (msg->type()) {
|
||||
@ -591,6 +605,8 @@ void ComboBox::openListBox()
|
||||
if (!isEnabled() || m_window)
|
||||
return;
|
||||
|
||||
onBeforeOpenListBox();
|
||||
|
||||
m_window = new Window(Window::WithoutTitleBar);
|
||||
View* view = new View();
|
||||
m_listbox = new ComboBoxListBox(this);
|
||||
@ -678,6 +694,16 @@ void ComboBox::onChange()
|
||||
Change();
|
||||
}
|
||||
|
||||
void ComboBox::onEntryChange()
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
void ComboBox::onBeforeOpenListBox()
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
void ComboBox::onOpenListBox()
|
||||
{
|
||||
OpenListBox();
|
||||
@ -688,6 +714,11 @@ void ComboBox::onCloseListBox()
|
||||
CloseListBox();
|
||||
}
|
||||
|
||||
void ComboBox::onEnterOnEditableEntry()
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
void ComboBox::filterMessages()
|
||||
{
|
||||
if (!m_filtering) {
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Aseprite UI Library
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
@ -57,13 +58,13 @@ namespace ui {
|
||||
void removeItem(Widget* item);
|
||||
|
||||
// Removes and deletes the given item.
|
||||
void removeItem(int itemIndex);
|
||||
void deleteItem(int itemIndex);
|
||||
|
||||
void removeAllItems();
|
||||
void deleteAllItems();
|
||||
|
||||
int getItemCount() const;
|
||||
|
||||
Widget* getItem(int itemIndex);
|
||||
Widget* getItem(const int itemIndex) const;
|
||||
const std::string& getItemText(int itemIndex) const;
|
||||
void setItemText(int itemIndex, const std::string& text);
|
||||
int findItemIndex(const std::string& text) const;
|
||||
@ -96,9 +97,21 @@ namespace ui {
|
||||
void onResize(ResizeEvent& ev) override;
|
||||
void onSizeHint(SizeHintEvent& ev) override;
|
||||
void onInitTheme(InitThemeEvent& ev) override;
|
||||
|
||||
// When the selected item is changed.
|
||||
virtual void onChange();
|
||||
|
||||
// When the text of an editable ComboBox is changed.
|
||||
virtual void onEntryChange();
|
||||
|
||||
// Before we open the list box, we can fill the combobox with the
|
||||
// items to show. TODO replace all this with a MVC-like combobox
|
||||
// model so we request items only when it's required.
|
||||
virtual void onBeforeOpenListBox();
|
||||
|
||||
virtual void onOpenListBox();
|
||||
virtual void onCloseListBox();
|
||||
virtual void onEnterOnEditableEntry();
|
||||
|
||||
private:
|
||||
void onButtonClick(Event& ev);
|
||||
|
Loading…
x
Reference in New Issue
Block a user