Improve context bar for slice tool (combobox of slices + action buttons)

This commit is contained in:
David Capello 2019-05-07 10:28:37 -03:00
parent 33dc3fd354
commit e8716cbb6e
22 changed files with 546 additions and 84 deletions

View File

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

View File

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

View File

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

View File

@ -182,7 +182,7 @@ private:
private:
void fill(bool all) {
removeAllItems();
deleteAllItems();
MatchWords match(getEntryWidget()->text());

View File

@ -898,7 +898,7 @@ private:
}
void refillLanguages() {
language()->removeAllItems();
language()->deleteAllItems();
loadLanguages();
}

View File

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

View File

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

View File

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

View File

@ -181,7 +181,7 @@ DitheringSelector::DitheringSelector(Type type)
void DitheringSelector::regenerate()
{
removeAllItems();
deleteAllItems();
Extensions& extensions = App::instance()->extensions();
auto ditheringMatrices = extensions.ditheringMatrices();

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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